Jul 17, 2026·6 min read·52 visits
Oversized HTTP request bodies skip Skipper's OPA inspection filter but are still forwarded intact to backend microservices, causing deny-on-presence authorization checks to fail open.
An authorization bypass vulnerability in the Open Policy Agent (OPA) integration of the Skipper HTTP router allows unauthenticated remote attackers to bypass OPA policy inspection. When an incoming HTTP request declares a Content-Length exceeding Skipper's configured maxBodyBytes limit, Skipper bypasses body parsing and forwards an empty document to OPA, while transmitting the full, uninspected payload intact to the upstream backend.
Skipper is a high-performance HTTP router and reverse proxy commonly deployed as a Kubernetes Ingress controller or standalone API gateway. Within its security features, Skipper provides an opaAuthorizeRequestWithBody filter that extracts incoming HTTP request bodies and forwards them to an Open Policy Agent (OPA) decision engine to enforce content-based security policies.
These policies often inspect incoming payloads to block restricted parameters, privilege-escalation fields, or unauthorized administrative actions. Under normal operating conditions, requests with bodies are fully buffered and decoded into JSON structures before evaluation.
However, GHSA-8QQM-FP2Q-V734 represents an incomplete fix for a security bypass flaw. When a request declares a Content-Length larger than the configured maximum limit (maxBodyBytes), the proxy completely skips body inspection but continues to route the entire request downstream. This structural discrepancy allows an attacker to bypass critical security controls entirely.
To understand the root cause of GHSA-8QQM-FP2Q-V734, it is necessary to examine the patch introduced for CVE-2026-50197 in commit 3152f3b0bb52ca89c3564be42434db0a2a1cea23. The initial vulnerability occurred because chunked encoding set req.ContentLength to -1, which bypassed the buffer initialization in fillBuffer and returned an empty body to OPA.
The developers corrected this behavior by introducing an expectedSize variable that normalized negative lengths to the configured maximum body limit. However, they failed to account for situations where expectedSize exceeds the configured buffer limit (maxBodyBytes).
When a request has a positive Content-Length larger than maxBodyBytes, the conditional gate expectedSize <= int64(opa.maxBodyBytes) evaluates to false. Skipper then drops execution down to the end of the ExtractHttpBodyOptionally helper, returning a nil raw body slice to the authorization filter. Consequently, OPA receives an empty document and defaults to allowing the transaction, while Skipper forwards the original unconsumed HTTP request body directly to the upstream server.
The vulnerability lives within the extraction routine inside filters/openpolicyagent/openpolicyagent.go. The logic block before and after the original fix illustrates the incomplete validation path.
// Vulnerable logic flow prior to fix
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)
// ...
rawBody, err := wrapper.fillBuffer(req.ContentLength)
return wrapper, rawBody, func() {}, err
}
return req.Body, nil, func() {}, nil
}The patch modified the function to explicitly normalize expectedSize as follows:
func (opa *OpenPolicyAgentInstance) ExtractHttpBodyOptionally(req *http.Request) (io.ReadCloser, []byte, func(), error) {
body := req.Body
+ expectedSize := req.ContentLength
+ if expectedSize < 0 {
+ expectedSize = opa.maxBodyBytes
+ }
+
if body != nil && !opa.EnvoyPluginConfig().SkipRequestBodyParse &&
- req.ContentLength <= int64(opa.maxBodyBytes) {
+ expectedSize <= int64(opa.maxBodyBytes) {
wrapper := newBufferedBodyReader(req.Body, opa.maxBodyBytes, opa.bodyReadBufferSize)
- requestedBodyBytes := bodyUpperBound(req.ContentLength, opa.maxBodyBytes)
+ requestedBodyBytes := bodyUpperBound(expectedSize, opa.maxBodyBytes)
if !opa.registry.maxMemoryBodyParsingSem.TryAcquire(requestedBodyBytes) {
return req.Body, nil, func() {}, ErrTotalBodyBytesExceeded
}
- rawBody, err := wrapper.fillBuffer(req.ContentLength)
+ rawBody, err := wrapper.fillBuffer(expectedSize)
return wrapper, rawBody, func() { opa.registry.maxMemoryBodyParsingSem.Release(requestedBodyBytes) }, err
}
return req.Body, nil, func() {}, nil
}While normalizing negative values prevents the chunked transport bypass, it introduces a strict path where an explicitly oversized header skips the entire block. Because the check fails, the execution falls through to return req.Body, nil, func() {}, nil. The proxy returns the uninspected request body to the transport layer, but hands a nil raw slice to OPA, leading to a silent security failure.
To exploit this authorization bypass, an attacker must craft an HTTP request that intentionally exceeds Skipper's configured maxBodyBytes limit. This limit is often configured to a standard value (such as 1 MB) or can be determined experimentally.
First, the attacker identifies a destination API route protected by an OPA blocklist policy. A common scenario is a Rego rule that blocks the value "action": "delete" in the request body. If the policy is written as allow-by-default, any evaluation where input.parsed_body is empty will bypass the restriction.
Next, the attacker constructs a payload that performs the forbidden action and appends high-volume junk characters or whitespace as padding until the raw request size exceeds the limit. The attacker transmits this request with a precise Content-Length header matching the padded payload size.
POST /admin HTTP/1.1
Host: target-gateway.local
Content-Type: application/json
Content-Length: 2000000
Connection: close
{"action":"delete","padding":"AAAAAA..."}Because the request size exceeds the configured maximum, Skipper skips body buffering, passes a nil body to OPA, and OPA evaluates the request as safe. The proxy then forwards the full, malicious payload to the upstream backend service, where the operation executes successfully.
This vulnerability has high severity because it enables complete authorization bypass on any endpoints relying on request-body policies. Organizations using Skipper as an ingress router to enforce compliance, API access boundaries, or data sanitization are vulnerable to remote, unauthenticated privilege escalation.
If the OPA configuration employs a blocklist (or a deny-on-presence design), any request that exceeds the buffer size limit will fail-open. Because of this, untrusted users can perform administrative actions, modify configurations, or inject malicious payloads into backend microservices.
Furthermore, the physical routing behavior remains completely unaffected by this condition. The upstream backend processes the fully formed, malicious body because Skipper forwards the untouched, original body stream (req.Body). This results in a classic request-interpretation mismatch between the proxy gateway and the internal backend services.
The primary remediation is upgrading to Skipper version 0.27.26 or later, which correctly identifies and communicates the body truncation state. To ensure complete protection, administrators must also update their OPA policy rules to fail closed when request bodies are omitted.
Security teams must modify existing Rego policies to check the input.attributes.request.http.truncated_body flag. If this property is set to true, the policy must explicitly deny the request to prevent uninspected payloads from reaching the backend.
package envoy.authz
import rego.v1
default allow := false
# Fail closed if the body has been truncated or omitted
allow := false if {
input.attributes.request.http.truncated_body == true
}
# Standard evaluation logic
allow := true if {
not input.attributes.request.http.truncated_body
input.parsed_body.action != "delete"
}If upgrading is not immediately possible, temporary workarounds include enforcing payload limits at the web application firewall (WAF) layer or reducing the backend's accepted content size to match Skipper's maxBodyBytes setting.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N| Attribute | Detail |
|---|---|
| CWE ID | CWE-863 |
| Attack Vector | Network |
| CVSS Score | 8.2 |
| Exploit Status | PoC Available |
| Affected Component | filters/openpolicyagent |
| Fixed Version | v0.27.26 |
The software performs authorization checks but makes the decision based on incomplete or manipulated input data, leading to an unauthorized action being executed downstream.
CVE-2026-10032 is a DOM-based Cross-Site Scripting (XSS) vulnerability in Google's @a2ui/web_core Node.js library. The vulnerability is located within the openUrl utility function, which processes and opens dynamic URLs defined in layout configurations. Because the function fails to sanitize or validate the target URL scheme before passing it to the window.open browser sink, an attacker can specify a javascript: pseudo-protocol to execute arbitrary client-side script in the context of the host origin.
CVE-2026-59944 is a path traversal and link-following vulnerability in Composer, the PHP dependency manager. This flaw allows malicious or compromised packages to bypass previous path-hardening protections and perform arbitrary filesystem operations outside of their designated installation directory, leading to unauthorized permission modifications or execution proxy creations.
A critical Broken Object Level Authorization (BOLA) vulnerability was identified in Trigger.dev before version v4.5.2. An authenticated attacker could trigger a run replay and supply an arbitrary target environmentId belonging to a completely different tenant. Because the server failed to validate whether the target environment belonged to the same project or organization as the source run, it would execute the task within the victim's environment, resulting in unauthorized cross-tenant write operations and remote task execution.
A critical Server-Side Request Forgery (SSRF) vulnerability in Trigger.dev prior to version 4.5.2 allows authenticated organization members to configure webhook alert channels with unvalidated target URLs. This can lead to internal network scanning and cloud metadata extraction.
A Server-Side Request Forgery (SSRF) vulnerability exists in the rmcp OAuth client, which is part of the Model Context Protocol (MCP) Rust SDK. The vulnerability arises from insecure processing of the resource_metadata parameter in WWW-Authenticate headers returned by a malicious or compromised MCP server. The client parses and fetches absolute URLs from this header without validation of scheme, origin, or network routing, allowing remote attackers to initiate HTTP GET requests to local network interfaces, RFC 1918 private subnets, or cloud metadata endpoints.
A module allowlist bypass vulnerability (CVE-2026-92945 / GHSA-7q3f-wx44-378m) was identified in the vm2 sandboxing library prior to version 3.11.7. This flaw permits unauthenticated or untrusted code running within the sandbox environment to bypass explicit module restrictions. When the transitive resolution option is disabled, the system fails to validate file system path boundaries, allowing prefix-sharing sibling directories to be resolved and loaded, thereby escaping intended sandbox restrictions.