isOwnWorkingTree() treated any target at or below cwd as own, so a foreign
clone under cwd (cwd = $HOME, a vendor clone in a project) still had its
.llm-security-ignore, policy.json and custom SIG rules read. The target must
now also share cwd's git root: nearest ancestor with a `.git` entry (dir for
a clone, file for a submodule/worktree), or none for both. No git spawn.
tmpdir stays foreign. Chosen per the PM order: it is exactly the line
between "my repo" and "something I fetched", and the failure direction is
safe (foreign => config ignored => more findings, never fewer).
Red first: tests/lib/own-working-tree.test.mjs 4 fail / 5 pass (the 5 are
known-positives), tests/scanners/nested-clone-scope.test.mjs 5 fail / 4 pass
on the old rule. Green after; reverting the git-root comparison turns 9 red.
Suite 2306 / 2300 pass / 0 fail / 6 skip; hooks 370/0 (implicit root
untouched); golden 109/7/4, 61/61.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
loadPolicy() read .llm-security/policy.json from whatever root it was
given, and every scanner passes the SCANNED TARGET: scan-orchestrator
(policyRoot = resolve(args.target)), entropy-scanner (thresholds and
suppression patterns), signature-scanner (sig.custom_rules_path and
enabled_families), trigger-scanner (phrase lists) and ast-taint-scanner
(enabled, python_path). A foreign/cloned target could raise its own
entropy thresholds, disable SIG families, supply its own SIG ruleset or
name the interpreter the AST scanner spawns — configuring the scan of
itself. Same defect class as S3b's .llm-security-ignore fix.
Chosen: move isOwnWorkingTree() to scanners/lib/own-working-tree.mjs (one
copy, reused by the orchestrator's ignore-file check) and make
loadPolicy() refuse an EXPLICIT root that is not the caller's own tree —
defaults plus one stderr line, same form as S3b — because one rule in one
function covers every scanner and a future call site cannot forget it.
The IMPLICIT root (CLAUDE_PROJECT_ROOT/cwd, what every hook uses) is the
caller's own project by construction and is read as before.
entropy-scanner's calibration.policy_source no longer reports an ignored
file as its source.
New tests/scanners/policy-scope.test.mjs was red on 0d37f5a (foreign
target: entropy finding silenced, custom SIG rule loaded, findings differ
from the same tree without policy.json, no stderr line) and is green now;
its own-tree scenario (known-positive) is green before and after. The 15
existing policy tests that placed own-tree fixtures under os.tmpdir() now
use tests/helpers/own-tree.mjs (fixture under $HOME, cwd set to it).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>