Repository navigation
permissions: cover MCP/hook process-source configs and trailing-dot variants in is_sensitive_path - #271
Conversation
|
@Zongwei9888 — this PR touches the sensitive-path denylist and a CVE-2026-103572-class normalization bypass, so it needs a security review. A fork can't set the Summary for the reviewer:
|
|
Thank you, @raymondginger2018-sudo. The trailing-dot part is right: Windows drops trailing dots and spaces from path components when it opens a file, so The new patterns need to change before we can merge:
A version we'd merge: the trailing-dot probe as it is, plus a check that turns a file-tool write to one of the three hook files into an |
8c88f38 to
b25569f
Compare
|
@Zongwei9888 — rewritten as the version you described, pushed as
Tests cover both directions: writes ask even in Local preflight of this exact head (Windows, Python 3.12/3.13/3.14): no new failures; only the already-tracked Windows-only lifecycle/desktop flakes. |
|
Thank you for the quick rewrite, @raymondginger2018-sudo. This is the shape we asked for: reads stay free, the trailing-dot probe is kept, and stricter decisions keep their strength. One change before merge. Full Access. Full Access resolves to Please skip this check under Full Access, the same way Full Access skips the sensitive-path guard and origin approval ( |
The three hook-discovery files (user and workspace hooks.json, workspace .claude/settings.json) are not secrets: reads stay free. A written-in handler runs commands in a later session, so a file-tool write that would otherwise be allowed becomes an ask; stricter decisions (deny rules, plan mode, the read-only preset) are never relaxed, and no allow rule can silence the confirmation. Also match sensitive paths through Windows trailing dots and spaces, which the OS drops at open time while normpath keeps them: a stripped probe variant is checked alongside the literal path (the reported path stays as written).
Full Access resolves to full_auto with approval_policy=never, where the hook-write confirmation becomes a deny nobody can answer. Skip the check under Full Access, the same way it skips the sensitive-path guard and origin approval: build_permission_engine passes protect_hook_configs, off exactly when access_preset is FULL_ACCESS. The legacy full_auto path with an on_request approver keeps asking.
b25569f to
ec35749
Compare
|
@Zongwei9888 Done — the hook-write check is now skipped under Full Access, exactly along the lines you suggested. Implementation (
Tests (3):
Rebased onto latest Gate status: local CI replay passed — Thanks for the precise pointer to the Full Access skip pattern! |
|
Thank you, @raymondginger2018-sudo, this is exactly the shape we asked for. Merged on 2026-10-27 as 1452113. I checked it through
The flag comes only from the validated profile, and allow rules can't silence it. The PR title and description still describe the earlier five-pattern version, so the merge commit uses a title that matches what landed. Thanks for the careful iterations! |
What
Two targeted hardenings in
core/harness/permissions.py, both about the path-matching layer:Protect MCP/hook process-source configs. Adds 5 names to
SENSITIVE_PATH_PATTERNS:.mcp.json,mcp_servers_canonical.json,.deepcode/settings.json,.claude/settings.json,.deepcode-plus/settings.json.These files decide which external processes the next session spawns (MCP stdio servers,
workspace hooks), so a written-in server definition is delayed code execution; the generated
sync copies also carry plaintext env-derived keys. They belong in the same protected class
as
.env/.netrcalready on the list, and they are script-maintained outside the agenttool path, so denying tool-path access breaks no legitimate in-session workflow.
Match through trailing dots/spaces (CVE-2026-103572-class bypass). Windows drops
trailing dots and spaces from path components at open time while
os.path.normpathkeepsthem — the literal name
.mcp.json.opens.mcp.json, yet a literal compare misses it.is_sensitive_pathnow also probes per-componentrstrip(" .")variants (with./..components protected). The strip is match-only: the reported path stays as written.
Why
file tools under any permission mode.
as-written form is what CVE-2026-103572 exploited upstream of this pattern family.
Tests
3 new regression tests (red → green):
.vscode/settings.jsonor plain JSON files.Local run on Windows git-bash:
tests/test_harness_permissions.py38 passed. Thepre-existing
test_denylist_follows_symlinksfails locally withWinError 1314(symlinkprivilege) — that test exists unchanged on
mainand is environmental, not introduced here.Security Considerations
is_sensitive_path) for 5 explicit confignames; no exec/sandbox control-flow change, patterns are data-only additions.
logs and error messages keep the user-visible name.
tool path (that is the point for generated plaintext-key-bearing copies).