CVE-2026-65838 Overview
CVE-2026-65838 is an authorization bypass vulnerability in Skipper, an HTTP router and reverse proxy used for service composition. The flaw resides in the opaAuthorizeRequestWithBody filter within filters/openpolicyagent/openpolicyagent.go. An attacker can send a request whose body exceeds -open-policy-agent-max-request-body-size (default 1 MB) to bypass deny-on-presence Open Policy Agent (OPA) Rego policies. The ExtractHttpBodyOptionally function leaves OPA with an empty parsed_body while still forwarding the complete body upstream, causing policy checks to fail open. This is an incomplete remediation of CVE-2026-50197 and is fixed in Skipper version 0.27.35.
Critical Impact
Attackers can smuggle forbidden payloads past OPA body-content authorization by inflating the request beyond the configured maximum body size, reaching protected backend services.
Affected Products
- Zalando Skipper HTTP router prior to version 0.27.35
- Deployments using the opaAuthorizeRequestWithBody filter with body parsing enabled
- OPA Rego policies that evaluate input.parsed_body without also checking input.attributes.request.http.truncated_body
Discovery Timeline
- 2026-09-14 - CVE-2026-65838 published to the National Vulnerability Database
- 2026-09-16 - Last updated in NVD database
Technical Details for CVE-2026-65838
Vulnerability Analysis
Skipper integrates OPA to evaluate HTTP request attributes, including request bodies, against Rego policies. When bodyParsing is enabled in the opaAuthorizeRequestWithBody filter, Skipper calls ExtractHttpBodyOptionally to hand the body to OPA as input.parsed_body. The prior fix for CVE-2026-50197 addressed the chunked-body case but did not signal to policy consumers when a body was truncated because it exceeded the configured maximum. As a result, oversized requests reach OPA with an empty parsed_body while the full payload is still proxied to the upstream service. Policies that deny on the presence of specific fields therefore evaluate against no body content and return an allow decision.
Root Cause
The root cause is missing check for exceptional conditions [CWE-754]. ExtractHttpBodyOptionally returned only the raw bytes and a finalizer without communicating whether the body was truncated. Callers had no way to propagate a truncated_body signal into the OPA input, so policy authors could not reliably reject oversized requests. The patch changes the function signature to return an explicit bodyTruncated bool and threads that value through AdaptToExtAuthRequest into the Envoy ext_authz CheckRequest.
Attack Vector
Exploitation requires only network access to a Skipper route protected by an OPA body-content policy. An attacker constructs an HTTP request whose declared Content-Length exceeds -open-policy-agent-max-request-body-size (1 MB by default) and places the forbidden payload inside the body. Skipper truncates the body for OPA, OPA sees empty parsed_body, the policy fails open, and Skipper forwards the complete body to the protected backend. No authentication or user interaction is required.
// Patch: filters/openpolicyagent/internal/envoy/skipperadapter.go
-func AdaptToExtAuthRequest(req *http.Request, metadata *ext_authz_v3_core.Metadata, contextExtensions map[string]string, rawBody []byte) (*ext_authz_v3.CheckRequest, error) {
+func AdaptToExtAuthRequest(req *http.Request, metadata *ext_authz_v3_core.Metadata, contextExtensions map[string]string, rawBody []byte, bodyTruncated bool) (*ext_authz_v3.CheckRequest, error) {
if err := validateURLForInvalidUTF8(req.URL); err != nil {
return nil, fmt.Errorf("invalid url: %w", err)
}
// Patch: filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest.go
var rawBodyBytes []byte
+ var bodyTruncated bool
if f.bodyParsing {
- body, rawBodyBytes, finalizer, err = f.opa.ExtractHttpBodyOptionally(req)
+ body, rawBodyBytes, bodyTruncated, finalizer, err = f.opa.ExtractHttpBodyOptionally(req)
defer finalizer()
// Source: https://github.com/zalando/skipper/commit/2cfceabaa6ff0af65b312dcb9bcbe84691b9d507
Detection Methods for CVE-2026-65838
Indicators of Compromise
- Inbound HTTP requests with Content-Length values close to or exceeding the configured -open-policy-agent-max-request-body-size (default 1048576 bytes).
- OPA decision logs showing allow verdicts on routes protected by body-content policies where input.parsed_body is empty or null.
- Backend application logs recording payloads that OPA policies were expected to reject.
Detection Strategies
- Correlate Skipper access logs with OPA decision logs to identify requests where the proxied body size exceeds the OPA parsed_body size.
- Alert on any allowed request where the OPA input contains truncated_body: true after patching, or where parsed_body is empty on routes that require body inspection.
- Deploy a Rego rule that explicitly denies when input.attributes.request.http.truncated_body == true and monitor for triggered denials as evidence of probing.
Monitoring Recommendations
- Track the distribution of request body sizes per route and alert on statistical outliers above the OPA maximum.
- Ingest Skipper, OPA, and backend logs into a centralized analytics platform and pivot on request identifiers to reconstruct authorization decisions.
- Monitor Skipper version and configuration drift to ensure patched builds and appropriate -open-policy-agent-max-request-body-size values remain deployed.
How to Mitigate CVE-2026-65838
Immediate Actions Required
- Upgrade Skipper to version 0.27.35 or later, which threads the bodyTruncated signal into the OPA input.
- Audit all Rego policies attached to opaAuthorizeRequestWithBody filters and add explicit denial when input.attributes.request.http.truncated_body is true.
- Review historical logs for oversized requests to routes protected by body-content policies to identify potential prior abuse.
Patch Information
The fix is included in Skipper release v0.27.35. The remediation commit updates ExtractHttpBodyOptionally and AdaptToExtAuthRequest to return and propagate a bodyTruncated boolean, making truncation visible to OPA. See the GitHub Security Advisory GHSA-8qqm-fp2q-v734 and the patch commit for full details.
Workarounds
- Increase -open-policy-agent-max-request-body-size above the largest legitimate request body so no bodies are silently truncated, accepting the memory tradeoff.
- Enforce an upstream body-size limit at a load balancer or Web Application Firewall (WAF) that rejects any request larger than the OPA maximum before it reaches Skipper.
- Update Rego policies to default-deny when parsed_body is empty on routes where body inspection is required by policy.
# Example Rego guard against truncated bodies
# deny when OPA received a truncated body on a body-authorization route
deny[msg] {
input.attributes.request.http.truncated_body == true
msg := "request body exceeded OPA max size; rejecting"
}
# Skipper flag example: raise the body limit above the largest expected payload
skipper \
-open-policy-agent-max-request-body-size=8388608 \
-filters-file=/etc/skipper/filters.yaml
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.