The Cart That Ran Code
pickle.loads() on a client cookie is remote code execution
Published Oct 29, 2026
Executive Summary
A vibe-coded Flask app 'remembers' the shopping cart by pickling it into a cookie and unpickling it on the next request. pickle reconstructs arbitrary Python objects, so a crafted cookie runs attacker code during deserialization. The cart is attacker-controlled bytes wearing a session costume.
The Prompt
“Remember the user's shopping cart between visits by saving it to a cookie.”
The Flaw
pickle.loads() reconstructs arbitrary Python objects, and a malicious payload can execute code during deserialization (via a crafted __reduce__). Storing the pickled cart in a client-side cookie means the user controls the bytes that get unpickled — a forged cookie executes commands as the server process. The round-trip convenience is the trap.
Risk & Impact
[Security] Any visitor can craft a cookie that runs code on the server — no login required. The 'cart' feature is an open deserialization endpoint.
Cost: Remote code execution and full server compromise from a single forged cookie.
Code Comparison
❌ Vulnerable (AI generated)
❌ Vulnerable (AI generated)
✅ Hardened (production ready)
The Fix
Serialize untrusted data as JSON — data, never code — and validate every field on the way back in. Never unpickle bytes you did not create yourself.
Prevention
- [linter] Bandit flags pickle.loads() as high-severity (B301) — fail CI on any pickle use in a web request path.
- [ci] A test that feeds a known-malicious pickle payload must raise or reject it, never execute it.
- [manual] Forbid pickle for anything crossing a trust boundary (rule 6), and validate deserialized data against a schema (rule 3).
Why This Happened
Root cause: the prompt omitted Rule 3 and Rule 6 — the guardrails that would have caught this. See the full guide →
