mirror of
https://github.com/affaan-m/ECC.git
synced 2026-10-26 18:35:05 +00:00
fix(gateguard): lowercase the whole re-rooted exempt glob
realAbsoluteGlobRegex lowercased only the resolved prefix, so an absolute glob with uppercase text after its first wildcard stopped matching once its leading directory resolved through a symlink. Normalize the rebuilt pattern as a whole and cover it with a mixed-case case. The exempt-link test helper now fails on a nonzero hook exit or invalid JSON instead of reading it as an exemption, and the design note states the both-must-match rule directly. Refs #3655
This commit is contained in:
1 parent
119d4b246a
commit
2c11fd74c1
3 files changed
+15
-8
No files matched your search
@@ -176,9 +176,9 @@ for existing configurations. The precedence is documented in the skill's
|
||||
order of checks, and the table row warns that exemptions apply to sensitive
|
||||
and hard-linked targets too.
|
||||
|
||||
An exemption covers the path as written and its real location, not wherever
|
||||
a symlink points: the target's symlink-resolved path (for a new file, the real
|
||||
path of its nearest existing ancestor) must also match an exempt glob, judged
|
||||
An exemption requires both the path as written and its symlink-resolved
|
||||
location (for a new file, the real path of its nearest existing ancestor) to
|
||||
match an exempt glob. The resolved location is judged
|
||||
against the project root's real path for relative globs and, for absolute
|
||||
globs, against the glob with its literal leading directories resolved (`/tmp`
|
||||
is `/private/tmp` on macOS). Otherwise a link in an exempt scratch directory
|
||||
|
||||
@@ -240,7 +240,7 @@ function realAbsoluteGlobRegex(glob, isWin) {
|
||||
const realPrefix = targetClass().realLocationOf(isWin ? prefix.replace(/\//g, '\\') : prefix, isWin);
|
||||
if (!realPrefix) return null;
|
||||
const rest = segments.slice(literal).join('/');
|
||||
return exemptGlobRegex(`${normalizeForMatch(realPrefix).replace(/\/$/, '')}/${rest}`);
|
||||
return exemptGlobRegex(normalizeForMatch(`${realPrefix.replace(/[\\/]$/, '')}/${rest}`));
|
||||
}
|
||||
|
||||
// The path as written and its real location must both match: a symlink inside
|
||||
|
||||
@@ -3969,11 +3969,18 @@ function runTests() {
|
||||
})();
|
||||
const exemptLinkRun = (tool, toolInput, glob, root = exemptLinkRoot) => {
|
||||
clearState();
|
||||
const output = parseOutput(runHook(
|
||||
const result = runHook(
|
||||
{ cwd: root, tool_name: tool, tool_input: toolInput },
|
||||
{ GATEGUARD_EXEMPT_GLOBS: glob, CLAUDE_PROJECT_DIR: root }
|
||||
).stdout);
|
||||
return output && output.hookSpecificOutput ? output.hookSpecificOutput.permissionDecision : undefined;
|
||||
);
|
||||
assert.strictEqual(result.code, 0, result.stderr);
|
||||
const output = parseOutput(result.stdout);
|
||||
assert.ok(output && typeof output === 'object', `hook output must be JSON: ${result.stdout}`);
|
||||
if (!output.hookSpecificOutput) {
|
||||
assert.strictEqual(output.tool_name, tool, 'a pass-through must echo the tool input');
|
||||
return undefined;
|
||||
}
|
||||
return output.hookSpecificOutput.permissionDecision;
|
||||
};
|
||||
const exemptLinkCase = (name, fn) => {
|
||||
if (test(name, () => {
|
||||
@@ -4013,7 +4020,7 @@ function runTests() {
|
||||
exemptLinkCase('a project reached through a symlinked parent keeps relative and absolute exemptions', () => {
|
||||
const aliasRoot = path.join(exemptLinkBase, 'alias', 'proj');
|
||||
const plain = path.join(aliasRoot, 'scratch', 'notes.md');
|
||||
for (const glob of ['scratch/**', `${aliasRoot.replace(/\\/g, '/')}/scratch/**`, `${exemptLinkBase.replace(/\\/g, '/')}/alias/*/scratch/**`]) {
|
||||
for (const glob of ['scratch/**', `${aliasRoot.replace(/\\/g, '/')}/scratch/**`, `${exemptLinkBase.replace(/\\/g, '/')}/alias/*/scratch/**`, `${exemptLinkBase.replace(/\\/g, '/')}/alias/*/SCRATCH/**`]) {
|
||||
assert.strictEqual(exemptLinkRun('Edit', { file_path: plain, old_string: 'a', new_string: 'b' }, glob, aliasRoot), undefined, glob);
|
||||
assert.strictEqual(exemptLinkRun('Edit', { file_path: path.join(aliasRoot, 'scratch', 'link.md'), old_string: 'a', new_string: 'b' }, glob, aliasRoot), 'deny', glob);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user