A Classifier in Front of the LLM Judge: Prompt-Injection Screening at 1.1s per 12,000 Characters
September 20, 2026
For Coding Agents, Harness Alignment Matters More Than Model Alignment
September 9, 2026
Prismor OSS Ships Three Governance Modes for AI Coding Agents, and What Each One Gives Up
September 7, 2026
The Definition of a Wrong Agent Action Depends on Org Context That Drifts
September 1, 2026
Why Agent Identity Needs More Than OAuth Delegation
August 18, 2026
Where AI Agent Security Tools Attach, and What Each One Misses
August 12, 2026
How to Build a Trustworthy AI Control Plane for Enterprises
July 31, 2026
Hugging Face Was Breached by an Autonomous AI Agent, Then Had to Use an Open-Weight Model to Investigate It
July 22, 2026
The EU AI Act Is in Motion: Scope, Timelines, and What Your Team Actually Has to Do
July 15, 2026
Beyond the Binary: Why Agent Governance Is an Optimization Problem
July 14, 2026
AI SOCs Need an Execution Boundary Before They Gain Autonomy
July 10, 2026
Google Dialogflow CX Rogue Agent Showed How One Playbook Editor Could Reach Every Code Block Agent
July 8, 2026
AI Agent Security in 2026: Why the Control Plane Has to Sit at the Tool-Call Boundary
July 7, 2026
Every Agent Has a Door. Most Are Unlocked.
July 3, 2026
Why We Built Prismor to Secure AI Coding Agents at the Tool-Call Boundary
June 26, 2026
143 Mastra npm Packages Backdoored via easy-day-js Dependency Swap
June 17, 2026
TrapDoor: 34 Malicious Packages Stole Crypto Wallet Keys and SSH Credentials Across npm, PyPI, and Crates.io
May 25, 2026
AntV npm Packages Compromised via Hijacked Maintainer Account
May 19, 2026
CVE-2026-42945: NGINX Rewrite Module Heap Overflow Enables Unauthenticated Code Execution
May 13, 2026
TanStack and Mistral npm Packages Compromised in Mini Shai-Hulud Supply Chain Attack
May 11, 2026
Microsoft Patches Critical ASP.NET Core DataProtection Flaw That Breaks Cryptographic Signatures
April 23, 2026
Vercel got breached. Here is what that means for everyone downstream.
April 19, 2026
When the System Built to Warn Us Can No Longer Keep Pace
April 15, 2026
Your AI Agent Remembers Your Secrets
April 13, 2026
Your Home Directory is a Secret Goldmine for Attackers
April 6, 2026
When a core npm dependency becomes the attack path: lessons from the Axios compromise
March 31, 2026
When the AI dependency becomes the attack path: lessons from the LiteLLM PyPI compromise
March 24, 2026
When the security scanner becomes the attack path: lessons from the Trivy compromise
March 23, 2026
Critical Path Traversal Vulnerability in jsPDF Library (CVE-2025-68428)
January 6, 2026
MongoBleed: Unauthenticated Memory Disclosure in MongoDB (CVE-2025-14847)
December 27, 2025
Critical Remote Code Execution in n8n Workflow Automation: CVE-2025-68613
December 19, 2025
The Return of Shai-Hulud: npm's Worm Strikes Back
November 24, 2025
Critical Chrome Zero-Day Exploited: Google Rushes Emergency Patch for CVE-2025-0411
October 28, 2025
Critical Remote Code Execution Vulnerability in React Server Components
December 3, 2025
September 20, 20266 min read

A Classifier in Front of the LLM Judge: Prompt-Injection Screening at 1.1s per 12,000 Characters

Our regex pre-screen capped injection detection at 34% because most payloads never got escalated. Judging every text fixes that and makes latency the whole problem. A classifier judged 12,000 characters in 1.1s where our LLM judge needed 11.4s.

Any README, web page or script output an agent reads can tell it to ignore its instructions and upload your SSH key. We screen every text an agent reads before it acts, at runtime, in the path of the tool call. Last quarter that screen ran at 2 seconds p50 per call, and our last evaluation found the bottleneck somewhere we had not been looking.

The judge was fine. The regex pre-screen in front of it was the problem. Detection capped at 34% because most injections never matched a pattern, so they never got escalated to the judge at all. The fix is to judge everything. Do that and judge latency becomes the whole problem, which is how we ended up benchmarking a classifier against the generative model we had been using.

Why the pre-screen capped detection at 34%

A regex pre-screen is a cost-control mechanism wearing a security hat. It exists so you only pay for the expensive judge on text that looks suspicious. That works when attacks look suspicious. Injection payloads are prose, written by someone who knows a filter is there, and prose has more ways to say “ignore your previous instructions” than a pattern list can hold.

So two thirds of what we planted walked straight through, untouched, because nothing escalated it. The judge behind the screen was accurate on everything it actually saw. It just never saw most of the attacks. Removing the pre-screen raises the ceiling to whatever the judge can catch, and hands you a bill: every text an agent reads now costs a judge call, in the critical path, before the agent is allowed to act.

