cfgaudit

Rules / CFG091

CFG091 qwen-code auto-approves every tool call (tools.approvalMode: "yolo")

Severity: error OWASP: LLM03:2026 – Excessive Agency

What cfgaudit checks

qwen-code (QwenLM/qwen-code, a diverged Gemini CLI fork) reads its config from .qwen/settings.json. Its tools.approvalMode decides whether tool calls need the user's confirmation. cfgaudit flags a committed project file that sets it to "yolo" — the mode that auto-approves every tool call, shell commands included, with no prompt (verified against source: needsConfirmation returns false for YOLO on all tools except ask_user_question).

This is the qwen analogue of Claude Code's defaultMode: bypassPermissions (CFG004) and Gemini's defaultApprovalMode: yolo (CFG060), reached through a tools.-nested key. A committed .qwen/settings.json removes the human-in-the-loop for everyone who opens the repository.

{ "tools": { "approvalMode": "yolo" } }

→ error: anyone who opens this project runs qwen with every tool call auto-approved.

Why the severity is higher than the Cursor/Codex equivalents

qwen ships folder trust disabled by default — security.folderTrust.enabled defaults to false (trustedFolders.ts, enforced by a ?? false fallback). Verified chain:

  1. Folder trust off → isWorkspaceTrusted returns { isTrusted: true } (everything is trusted).
  2. The full project .qwen/settings.json is merged in (settings.ts: safeWorkspace = isTrusted ? workspace : {}).
  3. The untrusted-folder downgrade — if (!trustedFolder && approvalMode !== DEFAULT && !== PLAN) approvalMode = DEFAULT (config.ts) — does not fire, because the folder is trusted-by-default.

So a committed "yolo" takes effect with no trust prompt. This is the inverse of Cursor (workspace trust), Codex (project-layer denylist when untrusted), and Grok (folder trust): those gate project config on trust, which softens the equivalent finding; qwen does not.

Workspace settings also override user settings (customDeepMerge precedence: system defaults → user → workspace), so a committed "yolo" overrides a careful user who set default or plan in ~/.qwen/settings.json.

Why only "yolo" is flagged

The other approvalMode values are deliberately not flagged, because none of them is a committed escalation footgun:

Value Behaviour (verified) Flagged?
yolo auto-approves every tool call incl. shell error
auto qwen's shipped default (schema + runtime). Auto-accepts edits and a safe read-only allowlist; routes shell through a destructive-command guard + a classifier that blocks destructive actions no — flagging it would flag qwen's product default, not a committed footgun
auto-edit auto-approves only edit/info confirmations; still prompts for shell no — this is stricter than the auto default; committing it is a de-escalation
default always prompts (except policy-safe read-only tools) no
plan read-only; execution blocked no

tools.autoAccept is not read either: it is vestigial in qwen — the schema key exists (showInDialog: false) but no code in the approval path consumes it, so flagging it would report an effect that does not exist.

Safe alternative

Leave tools.approvalMode out of the committed .qwen/settings.json (qwen's own default already gates shell behind a classifier), or set it to "default" / "plan" so tool calls are confirmed. If a project genuinely needs looser approval, that belongs in a developer's own ~/.qwen/settings.json, where they opted in, not in a file that ships with the repository.

References

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