fix(terminal): fix tabKey parsing for peerIds containing underscores (#14354)

Terminal tab keys use the format "peerId_terminalId". The previous code
used split('_')[0] or startsWith('$peerId_') to extract the peerId,
which breaks when the peerId itself contains underscores.

This can happen in two scenarios:
- Hostname-based ID: when OPTION_ALLOW_HOSTNAME_AS_ID is enabled, the
  peerId is derived from the system hostname, which commonly contains
  underscores (e.g. "my_dev_machine").
- Custom ID: the validation regex ^[a-zA-Z][\w-]{5,15}$ allows
  underscores since \w matches [a-zA-Z0-9_], so IDs like "my_dev_01"
  are valid.

Fix all three parsing sites in terminal_tab_page.dart to use
lastIndexOf('_'), which is safe because terminalId is always a plain
integer with no underscores.
This commit is contained in:
fufesou
2026-02-19 23:45:06 +08:00
committed by GitHub
parent 20f11018ce
commit 34ceeac36e

View File

@@ -194,7 +194,10 @@ class _TerminalTabPageState extends State<TerminalTabPage> {
final currentTab = tabController.state.value.selectedTabInfo;
assert(call.arguments is String,
"Expected String arguments for kWindowEventActiveSession, got ${call.arguments.runtimeType}");
if (currentTab.key.startsWith(call.arguments)) {
// Use lastIndexOf to handle peerIds containing underscores
final lastUnderscore = currentTab.key.lastIndexOf('_');
if (lastUnderscore > 0 &&
currentTab.key.substring(0, lastUnderscore) == call.arguments) {
windowOnTop(windowId());
return true;
}
@@ -329,7 +332,10 @@ class _TerminalTabPageState extends State<TerminalTabPage> {
void _addNewTerminal(String peerId, {int? terminalId}) {
// Find first tab for this peer to get connection parameters
final firstTab = tabController.state.value.tabs.firstWhere(
(tab) => tab.key.startsWith('$peerId\_'),
(tab) {
final last = tab.key.lastIndexOf('_');
return last > 0 && tab.key.substring(0, last) == peerId;
},
);
if (firstTab.page is TerminalPage) {
final page = firstTab.page as TerminalPage;
@@ -350,9 +356,10 @@ class _TerminalTabPageState extends State<TerminalTabPage> {
void _addNewTerminalForCurrentPeer({int? terminalId}) {
final currentTab = tabController.state.value.selectedTabInfo;
final parts = currentTab.key.split('_');
if (parts.isNotEmpty) {
final peerId = parts[0];
final tabKey = currentTab.key;
final lastUnderscore = tabKey.lastIndexOf('_');
if (lastUnderscore > 0) {
final peerId = tabKey.substring(0, lastUnderscore);
_addNewTerminal(peerId, terminalId: terminalId);
}
}
@@ -369,9 +376,10 @@ class _TerminalTabPageState extends State<TerminalTabPage> {
labelGetter: DesktopTab.tablabelGetter,
tabMenuBuilder: (key) {
// Extract peerId from tab key (format: "peerId_terminalId")
final parts = key.split('_');
if (parts.isEmpty) return Container();
final peerId = parts[0];
// Use lastIndexOf to handle peerIds containing underscores
final lastUnderscore = key.lastIndexOf('_');
if (lastUnderscore <= 0) return Container();
final peerId = key.substring(0, lastUnderscore);
return _tabMenuBuilder(peerId, () {});
},
));