Rules / CFG004
CFG004 defaultMode set to bypassPermissions or auto
Severity: error (bypassPermissions) / warn (auto), each dropping one level (warn / info) when the detected Claude Code no longer honours the value from that file
OWASP: LLM03:2026 – Excessive Agency
What cfgaudit checks
cfgaudit flags permissions.defaultMode when set to "bypassPermissions" or "auto". This is the schema location for the key — a top-level defaultMode is not honoured by Claude Code and is not flagged.
| Value | Effect | Severity |
|---|---|---|
"bypassPermissions" |
All permission checks disabled; Claude Code acts with full autonomy and no confirmation prompts. All subagents inherit this mode with no override. | error |
"auto" |
Confirmation prompts suppressed; permission rules still enforced, but any allow rule is acted on immediately. | warn |
Real-world incidents with bypassPermissions include developers losing entire home directories when Claude Code executed rm -rf ~/ without interruption.
Where the value is honoured, and where it is not
Current Claude Code refuses both values from the two repository-controlled files. Verbatim from the 2.1.263 binary:
settings defaultMode "bypassPermissions" ignored — only policy/user/flag settings may
grant bypass mode (projectSettings and localSettings are repo-controllable)
and the same sentence for "auto". Both fall back to default.
That does not make a committed value safe, and it is why cfgaudit keeps reporting it. The file being audited is read by everyone who clones the repository, and an installation on an older release still honours it. So the finding stays and the severity drops one level, with a message that says which of the two worlds the reader is in:
| Scope | Detected Claude Code | bypassPermissions |
auto |
|---|---|---|---|
| project, project-local | at or above the cutoff | warn |
info |
| project, project-local | below the cutoff, or undetected | error |
warn |
| user | any | error |
warn |
An undetected version keeps the honoured reading on purpose. Absence of version information is not evidence that the newest behaviour applies, and treating it as such would hide a value that is live for every reader on an older release.
The cutoffs are measured, not read off the CHANGELOG
One repository carrying the value in .claude/settings.json, run under --debug against each shipped binary, reading the resolved session mode out of the debug log:
| Binary | Value | Warning emitted | Resolved mode |
|---|---|---|---|
| 2.1.252 | auto |
yes | default |
| 2.1.252 | bypassPermissions |
no | bypassPermissions |
| 2.1.263 | bypassPermissions |
yes | default |
| 2.1.263 | bypassPermissions, user scope |
no | bypassPermissions |
So the bypass half is bracketed exactly between 2.1.252 and 2.1.257, which is also the release the CHANGELOG names:
2.1.257 — Changed
defaultMode: "bypassPermissions"in.claude/settings.jsonor.claude/settings.local.jsonto be ignored, like"auto"; set it in user or managed settings, or pass--permission-mode
The auto half was already ignored in 2.1.252, the oldest binary available, and it has no CHANGELOG entry of its own. cfgaudit therefore uses 2.1.252 as an upper bound on the real cutoff rather than as the cutoff: on a release between the true one and 2.1.252 the finding keeps its higher severity, which errs towards reporting.
The user-scope row is measured too, not assumed: the same value in ~/.claude/settings.json on 2.1.263 produced no warning and resolved to bypassPermissions.
If your readers are not on your version
The version that decides the severity is the one cfgaudit detects on the machine running the scan, which is not necessarily the one your colleagues or your CI image run. --claude-version pins it, and it works in both directions:
$ cfgaudit --claude-version 2.1.250 .
… [error] CFG004: defaultMode: "bypassPermissions" disables all permission checks …
Pin it to the oldest release your team still has installed to audit for that population, or use the config file's per-rule severity override to put the finding back at the level your policy wants.
Trigger examples
{ "permissions": { "defaultMode": "bypassPermissions" } }
{ "permissions": { "defaultMode": "auto" } }
Safe alternative
Omit defaultMode entirely (interactive mode is the default), or set it explicitly:
{ "permissions": { "defaultMode": "default" } }
Enable bypassPermissions only in isolated, throwaway environments (containers, ephemeral VMs) where destructive actions have no lasting impact.
References
- Claude Code docs: Modes
- Real-world home directory destruction incidents (October 2025, December 2025)
- CFG001: unrestricted
Bash(*)— limits blast radius even when auto mode is active
Documentation for cfgaudit 1.18.1. Source of this page · Rule implementation