OWASP Top 10:2025 — A08 Software or Data Integrity Failures for AI-Generated Code

Written by the Rafter Team

You ask the model to "add an admin settings export," it writes pickle.loads(request.body) because pickle round-trips arbitrary Python objects in two lines, and you ship it. Six hours later someone POSTs a crafted blob and gets a shell. The model did exactly what you asked. It did not stop to ask whether the bytes on the wire should be trusted to instantiate classes. That is A08 — Software or Data Integrity Failures — in one paragraph, and AI codegen produces it on demand because the insecure version is almost always shorter.
What A08 actually is
A08 covers any code path that consumes code, configuration, or data without verifying it came from where you think it did and hasn't been tampered with. Concrete example: your app pulls a CI-built artifact from an S3 bucket and runs it. There's no signature, no checksum compared against a value stored somewhere the build pipeline can't write to. Anyone who can write to that bucket — a leaked CI token, a misconfigured IAM role, a compromised dependency in the build step — can replace your binary and you'll never know. Same shape applies to deserializing user-controlled JSON into typed objects via eval-adjacent paths, accepting webhook payloads without verifying the HMAC, or pulling a config file over HTTP at boot.
Why AI-generated code trips on it
LLMs reach for whatever stdlib function gets the test passing fastest. That means:
- Python:
pickle.loads,yaml.load(instead ofyaml.safe_load),dill,shelve. All of these execute code during deserialization. The model uses them because they're one line. - Node:
eval-style config loaders,vm.runInNewContexton user input, JWTs verified with the algorithm field from the token itself (alg: nonebypass), webhooks accepted without checking the signature header. - PHP/Laravel:
unserialize()on session blobs or cache values where the cache backend isn't trusted. - Build/deploy: Dockerfiles that
curl | bashan install script over HTTP, GitHub Actions pinned to@maininstead of a commit SHA,npm installon a postinstall-hook-friendly package list with no lockfile audit.
The pattern: any time the AI needs to "load this thing and turn it into a usable object," it picks the deserializer that handles the most types. That deserializer is almost always the one with code-execution semantics. It also reaches for unpinned dependency versions because latest is what the docs show.
Frameworks that make this worse: anything with heavy "magic" deserialization defaults — older Rails, Django with pickle session backends, Spring with Jackson polymorphic types enabled. The model has read enough of these codebases to assume the defaults are safe. They aren't.
The fix on agentic CLIs (Claude Code, Codex)
Don't review for A08 line-by-line. Ask the agent to audit the diff for the specific patterns. Paste this after a feature lands:
Audit this diff for A08 integrity issues. Specifically:
1. Any deserializer that can instantiate classes from bytes —
pickle, yaml.load, unserialize, Jackson polymorphic, etc.
Replace with the safe variant or a strict schema (pydantic,
zod) parse.
2. Any webhook or callback handler that doesn't verify a
signature/HMAC against a known secret before trusting the
body.
3. Any external script/artifact fetched at build or boot
without a pinned SHA or checksum.
4. JWT verification where the algorithm is read from the token
header.
List findings as file:line with the fix. Don't apply yet.
Then read the list before letting the agent apply anything. If you're scanning with Rafter, the Copy-for-AI button on each finding produces a prompt already shaped for this — paste it into Claude Code or Codex and the fix lands with the right context (the rule that fired, the surrounding code, the recommended remediation pattern).
One more thing the agent will not do unless you tell it: pin your CI actions to commit SHAs, not branch names, and add npm ci (not npm install) to your deploy script.
The fix on opinionated platforms (base44, Greta, OpenClaw)
You can't grep the codebase. You can still drive the platform's agent toward the right shape. Ask in plain language:
"Review every endpoint that accepts a webhook or callback from a third party. For each one, confirm we verify the signature header against the shared secret before reading the body. If we don't, add it. List which endpoints you changed."
"List every place we deserialize incoming data into typed objects. Confirm we're using a strict schema parser, not a generic deserializer that can instantiate arbitrary classes. If any handler uses pickle, yaml.load, unserialize, or eval on user data, flag it and replace it."
"For any background job that runs a script fetched from a URL, confirm the URL is HTTPS and the script content is checked against a known hash. If not, either bake the script into the deployment or add the hash check."
The constraint here is honesty: if the platform won't tell you what's actually running, you can't verify the answer. Ask the platform vendor directly whether they sign their build artifacts and whether tenant-supplied config is sandboxed. If they handwave, assume it isn't, and keep anything that processes attacker-controlled bytes behind a strict schema you defined.
See also
- A03 Software Supply Chain Failures — the dependency side of the same problem
- A02 Security Misconfiguration — unsigned CI artifacts usually live here too
- A06 Insecure Design — "trust this byte stream" is a design decision, not an accident