You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Reviewed RFC 7230 §3.3.3 which mandates rejection of messages with multiple differing Content-Length values
Description
The parse_body_length() function in crates/openshell-sandbox/src/l7/rest.rs silently accepts HTTP requests with multiple Content-Length headers, using only the last value encountered. Per RFC 7230 Section 3.3.3:
"If a message is received without Transfer-Encoding and with either multiple Content-Length header fields having differing field-values or a single Content-Length header field having an invalid value, then the message framing is invalid and the recipient MUST treat it as an unrecoverable error."
This creates a request smuggling vector: if the L7 proxy and the upstream server disagree on which Content-Length value to use, an attacker can desynchronize request boundaries and smuggle a second request past policy evaluation.
Additionally, the Transfer-Encoding detection on line 245 uses .contains("chunked") which is insufficient — it doesn't reject multiple Transfer-Encoding headers or validate that the value is well-formed per RFC 7230.
fnparse_body_length(headers:&str) -> Result<BodyLength>{letmut has_te_chunked = false;letmut cl_value:Option<u64> = None;for line in headers.lines().skip(1){let lower = line.to_ascii_lowercase();if lower.starts_with("transfer-encoding:"){let val = lower.split_once(':').map_or("", |(_, v)| v.trim());if val.contains("chunked"){// LINE 245: substring match, not exact
has_te_chunked = true;}}if lower.starts_with("content-length:")
&& letSome(val) = lower.split_once(':').map(|(_, v)| v.trim())
&& letOk(len) = val.parse::<u64>(){
cl_value = Some(len);// LINE 253: OVERWRITES previous value}}// ...}
Send a request with duplicate Content-Length headers through the proxy:
POST /api HTTP/1.1
Host: target.example.com
Content-Length: 50
Content-Length: 10
<10 bytes of body><smuggled request as remaining 40 bytes>
The proxy interprets this as 10 bytes (last CL value), forwards 10 bytes, then treats the remaining 40 bytes as the start of a new request — which may bypass L7 policy evaluation if the smuggled request targets a different path.
Environment
Code review of main branch (commit HEAD as of 2026-03-26)
Agent Diagnostic
crates/openshell-sandbox/src/l7/rest.rsparse_body_length()(lines 237-270) iterates through headers and overwritescl_valueeach time aContent-Lengthheader is encounteredreject_dual_content_length_and_transfer_encodingonly covers CL+TE, not CL+CLDescription
The
parse_body_length()function incrates/openshell-sandbox/src/l7/rest.rssilently accepts HTTP requests with multipleContent-Lengthheaders, using only the last value encountered. Per RFC 7230 Section 3.3.3:This creates a request smuggling vector: if the L7 proxy and the upstream server disagree on which Content-Length value to use, an attacker can desynchronize request boundaries and smuggle a second request past policy evaluation.
Additionally, the Transfer-Encoding detection on line 245 uses
.contains("chunked")which is insufficient — it doesn't reject multipleTransfer-Encodingheaders or validate that the value is well-formed per RFC 7230.Reproduction Steps
crates/openshell-sandbox/src/l7/rest.rslines 237-270:Environment
mainbranch (commit HEAD as of 2026-03-26)crates/openshell-sandbox/src/l7/rest.rslines 237-270Logs
Missing test coverage — no test for multiple CL:
Suggested fix:
Agent-First Checklist
debug-openshell-cluster,debug-inference,openshell-cli)