- commit
- a9dc983
- parent
- 46b02ee
- author
- Patrik Sundberg
- date
- 2026-03-17 13:29:14 -0400 EDT
fix(attach): reject combined intentional modifiers for Ctrl+\ detach The parser accepted any modifier combination that included ctrl (ctrl+shift, ctrl+alt, ctrl+super, etc.) because it only checked the ctrl bit. This was over-permissive: ctrl+shift+\ is a different key combination and should not trigger detach. Tighten the check to require ctrl as the ONLY intentional modifier. Lock modifiers (caps_lock, num_lock) remain tolerated since they are ambient state, not deliberate key combinations.
1 files changed,
+17,
-6
+17,
-6
1@@ -246,8 +246,11 @@ fn parseKittyCtrlBackslash(buf: []const u8) bool {
2 if (mod_encoded < 1) return false;
3 const mod_raw = mod_encoded - 1;
4
5- // 5. Ctrl bit (0b100 = 4) must be set.
6- if (mod_raw & 0b100 == 0) return false;
7+ // 5. Ctrl must be the only intentional modifier. Lock modifiers
8+ // (caps_lock=0b1000000, num_lock=0b10000000) are tolerated because
9+ // they are ambient state, not deliberate key combinations.
10+ const intentional_mods = mod_raw & 0b00111111;
11+ if (intentional_mods != 0b100) return false;
12
13 // 6. Parse optional event type after ':'.
14 if (pos < buf.len and buf[pos] == ':') {
15@@ -634,14 +637,22 @@ test "isKittyCtrlBackslash" {
16 // ctrl + caps_lock + num_lock = 1 + (4 + 64 + 128) = 197
17 try expect(isKittyCtrlBackslash("\x1b[92;197u"));
18
19- // Combined modifiers: ctrl + shift = 1 + (4 + 1) = 6
20- try expect(isKittyCtrlBackslash("\x1b[92;6u"));
21+ // Combined intentional modifiers — must NOT match (ctrl+\ is the
22+ // detach key, not ctrl+shift+\ or ctrl+alt+\)
23+ // ctrl + shift = 1 + (4 + 1) = 6
24+ try expect(!isKittyCtrlBackslash("\x1b[92;6u"));
25
26 // ctrl + alt = 1 + (4 + 2) = 7
27- try expect(isKittyCtrlBackslash("\x1b[92;7u"));
28+ try expect(!isKittyCtrlBackslash("\x1b[92;7u"));
29
30 // ctrl + super = 1 + (4 + 8) = 13
31- try expect(isKittyCtrlBackslash("\x1b[92;13u"));
32+ try expect(!isKittyCtrlBackslash("\x1b[92;13u"));
33+
34+ // ctrl + shift + caps_lock = 1 + (1 + 4 + 64) = 70 — shift is intentional
35+ try expect(!isKittyCtrlBackslash("\x1b[92;70u"));
36+
37+ // ctrl + shift + num_lock = 1 + (1 + 4 + 128) = 134 — shift is intentional
38+ try expect(!isKittyCtrlBackslash("\x1b[92;134u"));
39
40 // Modifier without ctrl bit — must NOT match
41 // shift only = 1 + 1 = 2