← Back to Agent Permission Config Check
Claude Code, Cursor, and VS Code's Copilot agent mode all read a
project-level config that decides what an AI coding agent is allowed to do
without asking first, and which external MCP servers it can reach. Claude
Code's own .claude/settings.json carries a
permissions block (allow, deny,
ask rule lists); Claude Code, Cursor, and VS Code all read an
mcpServers block from a project file
(.mcp.json, .cursor/mcp.json,
.vscode/mcp.json respectively) in the same JSON shape. None of
it is reviewed the way application code is — it is typically written once,
during setup, by whoever got the agent running fastest.
The failure mode is not malice, it's friction. A permission prompt that
interrupts every git status or npm test gets
widened to a bare "Bash" allow rule the first time it becomes
annoying, and nothing narrows it back afterward because the agent stops
asking. An MCP server config copied from a blog post or a colleague's
machine during a quick setup keeps its http://localhost URL
and its inline API key exactly as written, because switching to an
environment-variable reference is a second step nobody was blocked on
taking. Both are one-line permission edits with no reviewer and no test
suite — the same unforced-drift shape this repository's own
agent-instructions-drift-check
documents for instruction files, applied to what the agent is allowed to
do rather than what it is told.
| Check | Source | Out of scope for v0 |
|---|---|---|
| Blanket Bash allow-all | A bare "Bash" or "Bash(*)" entry in permissions.allow | Non-Bash tools (Read, Write, WebFetch) granted broadly — real risk, but a much noisier, product-specific judgment about what "too broad" means for a file-editing or fetch tool |
| Unscoped destructive command | An unscoped allow entry for sudo, rm, curl, wget, chmod, eval, dd, mkfs, shutdown, or reboot, with no matching deny | Any other command; this is a short, named list of the highest-blast-radius families, not an exhaustive taxonomy |
| Plain-http MCP server | An mcpServers entry's url starting with http:// | Whether the server itself is trustworthy, malicious, or compromised — this tool never contacts it |
| Hardcoded-looking secret | A literal env/headers value matching a known token format (high confidence) or a long unrecognised literal (medium confidence) | Verifying the value is a real, live credential — this tool never sends it anywhere to test that |
Run it on every pull request that touches a permission or MCP config file:
- name: Check agent permission/MCP config run: node ventures/agent-permission-config-check/cli.mjs check --root .
The command exits non-zero if any high or medium finding is present — so the same pull request that widens an allow rule or adds an MCP server is the one that has to justify it.
General secret scanners (gitleaks, trufflehog) look for token-shaped strings anywhere in a repository's history and files, which overlaps with this tool's hardcoded-secret check. They do not parse permissions.allow/deny/ask rules for an allow-all Bash grant or an unscoped destructive command, and they do not know the mcpServers JSON shape well enough to flag a plain-http url specifically — both specific to how an AI coding agent's own permission config, rather than application code, grants risk.
Each is a deterministic, no-false-negative-by-construction shape: a rule string either matches the allow-all pattern or it doesn't, a url either starts with http:// or it doesn't. That is deliberately narrower than a full policy review — it catches the small number of shapes that are almost never intentional, not every possible over-broad grant, which would require understanding what the repo's own workflows actually need.
No. A narrowly-scoped allow list can still grant more than a given workflow needs, and a config with no MCP servers at all trivially passes every MCP-related check. This tool proves the absence of four specific, named risky shapes — it does not and cannot prove the presence of safety.