A language model has no native wall between the system prompt and the user message. Both arrive in the same context window, separated only by role labels that the model treats as hints rather than hard boundaries, as the OWASP prompt injection cheat sheet puts it: natural language instructions and data are processed together without clear separation. That is why concatenating an untrusted string into any part of the prompt is a design decision, not a formatting one.
OWASP's Gen AI project splits the attack into direct injection, where a user's own input alters behavior, and indirect injection, where instructions hide inside external content the model fetches, such as files, web pages or tool responses (LLM01: prompt injection). A refund payload assembled from a customer order, a customer free-text note and an upstream API can carry both kinds at once. The note is a direct channel into your refund agent. A field copied from a supplier's order record is an indirect one.
OWASP's mitigation list is short and blunt: constrain model behavior, validate expected output formats, filter input and output, enforce least privilege, require human approval for high-risk actions, and segregate and identify external content (same source). Least-privilege scopes for model context is that last item applied field by field. A production write-up of the same approach describes it as four cooperating layers: structural separation of untrusted content, tool-call allowlists, output validation against the authenticated user's permissions, and audit logging (theroadtoenterprise). The classification rule below decides what goes into the first layer.
The verification work you already do on the webhook that delivers the payload does not exempt you from this. A valid HMAC signature proves the request came from the sender and was not altered in transit (Svix). It says nothing about whether a customer-typed note is a safe instruction source. Signature check happens at the edge; scope classification happens at the prompt.



