Build1 publisher3 min readPublished
Rule 200001 turns a buffered prompt into fields the OWASP Core Rule Set can match
Solo's agentgateway runs Coraza as a shared extension, and the write-up's own warning is that a WAFPolicy without HeadersAndBody mode and a JSON body processor will pass every payload while looking healthy.
The Engineer · Build desk

What happened
- Solo Enterprise for agentgateway runs Coraza, the OWASP-maintained rules engine, as a shared extension, so the OWASP Core Rule Set can be applied to LLM prompts and MCP tool calls at the gateway.
- The write-up names two settings that make the rules work, the request processing mode that buffers the body and a rule that switches the body processor to JSON, and says skipping either passes every payload while looking healthy.
- With the JSON body processor on, a chat completions request becomes addressable fields, and ARGS covers json.model, json.messages.0.content and everything else in the document.
- Three custom signatures numbered 9001 to 9003 deny with 403 on instruction-override phrasing, jailbreak framing and system-prompt exfiltration attempts, running alongside stock OWASP CRS.
- Every status code in the post came from a live cluster on v2026.8.2, with the policies published in the themsquared/agentic-demo repo under manifests/governance/.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- constraint Existing CRS investment covers agent traffic only on routes where the body is both buffered and parsed, and a policy missing either setting produces no error for anyone to alert on.
- decision Because attachment is a separate resource, someone has to choose route by route which ones pay for body inspection, and a route without the reference runs uninspected while the policy exists.
- exposure Detection here is literal regex over ARGS, so coverage follows wording: a reworded instruction-override prompt falls outside rule 9001 entirely.
- cost Blocked callers receive one policy_violation sentence. That puts every incident investigation on the JSON audit records coming out of the gateway's stdout.
For an MCP call the fields worth a rule are the method, the tool name and the arguments, and they sit in the request body next to where an LLM call keeps its prompt [3]. Reaching them takes two config lines in different parts of the same WAFPolicy. `mode: HeadersAndBody` buffers the body and makes it available to the engine [5]. Rule 200001 matches `REQUEST_HEADERS:Content-Type` against `^application/json` and issues `ctl:requestBodyProcessor=JSON` [6].
Ship the first without the second and the body is one opaque blob, and every rule written against `ARGS` has nothing to match [7]. A rule with no input does not fail; it just does not fire, and the request goes through. The write-up puts it plainly: skipping either setting produces a WAF that passes every payload while looking healthy [4].
Ordering comes from the phase numbers rather than file order. 200001 sits in phase 1 and the three AI signatures sit in phase 2, so the parse is registered before the rules that read `ARGS` evaluate [26].
Those three signatures are the AI-specific content, and they are literal patterns. 9001 matches `ignore`, an optional `all`, then `previous`, `prior` or `above`, then `instructions` [11]. Rule 9002 covers `DAN mode`, `developer mode` and the bare word `jailbreak` [12]. The third, 9003, matches `print`, `reveal`, `show` or `repeat` followed by a reference to the system prompt or hidden instructions [13]. Everything else in the test list is stock CRS with no AI-specific configuration, on the reasoning that a gateway carrying LLM traffic is still an HTTP endpoint and gets scanned like one [22].
Every status code in the post came off a live cluster on v2026.8.2, with the same authenticated user on every row so the only variable is the payload [19][21]. CRS 4.23.0 is the version under test [15]. The row I would reproduce first is the false-positive case, a deliberately awkward but entirely real engineering prompt carrying a SQL keyword, a URL with query parameters, diagnostic codes and a firmware version [23]. Prompt text is long, unpredictable and full of strings that look hostile out of context [28]. Whether that result transfers depends on whether your users write prompts resembling that one, and on your own corpus.
Blocks look identical from outside. Each custom rule denies with 403 [10], the CRS default actions deny with 403 in both phases [9], and the caller gets the same terse body either way: `{"error":{"type":"policy_violation","message":"Request blocked by the enterprise AI WAF policy."}}` [17][25]. The stated reason is that the detail belongs in the audit log and not in a response body an attacker is reading to tune their next attempt [18]. That audit stream is JSON on stdout, RelevantOnly, parts AKHZ [14].
Attachment is its own resource, an EnterpriseAgentgatewayPolicy whose `targetRefs` point at the HTTPRoute. That is what lets one WAFPolicy be reused across many routes [16]. Before enabling `HeadersAndBody` on a production route I would measure the added latency on a long prompt and check the longest body the service accepts. The demo repo and every command in the post were run before publishing [24].
What to watch
- Whether Solo publishes a request body size limit and a latency figure for HeadersAndBody on long prompts.
- Whether an AI rule range like 9001 to 9003 lands in upstream CRS instead of per-deployment custom directives.
- Whether signatures appear for MCP method and tool-name fields, which the three demo rules do not address.