Skip to content
Injection and supply chain

Injection and supply chain 💉

Injection and supply chain are cross-cutting, but they are not magic words. Classic injection uses untrusted input to alter an interpreter with a separable grammar: parameterized queries, escaping, and allowlists are strong controls. Prompt injection exploits a model that reads instructions and data in the same linguistic representation: no perfect escape exists. Repeating this is not caution; it is the difference between a fix and a prayer.

The two families should be treated together only because both cut across every domain. The mechanisms — and therefore the controls — remain profoundly different.

The injection spectrum: from syntax to semantics 🌈

Case Mechanism Main defenses
SQL or command injection Input becomes executable syntax of a known grammar Parameters, allowlists, least privilege
Injection via serverless events Payload inside S3/SNS/queue events that a function executes Trigger validation, dedicated roles
Poisoned pipeline (PPE) Build configuration executes code on the runner Review, isolated runners, branch protection
Prompt injection Hostile content diverts the model in the same channel as instructions Minimal tools, confirmations, data isolation, adversarial eval

Classic injection has one interesting indirect confirmation: the 2023 API list removed it because it was deemed “not specific to APIs” — when a problem is everywhere, it belongs to nobody. The point is not that it disappeared: it is that the control is inherited from the backend and does not need rewriting for APIs. Prompt injection, by contrast, was born as a first-level category precisely because the context makes it specific to a runtime (the model) that had never had a category before.

Indirect injection: the sub-case that most often surprises. The hostile content does not come from the user’s prompt, but from a web page fetched into the RAG, an uploaded file, or a tool’s output. The physical mechanism is closer to stored XSS — you plant the payload where a trusted process will consume it later — than to classic reflected injection. It follows that defending only the chat input is useless: every surface that becomes context must do the defending.

The prompt injection fork 🧠

Do not call a dangerous LLM “a database that talks”. The control must sit chiefly after the model: structured validation, action policy, restricted capabilities, and human approval for high impacts. The upstream defenses — system filters, sanitizing RAG documents — reduce frequency, but cannot guarantee separation: the model has no hardware or software boundary between instructions and data the way a parameterized query does.

That is why this series’ taxonomy treats injection as a family, not an identity: SQL, pipeline poisoning, and prompt injection share the family (input crossing a trusted boundary) but differ in mechanism and fix. When a vendor sells you “total prompt injection protection the way you protect against SQLi”, know that they are selling a feeling.

The supply chain moves upward 📦

Supply chain is the risk family where layer separation is most explicit. Dependencies, SDKs, container images, CI/CD actions, weights, datasets, and vector stores share one question: where did this come from, and how can I prove it?

Layer Category Characteristic control
Web libraries Web A03 Lockfiles, SCA, SBOM, version policy
Mobile SDKs Mobile M2 SDK inventory, library verification
Container images Kubernetes K02 Trusted registry, scan, signing and digest
Build pipeline CICD-SEC-3/8/9 Pinning, revoking integrations, artifact signing
AI models and data LLM05 Weight provenance, dataset verification
API consumers API10 Validation and sanitization of external payloads

The CI/CD list splits the problem into three separate mechanisms: abuse of dependency resolution (dependency confusion, typosquatting, CICD-SEC-3), ungoverned use of third-party services such as actions and plugins with full build access (CICD-SEC-8), and failure to verify the integrity of artifacts in transit (CICD-SEC-9). Treating them together in a single “use secure components” entry was the old approach: it works until someone asks which of the three points you covered.

The typical dependency-confusion case: your private library auth-core gets published under the same name on a public registry, and the pipeline resolver prefers the public version — which is perfectly legitimate, except that it is owned by whoever read the name in a commit. The fix is not “be careful”: it is restricting resolution to authorized registries, pinning by digest, and making the scope explicit in the manifest. Typosquatting (a lodash- package with an extra hyphen) is defended by the same policies, plus review of lockfile additions.

And the part SCA tools do not see: a CVE in a library is a known weakness; an unsigned build artifact is an unknown that crosses the pipeline. Integrity verification (pipeline signing, artifact digests, registries with sigstore or equivalent) is the control that turns “we updated dependencies” into “we know that what we deployed is what we signed”.

Reduce risk with lockfiles, version or digest pinning, trusted registries, SBOMs, artifact signing and verification, integration review, and secrets outside the repository. Supply chain severity does not depend on the frequency of a single CVE. It depends on a component crossing many systems before your code really starts.

Checklist ✅

  • Parameterized inputs and validated output before downstream actions.
  • External content (RAG, web, files) treated as hostile before the model.
  • Agentic tools with minimal capabilities and effect gates.
  • Dependencies and actions pinned to version or digest.
  • Artifacts signed, inventoried, and verified at deployment.
  • Adversarial eval on prompts and output for every model or system-prompt change.

The difference between classic injection and prompt injection is not one of degree: it is one of mechanism. Choose your controls there.

Also consult agents in production and configuration, secrets, and telemetry.

Last updated on