high · 8.7CVE-2026-50197Jul 8, 2026

CVE-2026-50197: Skipper opaAuthorizeRequestWithBody OPA Policy Bypass via Chunked Encoding

Rohit Hatagale
AI Security Researcher, SecureLayer7

Skipper's OPA body-inspection filter silently skips reading the request body for chunked or HTTP/2 requests, letting attackers smuggle forbidden payloads past policy checks that should have blocked…

Packagegithub.com/zalando/skipper
Ecosystemgo
Affected< 0.26.10
Fixed in0.26.10

The problem

The opaAuthorizeRequestWithBody filter in zalando/skipper uses ExtractHttpBodyOptionally to buffer the request body before passing it to Open Policy Agent. When a client sends Transfer-Encoding: chunked (HTTP/1.1) or an HTTP/2 request without a content-length pseudo-header, Go's net/http sets req.ContentLength = -1.

The gate condition req.ContentLength <= maxBodyBytes passes because -1 <= positive limit is true. But fillBuffer(-1) immediately exits its loop because 0 < -1 is false, returning an empty buffer. OPA evaluates an empty input.parsed_body, any deny-rule keyed on a body field fails open, and the full attacker payload flows through to the upstream service.

No unauthenticated access is required.

Proof of concept

A working proof-of-concept for CVE-2026-50197 in github.com/zalando/skipper, with the exact payload below.

http
POST /protected HTTP/1.1
Host: skipper-proxy:9090
Content-Type: application/json
Connection: close
Transfer-Encoding: chunked

e
{"admin":true}
0

The root cause is a sign error in fillBuffer. Passing expectedSize = -1 makes the loop predicate len(buf) < -1 false on the first iteration, so the read loop never executes and the returned buffer is empty. OPA receives raw_body = []byte{}, so input.parsed_body is empty or undefined, and any Rego rule asserting a forbidden field is absent evaluates to the default allow.

The patch (targeting v0.26.10) adds a pre-check: if req.ContentLength < 0, clamp expectedSize to opa.maxBodyBytes before passing it to fillBuffer. This makes chunked bodies read up to the configured cap, matching the documented behavior for Content-Length requests.

CWE-444 (Inconsistent Interpretation of HTTP Requests) applies because the proxy and the authorization layer disagree on what body the request carries.

The fix

Upgrade github.com/zalando/skipper to v0.26.10 or later. The fix clamps expectedSize to opa.maxBodyBytes whenever req.ContentLength < 0, ensuring chunked and HTTP/2 bodies are read before OPA evaluation. No configuration change is needed; the existing -open-policy-agent-max-request-body-size flag continues to bound memory use.

Reported by tonghuaroot.

References: [1][2]

Related research