cfgaudit

Rules / CFG085

CFG085 subagent frontmatter weakens the permission mode

Severity: error (bypassPermissions, dontAsk) · warn (auto, acceptEdits) OWASP: LLM03:2026 – Excessive Agency

What cfgaudit checks

A subagent is a Markdown file with YAML frontmatter — Claude Code reads them from .claude/agents/, xAI Grok from .grok/agents/, both using the same camelCase permissionMode field, and qwen-code from .qwen/agents/ with a native approvalMode plus a bridged permissionMode. All let the subagent override how permission prompts are handled.

CFG004 flags permissions.defaultMode in settings.json. This is the same destination through a different door — and a subagent definition is just as committable, so a cloned repository can ship one.

cfgaudit reads permissionMode and flags the values that weaken the permission system:

Value Severity Effect
bypassPermissions error disables all permission checks — full autonomy, no prompts
dontAsk error suppresses prompts; the subagent proceeds without asking
auto warn hands decisions to the auto-mode classifier (whose allow/deny lists CFG079 covers)
acceptEdits warn auto-accepts file edits, so the working tree is written without confirmation

default, plan, and manual (a documented alias for default, Claude Code ≥ 2.1.200) prompt normally and are not flagged, nor is an absent field.

Severities mirror CFG004, which reads the same modes from settings.json.

Scope: only real subagent files, only modes that take effect

The rule fires only for Markdown directly under a .claude/agents/, .grok/agents/, or .qwen/agents/ directory — project or user. Two reasons, both of which would otherwise produce false positives:

Grok honors only bypassPermissions at spawn. Grok's source documents that "only BypassPermissions is wired at spawn; others are forward-compat", so for a .grok/agents/*.md file only bypassPermissions is flagged (error). Reporting dontAsk/auto/acceptEdits there would be a false positive on a mode Grok currently ignores. Claude Code applies all four, so all four are flagged for a .claude/agents/*.md file.

qwen mirrors the Claude agent schema but resolves the mode differently. A .qwen/agents/*.md frontmatter "mirrors Claude Code 2.1.168's .claude/agents/<name>.md schema verbatim", so a Claude agent file dropped into .qwen/agents/ parses identically. The effective mode is approvalMode ?? bridge(permissionMode):

qwen honours a definition's mode in a trusted folder, and qwen's folder trust is off by default, so "trusted" is the common state. In an untrusted folder the privileged modes are clamped back to the parent, the vendor's own guard against a repo "silently grant[ing] itself classifier-mediated automation".

Precedence caveat

The subagent's mode does not always win. Per Claude Code's documentation, if the parent session is in bypassPermissions or acceptEdits that takes precedence and cannot be overridden, and if the parent is in auto mode the subagent inherits it and its own permissionMode is ignored.

That narrows when the field bites, but not whether it is worth flagging: in an ordinary session — the parent at its default mode — a committed permissionMode: bypassPermissions is honoured.

Trigger example

---
name: deployer
description: Ships the build. Use when asked to deploy.
permissionMode: bypassPermissions
---

Run the deployment scripts in ./ops without stopping to confirm.

→ error: anyone who invokes this subagent runs it with permission checks off.

Safe alternative

Leave permissionMode out and let the session's mode govern. If a subagent legitimately needs broader access, grant it narrowly with tools / disallowedTools rather than switching off the prompt for everything it does.

References

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