# Skipper: Incomplete fix for CVE-2026-50197: an oversized body can bypass OPA deny-on-presence Rego policies

Skipper's OPA body filter returns an empty parsed_body for oversized declared Content-Length, so deny-on-presence Rego policies fail open while the payload reaches upstream.

- Payout: Research
- Severity: high
- Program: Skipper
- Found by: sec-reex, deepakravisankar
- Disclosed: 2026-07-17
- CVE: CVE-2026-65838
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-8qqm-fp2q-v734
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-8qqm-fp2q-v734
- 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:** OPA authorization filter (opaAuthorizeRequestWithBody) in Skipper proxy
**Root cause:** The CVE-2026-50197 fix only set expectedSize=maxBodyBytes when ContentLength < 0; a declared Content-Length larger than maxBodyBytes makes expectedSize > maxBodyBytes, skipping body extraction so ExtractHttpBodyOptionally returns nil rawBodyBytes and OPA evaluates an empty parsed_body.
**Steps:**
1. Deploy Skipper with `WithMaxRequestBodyBytes(32)` and route `* -> opaAuthorizeRequestWithBody("test") -> upstream`, policy `allow = false if input.parsed_body.action == "delete"`.
2. Send `{"action":"delete"}` (19B ≤ 32) → 403 denied.
3. Send `{"action":"delete","pad":"X..64"}` (> 32 bytes with declared Content-Length) → 200, upstream receives full body (bypass).
4. Positive control: small chunked `{"action":"delete"}` → 403, confirming original CVE fixed.
**How found:** Incomplete-patch measurement study: reading the CVE-2026-50197 fix commit 3152f3b0, noting its comment only reasons about ContentLength == -1 and the added PoC test only covers small bodies, then hypothesizing the oversized declared-Content-Length branch was never handled and confirming via Skipper's own unit test 'Read body exhausting max bytes'.
**Exploitation:** Attacker pads a forbidden request body past the configured max-request-body-size (default 1 MB) so OPA sees an empty parsed_body and deny-on-presence policies allow, while the full payload is forwarded to the upstream service.
**Insight:** When auditing a fix for a body-size/truncation bug, enumerate every branch of the size comparison (negative, equal, oversized) rather than the one the patch comment mentions; a filter that silently returns an empty document to a policy engine turns deny-on-presence rules into allow.

