high · 7.5 Patched

Coraza JSON body processor: argument-limit truncation reopens an unbounded-depth gjson.Valid stack overflow (process crash)

CVE-2026-107826 · GHSA-6gcq-wc29-5xf2 · GO-2026-6693 · published 8 Oct 2026

How serious is it

  • Severity: high, CVSS score 7.5 out of 10.
  • No exploit prediction (EPSS) score yet.
  • Not on the CISA list of exploited vulnerabilities.
  • No public exploit in Exploit-DB.

Is your app affected by CVE-2026-107826?

Paste this prompt into Claude Code, Cursor or any AI coding tool: it runs a free Shippable scan of your project and fixes what it finds. Built with Lovable, Bolt or v0? Scan your live app. Or upload your package file.

Who is affected

  • github.com/corazawaf/coraza/v3 (Go) >= 3.0.0, < 3.8.1

How to check

  1. Run go list -m github.com/corazawaf/coraza/v3 in your project to see the installed version, including copies pulled in by other packages.
  2. Or paste your lockfile into the checker: it lists every package with a known vulnerability, this one included.

How to fix

Copies a prompt for Claude Code, Cursor or Codex: it checks your lockfiles for this and upgrades it.
  1. Upgrade github.com/corazawaf/coraza/v3 to 3.8.1 or later, commit the lockfile and redeploy.
  2. If a vulnerable version was live, check your logs and rotate the secrets that server could read.

The advisory

### Summary The JSON body processor (`internal/bodyprocessors/json.go`) can be made to crash the whole process with an unrecoverable `fatal error: stack overflow`, using a request body that is well under the recommended `SecRequestBodyLimit` and the default `SecArgumentsLimit`. ### Root cause `readJSON` (json.go:113-143) runs a bounded, best-effort flattening walk (`readItems`) and *afterwards* calls `gjson.Valid(s)` on the raw body if `readItems` returned no error: ```go json := gjson.Parse(s) ... truncated, err = readItems(json, key, maxRecursion, argumentLimit, byteBudget, &usedBytes, &argCount, res) if err != nil { return res, truncated, err } if !gjson.Valid(s) { return res, truncated, errors.New("invalid JSON") } ``` `gjson.Valid` (gjson v1.18.0, `validany` -> `validarray`/`validobject`) recurses once per nesting level with **no depth bound**. `readItems` does have a depth bound (`maxRecursion`), enforced here (json.go:163-182): ```go func readItems(json gjson.Result, objKey []byte, maxRecursion int, argumentLimit int, byteBudget int, usedBytes *int, argCount *int, res map[string][]string) (truncated bool, err error) { if byteBudget > 0 && *usedBytes >= byteBudget { return true, nil // <-- checked first } if argumentLimit > 0 && *argCount >= argumentLimit { return true, nil // <-- checked second } ... if maxRecursion <= 0 { return false, errors.New("max recursion reached while reading json object") } ``` The byte-budget and argument-limit checks run *before* the recursion-depth check, and they short-circuit the walk with `truncated=true, err=nil` instead of recursing further. If the configured `SecArgumentsLimit` (`ArgumentLimit`, default 1000, `internal/corazawaf/waf.go:359`) is reached by earlier, shallow values in the document, `readItems` stops walking *before it ever reaches* a deeply nested tail later in the same document — so the `maxRecursion` error is never produced, `err` comes back `nil`, and `readJSON` falls through to the unconditional `gjson.Valid(s)` call on the complete raw body, including the part `readItems` never visited. This is not a new interaction with the recursion limit itself: at v3.7.0, `gjson.Valid` ran unconditionally before any recursion check at all, so a plain deeply-nested body crashed the process directly. A later fix added a depth check that returns an error before `Valid` runs for the *straightforward* case (nesting reached before any other guard fires). The argument-limit / byte-budget guards added since then (GHSA-6r3q-mjv7-xr8m, GHSA-3ww9-vw83-9w5x) reopened the same crash for the case above, because they short-circuit the walk (and therefore the recursion counter) ahead of the depth check, on both the request and response body path (`ProcessResponse` calls the same `readJSON`, json.go:57-88). Because this is `fatal error: stack overflow`, not a `panic`, it is **not** recoverable by any `recover()` in the calling goroutine — the process terminates unconditionally. ### PoC ```go package bodyprocessors import ( "strings" "testing" ) func TestStackOverflowRepro(t *testing.T) { body := "[" + strings.Repeat("1,", 1000) + strings.Repeat("[", 13_000_000) // 13,002,001 bytes total: under the recommended SecRequestBodyLimit // (13107200, coraza.conf-recommended:78) and default ArgumentLimit (1000, // internal/corazawaf/waf.go:359). _, _, _ = readJSON(body, 20, 1000) } ``` ``` $ go test -run TestStackOverflowRepro ./internal/bodyprocessors/ -v runtime: goroutine stack exceeds 1000000000-byte limit fatal error: stack overflow ... github.com/tidwall/gjson.validarray(...) .../gjson@v1.18.0/gjson.go:2584 github.com/tidwall/gjson.validany(...) .../gjson@v1.18.0/gjson.go:2499 github.com/tidwall/gjson.validarray(...) .../gjson@v1.18.0/gjson.go:2589 ... (repeats until the goroutine stack limit is hit) ``` Reproduced against commit `19b86824` (tag `v3.8.0`), both by calling `readJSON` directly and end-to-end through the recommended `coraza.conf-recommended` configuration (JSON `Content-Type`, default `SecArgumentsLimit`, recommended `SecRequestBodyLimit`). ### Impact An unauthenticated attacker who can send an HTTP request body (any endpoint protected by Coraza with the JSON body processor enabled, which is the default for `application/json`) can crash the entire host process with a single request, using a payload well within default and recommended body size and argument-count limits. There is no privilege or interaction requirement, and the crash cannot be caught or mitigated by the integrator (no `recover()` stops a stack-overflow fatal error). This is strictly worse than a CPU-exhaustion or slow-request DoS: the process must be restarted, and every in-flight request/transaction on that process is lost. ### Suggested fix Run an iterative, explicitly-bounded-depth pre-scan (or reuse `readItems`'s own recursion accounting) before calling `gjson.Valid`, and never call `gjson.Valid` on input whose nesting exceeds `maxRecursion`. The response path (`ProcessResponse`) needs the same treatment since it shares `readJSON`. ### AI involvement disclosure - **AI tools/models used:** Claude Sonnet 5 (Anthropic), via Claude Code. - **What was generated/assisted:** the initial vulnerability hypothesis and repro shape were supplied by the reporter as an existing written finding; Claude Sonnet 5 independently re-derived the root cause by reading the current source, wrote and ran a fresh PoC test against commit `19b86824` (tag `v3.8.0`), confirmed the crash and stack trace shown above, verified the default configuration values cited (`ArgumentLimit` default, `SecRequestBodyLimit` recommended value) against the current source, and drafted this advisory text. - **Review performed:** reproduced by hand by running the PoC test above with `go test -run TestStackOverflowRepro ./internal/bodyprocessors/ -v` against a clean c…

Check your whole app

Dependencies are only half of it. Free and read-only: before you push, run npx shippable-dev scan localhost:3000 in your project folder, no account needed. For your live app, you prove it's yours with a one-line tag first.

References

Merged from GitHub Advisory Database, OSV, NVD. Last updated 24 Oct 2026. All vulnerabilities · RSS · JSON