What it is
Integrity failures are about trusting something you did not verify. A webhook handler that processes any POST that looks like a payment event. A deserializer that turns untrusted bytes into objects, and therefore into code. A CI pipeline that runs a third-party step at the latest tag. An update mechanism with no signature. The common thread: the app assumes the input is what it claims to be.
It holds at eighth in 2025. The supply chain half of the old definition moved to A03; what remains is the application's own trust decisions.
How it shows up in AI-generated code
The AI-built version is almost always the webhook. Ask a tool to handle Stripe or GitHub webhooks and it will parse the JSON and act on it. Verifying the signature is a separate step with a separate secret, and unless the prompt mentioned it, it is missing. That means anyone who can find the URL can mark an order as paid. In Python apps the second pattern is pickle.loads or yaml.load on data from a request or a file, both of which execute code.
We also see timing-unsafe signature comparisons where verification does exist: comparing the HMAC with == leaks it byte by byte. Prbl's rules cover the deserialization and comparison cases; the webhook guide covers the rest.
Example: A webhook that believes the request
// POST /api/webhooks/stripe const event = JSON.parse(await req.text()); if (event.type === "checkout.session.completed") await markPaid(event.data.object.id);
const sig = req.headers.get("stripe-signature")!;
const raw = await req.text();
let event;
try {
event = stripe.webhooks.constructEvent(raw, sig, process.env.STRIPE_WEBHOOK_SECRET!);
} catch { return new Response("Bad signature", { status: 400 }); }
if (event.type === "checkout.session.completed") await markPaid(event.data.object.id);Verify against the raw body, not a re-serialised one, and use the provider's helper so the comparison is constant-time and the timestamp is checked for replay.
How to find it
- List every webhook route. For each, find the signature check. If there is none, that is the finding.
- Search for pickle.loads, yaml.load without SafeLoader, unserialize, and Java ObjectInputStream on anything from outside.
- Check CI workflows for third-party actions referenced by a mutable tag instead of a commit SHA.
- Search for == or === comparing signatures or HMACs.
- Prbl rules PRBL-R002 and the deserialization fix guides cover the code side; the webhook guide has the per-provider checklist.
How to fix it
- Verify every webhook with the provider's secret and helper, on the raw body, and reject on failure.
- Never deserialize untrusted data with a format that can execute code. Use JSON, or a safe loader.
- Pin CI actions to commit SHAs.
- Compare secrets with a constant-time function.
- Sign anything you ship to be auto-updated, and verify it on the receiving side.
What Prbl checks for this category
Run a free scan on a public repo or a live URL. Findings link to the fix guides below.
Fix guides for this category
Go deeper
Common questions
Why is an unverified webhook so serious?
Because the webhook is usually the thing that grants value: marks an order paid, upgrades a plan, provisions access. Without a signature check, whoever finds the URL can send that event themselves. Stripe, GitHub, Twilio and every serious provider sign their webhooks; the handler just has to check.
Is JSON.parse an integrity risk?
No. JSON has no code execution; the worst it does is produce a large object. The dangerous formats are the ones that reconstruct arbitrary objects: Python pickle, unsafe YAML, PHP unserialize, Java native serialization.
What moved to A03 in 2025?
The dependency and build-pipeline half. This category used to include vulnerable components; those now live in Software Supply Chain Failures. A08 keeps the app's own trust decisions: webhooks, deserialization, updates, and CI steps it chooses to run.