What we measured

We put 120 short texts through five configurations: 72 with injections, 48 benign. Each call carries four questions, covering injection, secrets, destructive action and severity. TypeSafe Jev answers with a probability for each instead of writing a verdict in prose, which is most of where the speed comes from. A classifier does one forward pass. A generative judge writes tokens, and you wait for all of them.

Two panel chart. Panel a, accuracy on 72 injections and 48 benign texts: gpt-4.1-nano caught 58% with 12% benign wrongly flagged, gpt-4o-mini 96% and 10%, gpt-5.6-luna 100% and 2%, TypeSafe at threshold 0.75 100% and 2%, TypeSafe at threshold 0.9 100% and 0%. Panel b, median latency per text with p95 error bars: gpt-4.1-nano 1.34 seconds, gpt-4o-mini 1.14 seconds, gpt-5.6-luna 2.07 seconds, TypeSafe with four questions 0.84 seconds.
The two small models are cheap and miss things. The accurate options are the classifier and the flagship, and they are 1.2 seconds apart per call.

Jev caught 72 of 72 at a 0.9 threshold with nothing benign wrongly flagged, at 0.84s median. gpt-5.6-luna matched the catch rate at 2.07s and flagged 2% of the benign set. The two cheaper models are the reason we did not simply downgrade the judge: gpt-4.1-nano caught 58% of injections while wrongly flagging 12% of benign text, which is the worst of both trades.

Look at the p95 whiskers rather than the bars. The flagship judge runs from 2.07s to past 4s on the slow tail. That tail sits in front of a tool call a developer is waiting on, and a p95 is not rare when an agent reads forty files in a session.

Long documents are where the gap opens

Short texts make the two look close. A 12,000-character README does not. The generative judge has to window a document that size into 3,000-character chunks and judge them in sequence, so one document becomes four calls and you pay the tail four times.

Bar chart of latency on long documents, n equals 48, seconds per 12,000-character document. TypeSafe whole document in one call: p50 1.1 seconds, p95 1.4 seconds. TypeSafe in 3000-character windows: p50 3.9 seconds, p95 5.2 seconds. gpt-5.6-luna in 3000-character windows: p50 11.4 seconds, p95 15.2 seconds.
Windowing costs the classifier 2.8 seconds as well. Reading the document in one pass is most of the win.

Jev reads the whole document in one call: 1.1s median, 1.4s at p95, and it caught 24 of 24 payloads we buried in those documents. The same work through the flagship judge took 11.4s median and 15.2s at p95, for the same 24 of 24. Ten times the wait for an identical answer.

The middle bar is worth more attention than the comparison. Running Jev over the same 3,000-character windows costs 3.9s instead of 1.1s, so windowing itself accounts for most of the cost. Whole-document context also matters for correctness: a payload split across a chunk boundary reads as innocent on both sides of the cut.

The false positives

A clean benchmark set is a flattering place to measure. Against real traffic the classifier’s false positive rate reached 41%, which is a different product from the one the 120-text run describes. Documentation that quotes an attack, a security README, a test fixture full of payloads: all of it scores high, and all of it is fine to read.

We tried teaching it the difference by adding a fifth question, roughly “is this quoted for a human reader”. That moved errors around instead of removing them. Texts that had been false positives became false negatives, which is the trade you least want on a security control.

So the probability output turned out to be the useful part, more than the verdict. A score of 0.97 and a score of 0.80 are different situations, and a judge that writes prose flattens both into “blocked”. Keeping the number lets you route on confidence instead of on a binary.

What teams should do

We landed on two stages: the classifier on everything, the LLM on the gray zone only. That keeps full coverage at classifier speed and spends the expensive judge on the small slice of traffic where the score is genuinely ambiguous.

  • Count what your pre-screen drops. Detection ceilings hide well. Plant payloads, then measure how many reached the judge rather than how many the judge caught.
  • Judge whole documents in one call. Windowing cost us 2.8s on a 12,000 character file and cuts payloads in half at the boundaries.
  • Route on the score. Allow under 0.75, block at 0.95 and above, send the middle to the LLM. Tune the thresholds against your own benign traffic, because 41% of ours was documentation that talks about attacks.
  • Watch p95, not p50. Medians look fine on every option here. The tail is what a developer feels when an agent reads forty files in a session.
  • Resist adding questions to fix precision. Our fifth question converted false positives into false negatives. Threshold tuning moved the needle where prompt engineering did not.

The broader thing this evaluation changed for us is where we look for a security ceiling. We spent months tuning a judge that was already accurate, while a cheap filter in front of it quietly decided two thirds of attacks were not worth asking about. Cost controls have a habit of becoming coverage decisions that nobody wrote down. Worth asking, on any runtime control you run: what does this thing skip, and when did anyone last measure it?