cfgaudit

Rules / CFG067

CFG067 hooks defined in a project-scoped settings file

Severity: warn OWASP: LLM04:2026 – Supply Chain OWASP Agentic: ASI04:2026 – Agentic Supply Chain Vulnerabilities: LLM04:2026 now covers the model and data supply chain and refers the agentic one, "including MCP servers and tool registries", to ASI04 CVE: CVE-2025-59536

What cfgaudit checks

Claude Code hooks run automatically on lifecycle events. In a user's global ~/.claude/settings.json they were placed there by the user themselves. In a project-scoped file (.claude/settings.json or .claude/settings.local.json) they were placed there by whoever committed to the repository — and they execute on any developer who opens the project, including ones who have never used Claude Code and don't know hooks exist (CVE-2025-59536).

cfgaudit flags the presence of any hooks in a project-scoped settings file, regardless of their content. This is a separate, lower-severity signal from the content checks: CFG008 / CFG014 / CFG015 / CFG037 / CFG038 / CFG039 still independently flag dangerous hook commands. The rule does not fire on the user-global ~/.claude/settings.json (hooks there are intentional) or on plugin hooks.json files.

A failing hook lets the action through

A hook that fails (its script is missing or exits non-zero without a blocking code) does not stop the event by default. Measured on 2.1.296 with a project UserPromptSubmit hook pointing at a missing script and a local endpoint standing in for the API (#650): with no onFailure, and with "onFailure": "continue", the prompt still reached the model; with "onFailure": "block", which Claude Code honours from a project file, it stopped with "failed; blocking because onFailure is "block"" and no request was made. So a committed hook meant as a guard guards nothing unless it sets onFailure: "block"; on PreToolUse and PermissionRequest an invalid onFailure value drops the whole settings file instead (CFG012).

Trigger example

{
  "hooks": {
    "PreToolUse": [
      { "matcher": "*", "hooks": [{ "type": "command", "command": "./scripts/setup.sh" }] }
    ]
  }
}

Committed as .claude/settings.json → warn.

Safe alternative

Keep hook definitions out of committed project config. Put them in your user-global ~/.claude/settings.json, or document them as a reviewed onboarding step so each developer opts in knowingly.

References

Documentation for cfgaudit 1.18.1. Source of this page · Rule implementation