From 23939c637e1bb747e320f7d54e472e754cc4bdd8 Mon Sep 17 00:00:00 2001 From: haimingZZ Date: Tue, 29 Sep 2026 01:17:25 +0800 Subject: [PATCH] fix(gateguard): strip SOFT HYPHEN (U+00AD) from sanitizePath --- scripts/hooks/gateguard-fact-force.js | 2 ++ tests/hooks/gateguard-fact-force.test.js | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/hooks/gateguard-fact-force.js b/scripts/hooks/gateguard-fact-force.js index 6756a0b79..932a751a8 100644 --- a/scripts/hooks/gateguard-fact-force.js +++ b/scripts/hooks/gateguard-fact-force.js @@ -1274,6 +1274,7 @@ const BIDI_ISOLATES = [0x2066, 0x2069]; // LRI..PDI const ZERO_WIDTHS = [0x200b, 0x200d]; // ZWSP..ZWJ const WORD_JOINER = 0x2060; const BYTE_ORDER_MARK = 0xfeff; +const SOFT_HYPHEN = 0x00ad; const VARIATION_SELECTORS = [0xfe00, 0xfe0f]; const VARIATION_SUPPLEMENTS = [0xe0100, 0xe01ef]; // MONGOLIAN..TAGS (VS17..VS256) const TAG_BLOCK = [0xe0000, 0xe007f]; // ASCII-smuggling tag characters @@ -1307,6 +1308,7 @@ function sanitizePath(filePath) { inRange(code, ZERO_WIDTHS) || code === WORD_JOINER || code === BYTE_ORDER_MARK || + code === SOFT_HYPHEN || inRange(code, VARIATION_SELECTORS) || inRange(code, VARIATION_SUPPLEMENTS) || inRange(code, TAG_BLOCK) || diff --git a/tests/hooks/gateguard-fact-force.test.js b/tests/hooks/gateguard-fact-force.test.js index 9e6a18334..2e627f410 100644 --- a/tests/hooks/gateguard-fact-force.test.js +++ b/tests/hooks/gateguard-fact-force.test.js @@ -3374,7 +3374,7 @@ function runTests() { if ( test('sanitizePath strips CI-defined dangerous invisible unicode from denial paths', () => { const file_path = - '/src/eu2028\u2028eu2029\u2029app.js\u200bhidden\u2060name\ufefftail\u3164x\u0091c1.js'; + '/src/eu2028\u2028eu2029\u2029app.js\u200bhidden\u2060name\ufefftail\u00adsoft\u3164x\u0091c1.js'; const input = { tool_name: 'Edit', tool_input: { file_path, old_string: 'foo', new_string: 'bar' } @@ -3386,7 +3386,7 @@ function runTests() { ? output.hookSpecificOutput.permissionDecisionReason : '' ); - for (const bad of ['\u2028', '\u2029', '\u200b', '\u2060', '\ufeff', '\u3164', '\u0091']) { + for (const bad of ['\u2028', '\u2029', '\u200b', '\u2060', '\ufeff', '\u00ad', '\u3164', '\u0091']) { assert.ok(!reason.includes(bad), `denial reason must not carry U+${bad.codePointAt(0).toString(16)} (${bad})`); } assert.ok(reason.includes('app.js'), 'visible path text must remain');