T07 · Tool Hijacking and Spoofing
- Location
governance.py:1321- Finding
Automatic hooks execute an unverified repository-supplied governance checker
- Content
View full analysis
Vulnerability Details
File Location:
governance.py:1321-1329; execution sinks intemplates/claude-hook-plan-gate.sh:29-38andtemplates/claude-hook-adr-cascade.sh:25-32
Vulnerability Type: Automatic execution of an unverified local tool
Risk Level: HighTechnical Analysis
During initialization, the Skill installs its bundled checker only when
scripts/governance.pydoes not already exist:python def plan_init(root: Path, cfg: dict | None, comps: list[Component] | None) -> list[tuple[str, str]]: """Return [(action, description)] without executing anything. Install what's missing; leave what exists alone.""" actions: list[tuple[str, str]] = [] if not (root / CONFIG_NAME).exists(): actions.append(("config", f"create {CONFIG_NAME} (this skill's config; checks can't run without it)")) if not (root / "scripts" / "governance.py").exists(): src = _checker_path() digest = hashlib.sha256(src.read_bytes()).hexdigest()[:12] actions.append(("script", f"copy governance.py into scripts/ (CI can't reach the skill directory on this machine) — from {src}, sha256 {digest}")) if not (root / ".githooks" / "pre-commit").exists() and is_git_repo(root): actions.append(("hook", "install .githooks/pre-commit and link it into .git/hooks (closes the path where direct pushes bypass CI)")) if not all((root / ".claude" / "hooks" / n).exists() for n in CLAUDE_HOOKS): desc = ("install two Claude Code hooks and register them in .claude/settings.json" " (hard gate before writing a new plan / cascade check right after editing a decision)")Mere existence of the checker suppresses installation of the trusted bundled copy. Unlike pre-existing Claude hook files, for which
do_initperforms a SHA-256 comparison against bundled files before registration, the pre-existing checker is not authenticated.The plan gate subsequently execu ...[truncated 2801 chars]
- Remediation
View remediation
Remediation Suggestions
- Before registering either Claude hook, calculate the digest of
scripts/governance.pyand require it to be byte-identical to the checker currently running from the Skill package. - If a different checker already exists, do not register hooks that invoke it. Report the mismatch and require an explicit, informed decision from the user.
- Offer a separate, clearly disclosed replacement operation that backs up or preserves the existing checker and installs the reviewed bundled copy only after confirmation.
- Apply the existing all-or-nothing verification model used for pre-existing Claude hook scripts to every executable component in the hook chain, including
scripts/governance.py. - For defense in depth, embed or store the expected checker digest during installation and have each hook verify that digest immediately before execution. Fail closed on mismatch rather than executing changed code.
- Document how users can remove the installed registrations and require re-verification whenever the checker is upgraded.
- Before registering either Claude hook, calculate the digest of
