Your assistant is supposed to summarize a document. Halfway through that document, a sentence tells it to abandon the task and reveal its hidden instructions. The user asked for a summary; the document is now trying to become the user.
That is the central problem in prompt injection: untrusted content attempts to redirect an AI system. This tutorial explains the trust boundary, shows a short integration with the public Patronus SDK, and shows why a detection result still needs an application policy. This article was prepared with AI assistance.
The attack changes who gives instructions
An LLM receives instructions and contextual material in a form it can interpret as language. A malicious passage exploits that overlap. It might claim higher authority, ask for a different answer, or try to make the assistant call a tool outside the intended task.
In a direct injection, the attempted redirection arrives in a user's message. In an indirect injection, it arrives through something the application retrieves: a page, email, document or tool response. Greshake and colleagues studied this second route in their research on indirect prompt injection.
For this tutorial, the attacker's objective is deliberately harmless: redirect a summary into a request for hidden instructions. There are no real credentials, external destinations or privileged tools. We are testing a detection boundary, not performing a data theft demonstration.
Define the boundary before choosing a detector
Consider a support assistant with a legitimate instruction: summarize a customer ticket and suggest a reply. The ticket body is evidence about the customer's problem. It has no authority to change the assistant's permissions, recipients or task. An embedded request to send the ticket elsewhere is therefore an attempted change of authority, even if it is written in polite language.
A useful threat model records four things: who can modify the input, which parts of the input they control, what outcome they want, and which capabilities the application exposes. The last point determines the consequences. A read-only assistant might produce a misleading answer; an assistant with messaging or database tools can turn the same redirection into an action. This is a design analysis of our example, not a claim that every injected instruction succeeds.
| Component | Role in this example | Authority |
|---|---|---|
| User's request | Summarize the supplied update | Defines the task |
| External update | Information to summarize | Provides data, not new permissions |
| Scanner | Returns evidence about the text | Does not authorize tool actions |
| Application policy | Decides whether to forward the input | Enforces this particular boundary |
Indirect-injection research motivates checking externally retrieved material, while OWASP describes a layered defensive approach. Neither source makes a detection score equivalent to a complete application authorization system. Our tutorial uses those ideas to define one narrow, testable boundary. 1, 2
Read the two inputs as a security question
The benign fixture contains an update about customer support. Its purpose remains compatible with the user's summary task. The injected fixture contains ordinary context followed by a request to reveal hidden instructions. The second passage attempts to replace the task, rather than adding a fact to the update.
That difference is more useful than a list of suspicious words. A training document can quote an attack for explanation, and an incident report can describe the exact instruction someone attempted. Their correct handling depends on the authorized task and context. The InjecGuard paper studies false alarms associated with attack-like wording; it is a reason to include difficult benign examples when evaluating detection, rather than declaring every quoted attack malicious. 3
The two fixtures here are deliberately easy to inspect. They establish that the integration has a normal route and a held route. They do not replace that broader evaluation.
Scan a short text with the public SDK
The public Patronus repository includes clients for TypeScript, Python and Rust. Here we use the TypeScript client, which works in Node.js. Install the published package in your own application and provide an API key through its environment. The client and its documentation are publicly accessible. 4
npm install @patronus-protect/api-client
This is the complete first request:
import { Patronus } from "@patronus-protect/api-client";
const patronus = new Patronus({
apiKey: process.env.PATRONUS_API_KEY,
});
const text = "Customer support response times improved this quarter.";
const result = await patronus.scanText(text, { categories: ["injection"] });
console.log(result.jobs[0].decision);
The string is deliberately ordinary. To explore the contrast, replace it with a synthetic document that includes an attempt to abandon the summary task. Keep the intended task fixed so that the comparison concerns the supplied content. The example uses the hosted Scanner API; the text is sent to that service. Its analysis profile requests only injection detection. It is not a demonstration of document confidentiality checks or every Patronus category.
The SDK submits the scan and resolves accepted jobs to terminal results. This saves the application from implementing its own polling loop. It does not decide whether the application should forward the content. The documentation distinguishes findings, which arrive as result data, from authentication, quota, timeout and transport failures, which raise PatronusError. 4
Read the response as a decision with conditions
An HTTP request can succeed while its analysis is incomplete. A job can finish while a requested category is missing. A detector can produce a finding without the SDK throwing an exception. Those are three different situations, so neither HTTP success nor the absence of an exception is sufficient permission to use the text.
The API documents lifecycle status, completion, coverage, category results and the overall decision separately. The application should read them in that order. Coverage asks whether the supplied content was represented; completion asks whether analysis finished successfully; the decision summarizes the category verdicts. 5
For this narrow example, a conservative policy is short enough to read in full:
function canUseText(result) {
const job = result.jobs?.[0];
const coverage = job?.coverage ?? result.coverage;
return result.status === "completed"
&& result.jobs?.length === 1
&& coverage?.state === "complete"
&& job?.status === "completed"
&& job?.completion?.state === "complete"
&& job?.decision === "allow"
&& job?.safety_status === "benign"
&& typeof job?.categories?.injection?.class_name === "string";
}
This function represents the application's policy, not an additional SDK API. It applies to the single-text, injection-only request above. An application requesting several categories must check its corresponding required results; one scanning files or URLs must also account for extraction and document coverage.
The safety_status check makes our treatment of review signals explicit. The API can report decision: "allow" alongside a review-oriented safety status in particular evidence conditions. Our example holds those inputs. That is a deliberate application choice, rather than a claim that the API's decision field is inconsistent. 5
Keep scanner failures on a separate path
Now connect the request to the predicate. Successful analysis can produce either true or false. A failed request must not turn into permission because some later branch wants to keep the workflow moving.
try {
const result = await patronus.scanText(text, { categories: ["injection"] });
console.log(canUseText(result) ? "Eligible for the next step" : "Hold for review");
} catch {
console.log("Scan unavailable: hold this input");
}
The example prints a route instead of calling an LLM. In your application, only the eligible route should reach the model. The held route needs an intentional treatment: review, a controlled retry or a rejected request. Retrying an unavailable service and accepting a detected injection are different decisions and should not share a fallback that silently forwards the original text.
Also keep the exact text associated with its result. Re-fetching the source after scanning can obtain a different document. A useful integration retains the original input or records its version so that the text entering the model is the text that was checked.
Detection is one part of the control path
Content detection, tool permissions and action approval address different boundaries. A scan result does not grant permission to perform an external action.
The SDK is a client for the hosted API. It is not a local Wolf Defender model invocation. Wolf Defender is Patronus' open injection classifier, and Patronus Ark is the local scanning stack. Their model and deployment documentation addresses a different level of the system. An API result should not be presented as an independent benchmark of one particular model artifact. 6
That distinction also shapes testing. A contract test can show that these snippets submit the expected request, interpret a complete benign response and hold incomplete or blocked responses. It does not measure detection recall. A model evaluation needs labeled inputs and measured outputs; an agent evaluation additionally needs to observe the actual downstream behavior.
A useful test matrix for this integration contains an allowed result, a blocked result, degraded completion, partial coverage, a missing injection result and a request failure. Each case asks a concrete engineering question: can this route reach the model when its stated prerequisite is absent? Once that behavior is correct, evaluate the detector on representative normal and adversarial inputs rather than inferring its quality from successful HTTP handling.
What to do after detection
Even a correctly allowed document remains external data. Preserve that provenance, avoid giving the summarizer unnecessary tools, and require independent permission for actions such as sending a message or changing a record.
Also test benign text that discusses attacks. A security report may contain suspicious language for a legitimate reason. Blocking that report is a different failure from missing an actual injection, and both need representation in an evaluation set.
For your first integration, collect a small set of representative inputs, label the intended behavior, run the exact deployed scanner configuration, and inspect both misses and false alarms. Record the SDK version and scan configuration alongside the results. Keep illustrative tests separate from a held-out benchmark.
Try the boundary in your own application
Start with the public Patronus SDKs and the Scanner API for integration into your application. The Wolf Defender model card explains the model's evaluation and limitations. For the broader product and deployment options, see Patronus Protect.
The practical next step is small: add one explicit check before a document enters the LLM context, and prove that the blocked and unavailable-scanner paths cannot reach the forwarding step.
References
- Greshake, K., Abdelnabi, S., Mishra, S., Endres, C., Holz, T., and Fritz, M. (2023). Not what you've signed up for: Compromising Real-World LLM-Integrated Applications with Indirect Prompt Injection. arXiv:2302.12173, v2.
- OWASP Cheat Sheet Series. LLM Prompt Injection Prevention Cheat Sheet. Technical guidance, accessed 12 October 2026.
- Li, H., and Liu, X. (2024; revised 2025). InjecGuard: Benchmarking and Mitigating Over-defense in Prompt Injection Guardrail Models. arXiv:2410.22770, v3.
- Patronus Protect. TypeScript API client. Installation, requests and errors, accessed 12 October 2026.
- Patronus Protect. Results and evidence. API response semantics, accessed 12 October 2026.
- Patronus Protect. Wolf Defender model card. Model, evaluation and limitations, accessed 12 October 2026.
The short examples illustrate the integration and its decision rule. They do not report a new detector benchmark.

Top comments (0)