🛡️ CVE-2026-50197 — skipper

🟠 CVSS 8.0 — High ✅ No Known Exploit CWE-444 NVD
8.0
CVSS Score
0 Low4 Medium7 High9 Critical10

Description

Skipper: opaAuthorizeRequestWithBody filter bypasses OPA policy on Transfer-Encoding — chunked / HTTP/2 requests

Summary

zalando/skipper's OpenPolicyAgent integration silently bypasses request-body

inspection on HTTP/1.1 Transfer-Encoding: chunked and HTTP/2 requests that

omit the content-length pseudo-header. When the

opaAuthorizeRequestWithBody filter is configured, the

OpenPolicyAgentInstance.ExtractHttpBodyOptionally helper produces an

empty raw_body for any request whose Content-Length header is missing,

while the underlying chunked body still flows through to the upstream

service. Rego policies that gate on input.parsed_body (e.g. "deny when a

forbidden field is present") evaluate against an empty document, treat the

forbidden field as absent, and authorize the request. The upstream handler

then receives the full attacker payload that the policy intended to block.

Affected versions

github.com/zalando/skipper versions <= v0.26.8 (the latest release on

2026-05-26, current master 4eed47ff). The vulnerable helper and gate

have lived in filters/openpolicyagent/openpolicyagent.go since the

buffered-body extractor was introduced; no released version contains the

fix at the time of filing.

Privilege required

Unauthenticated network access to the skipper proxy listener. The threat

model targets operators who place skipper in front of a private upstream

and rely on opaAuthorizeRequestWithBody to enforce body-content checks

(field allow/deny lists, payload schema gates, content-moderation flags,

multi-tenant per-action authorization). Both HTTP/1.1 and HTTP/2 clients

are affected; HTTP/2 traffic without a content-length pseudo-header is

the dominant case because Go's net/http sets

http.Request.ContentLength = -1 for chunked HTTP/1.1 AND for HTTP/2

requests whose framing carries the body as DATA frames without an explicit

length header.

Root cause

filters/openpolicyagent/openpolicyagent.go:1242-1269 (HEAD 4eed47ff):

```go

func bodyUpperBound(contentLength, maxBodyBytes int64) int64 {

if contentLength <= 0 {

return maxBodyBytes

}

if contentLength < maxBodyBytes {

return contentLength

}

return maxBodyBytes

}

func (opa *OpenPolicyAgentInstance) ExtractHttpBodyOptionally(req *http.Request) (io.ReadCloser, []byte, func(), error) {

body := req.Body

if body != nil && !opa.EnvoyPluginConfig().SkipRequestBodyParse &&

req.ContentLength <= int64(opa.maxBodyBytes) {

wrapper := newBufferedBodyReader(req.Body, opa.maxBodyBytes, opa.bodyReadBufferSize)

requestedBodyBytes := bodyUpperBound(req.ContentLength, opa.maxBodyBytes)

if !opa.registry.maxMemoryBodyParsingSem.TryAcquire(requestedBodyBytes) {

return req.Body, nil, func() {}, ErrTotalBodyBytesExceeded

}

rawBody, err := wrapper.fillBuffer(req.ContentLength)

return wrapper, rawBody, func() { opa.registry.maxMemoryBodyParsingSem.Release(requestedBodyBytes) }, err

}

return req.Body, nil, func() {}, nil

}

```

filters/openpolicyagent/openpolicyagent.go:1195-1210:

```go

func (m *bufferedBodyReader) fillBuffer(expectedSize int64) ([]byte, error) {

var err error

for err == nil && int64(m.bodyBuffer.Len()) < m.maxBufferSize && int64(m.bodyBuffer.Len()) < expectedSize {

var n int

n, err = m.input.Read(m.readBuffer)

m.bodyBuffer.Write(m.readBuffer[:n])

}

if err == io.EOF { err = nil }

return m.bodyBuffer.Bytes(), err

}

```

When the client sends Transfer-Encoding: chunked (HTTP/1.1) or an

HTTP/2 request without content-length, Go's net/http server sets

req.ContentLength = -1. The gate at line 1258 (`req.ContentLength <=

int64(opa.maxBodyBytes)) is true (-1 <= positiveLimit`), so the body

gets wrapped in bufferedBodyReader. bodyUpperBound(-1, max) returns

max, so the memory semaphore is acquired, but fillBuffer(-1) then

evaluates int64(m.bodyBuffer.Len()) < expectedSize as 0 < -1, which

is false on the first iteration. The loop never enters, the buffer stays

empty, and the helper returns []byte{} as rawBody to the caller.

The caller in

filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest.go:121

hands this empty slice to envoy.AdaptToExtAuthRequest which puts it

into AttributeContext.Request.Http.RawBody. The OPA SDK then exposes

the empty buffer as both input.attributes.request.http.raw_body and

the parsed input.parsed_body document (the latter becomes an

empty/undefined value). Any Rego rule that asserts the presence of a

forbidden field in input.parsed_body evaluates to undefined and fails

into the rule's default (typically allow).

Meanwhile, the wrapped body returned to the filter (req.Body = body

at line 127 of opaauthorizerequest.go) is a bufferedBodyReader whose

Read() falls through to the underlying m.input.Read(p) when the

buffer is empty (lines 1212-1228). The upstream handler therefore

How this vulnerability can be exploited

This issue can be reached over the network, attack complexity is low, an attacker needs no privileges on the target. No user interaction is required. The scope is changed, meaning a successful attack can affect components beyond the vulnerable one. Rated impact: confidentiality high, integrity high, availability none.

Weakness class

CVE-2026-50197 is classified as CWE-444: HTTP Request Smuggling. A proxy and a server disagree on where one request ends, letting an attacker slip a second request past the front end.

Affected software

CVE-2026-50197 is recorded against 2 packages.

  • github.com/zalando/skipper
  • unknown

Timeline and source

Published on 8 July 2026 and last revised on 21 July 2026. No public exploit is currently recorded for this entry. Record sourced from NVD.

References

github.com (Web)
github.com (Package)

Details

Severity HIGH
CVSS Score 8.0
CVSS Vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N/E:U/RL:O
CWE CWE-444
Public Exploit ✅ No
Source NVD
Published 2026-07-08
Updated 2026-08-12
Modified 2026-07-21
Fix URL N/A

Affected Packages

Software From version Fixed in
github.com/zalando/skipper
unknown

Similar Threats

Site Security Check

Is skipper part of your stack?

CVE-2026-50197 is rated CVSS 8.0 High. BotEraser scans your installation against known CVE records and tells you whether this vulnerability applies to the versions you actually run.

Scan My Site Free →

No credit card required  ·  Results in minutes

ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.