# Skipper has OPA body-authz bypass: truncated_body mitigation fails open on chunked/HTTP-2 (incomplete fix GHSA-8qqm-fp2q-v734)

Skipper's OPA body-authz truncated_body mitigation fails open on chunked/HTTP-2 requests lacking Content-Length, letting oversized un-inspected bodies reach the upstream.

- Payout: Research
- Severity: high
- Program: Skipper
- Found by: Pig-Tail
- Disclosed: 2026-09-17
- CVE: CVE-2026-86043
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-5gpm-rgj3-9q76
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-5gpm-rgj3-9q76
- Tags: area:web domain:api domain:web impact:integrity kind:research platform:ghsa sev:high tech:go technique:incomplete-fix technique:source-review vuln:access-control vuln:auth-bypass

**Target:** Skipper opaAuthorizeRequestWithBody filter + OPA envoy plugin body parsing
**Root cause:** opa-envoy-plugin only computes truncated_body when a content-length header exists; chunked/HTTP-2 requests have none, so truncated_body stays false even though Skipper truncated the body to max-request-body-size.
**Steps:**
1. Deploy opaAuthorizeRequestWithBody with the advisory's mitigation policy `allow if { input.truncated_body == false }`.
2. Send an oversized body (> max-request-body-size) with `Transfer-Encoding: chunked` and no Content-Length, e.g. `a=` + 64*`A` with Content-Type `application/x-www-form-urlencoded`.
3. Skipper truncates to maxBodyBytes and forwards truncated RawBody plus headers (no content-length) to OPA.
4. getParsedBody finds no content-length, returns truncated_body=false; policy allows.
5. Skipper's bufferedBodyReader serves the buffered prefix then drains the original body, delivering the full oversized payload upstream.
**How found:** Audited the fix chain for CVE-2026-50197 and GHSA-8qqm-fp2q-v734, noticed the 8qqm positive-control test only exercised small chunked bodies, then read getParsedBody/checkIfHTTPBodyTruncated and saw truncation is only signalled via content-length comparison.
**Exploitation:** Unauthenticated attacker sends a chunked (or HTTP/2) request whose body exceeds the inspection cap; the truncated_body-gated policy authorizes it and the full payload reaches the protected upstream, bypassing body-based authorization.
**Insight:** When a mitigation depends on a client-supplied header (content-length) to detect a server-side condition (truncation), test the same request over alternate framings (chunked, HTTP/2) that omit that header; also check whether the fix's own tests only cover the small-body case.

## Proof of concept (copied exactly from the original report)

**Chunked request construction without Content-Length**

```
if chunked {
			// Force chunked framing: no Content-Length reaches the server,
			// so the server sees req.ContentLength == -1.
			req.ContentLength = -1
			req.TransferEncoding = []string{"chunked"}
		}
```

**Mitigation Rego policy used in PoC**

```
default allow := false

allow if {
    input.truncated_body == false
}
```

