The Config File That Executed Itself
yaml.load() turns a settings file into a code-execution vector
Published Oct 29, 2026
Executive Summary
A vibe-coded app loads admin-editable YAML with yaml.load() — the unsafe loader. YAML's !!python/object/apply: tags let a document construct arbitrary Python objects, so a settings file becomes a code-execution payload the moment it is parsed. The file looks like config, but the loader treats it like code.
The Prompt
“Load the site's feature flags from a YAML file that admins can edit in the admin panel.”
The Flaw
yaml.load() (without a SafeLoader) honors YAML tags like !!python/object/apply: that construct arbitrary Python objects. A YAML document is therefore an execution payload, not just data. If the 'admin-editable' file is ever reachable by a less-trusted user — or the admin account is compromised — parsing it runs attacker code.
Risk & Impact
[Security] The feature-flags file is a code-execution vector. The damage depends only on who can write the file, and 'admin-only' is rarely a real boundary.
Cost: Remote code execution once an attacker can edit the settings file.
Code Comparison
❌ Vulnerable (AI generated)
❌ Vulnerable (AI generated)
✅ Hardened (production ready)
The Fix
Use yaml.safe_load() — it only constructs plain dicts/lists/scalars and ignores object tags. Never parse untrusted YAML with the full loader.
Prevention
- [linter] Bandit flags yaml.load() as high-severity (B506) — fail CI on any use of the unsafe loader.
- [ci] A test that feeds a YAML document containing !!python/object/apply: must raise or be ignored, never executed.
- [manual] Pin the exact loader in the prompt (rule 10: 'use safe_load') and forbid object tags (rule 6).
Why This Happened
Root cause: the prompt omitted Rule 6 and Rule 10 — the guardrails that would have caught this. See the full guide →
