User Story
As an operator running agents behind an L7 rest endpoint,
I want request paths that contain an encoded semicolon (%3B) to reach the upstream unchanged,
so that the agent acts on the resource it named and not on a different one.
Problem Statement
With the default strip_path_parameters: true, canonicalize_request_target percent-decodes %3B into a literal ; before splitting and stripping ;params. An encoded semicolon, which is data under RFC 3986, is then treated as a path-parameter delimiter: it and everything after it in that segment are dropped. Because the request line is rewritten with the canonical path (rewritten == true), the truncated path is what goes on the wire.
Observed with the canonicalizer from main (0ea0d31) built standalone:
| request-target |
canonical path sent upstream |
/a/%3Bx |
/a/ |
/objects/backup%3Bold |
/objects/backup |
/a/..;/b |
/b (intended: ..; traversal guard) |
Relevant code:
- decode:
- strip after decode:
|
segments.into_iter().map(strip_path_parameters).collect() |
strip_path_parameters:
|
fn strip_path_parameters(seg: &[u8]) -> &[u8] { |
The existing tests (/public/..%3B/secret → /secret) cover the dot-segment case only; there is no test asserting that %3B inside an ordinary segment survives.
Impact / Why This Matters
The agent's request is retargeted without any error or log entry:
DELETE /v1/objects/backup%3Bold is forwarded as DELETE /v1/objects/backup, which deletes a different object.
GET/PUT on resource names, S3-style keys, or IDs that contain ; read or overwrite the wrong resource.
- Nothing is surfaced to the agent or operator (no OCSF event, no 4xx), so the data loss is silent.
The only workaround today is setting strip_path_parameters: false on the endpoint, which also disables the intended ;jsessionid-class mitigation.
Acceptance Criteria
Reproduction Steps
- Create a sandbox with a
protocol: rest endpoint for a test upstream (e.g. an HTTP echo server) and an allow rule for /** with method *.
- From inside the sandbox:
curl -X DELETE "http://<upstream>/objects/backup%3Bold"
- Observe the upstream receives
DELETE /objects/backup.
Environment
- OpenShell:
main @ 0ea0d31 (code review; canonicalizer exercised standalone)
- Component:
openshell-supervisor-network L7 REST relay, default CanonicalizeOptions
- Any driver (the behavior is in the supervisor proxy)
Logs
User Story
As an operator running agents behind an L7
restendpoint,I want request paths that contain an encoded semicolon (
%3B) to reach the upstream unchanged,so that the agent acts on the resource it named and not on a different one.
Problem Statement
With the default
strip_path_parameters: true,canonicalize_request_targetpercent-decodes%3Binto a literal;before splitting and stripping;params. An encoded semicolon, which is data under RFC 3986, is then treated as a path-parameter delimiter: it and everything after it in that segment are dropped. Because the request line is rewritten with the canonical path (rewritten == true), the truncated path is what goes on the wire.Observed with the canonicalizer from
main(0ea0d31) built standalone:/a/%3Bx/a//objects/backup%3Bold/objects/backup/a/..;/b/b(intended:..;traversal guard)Relevant code:
OpenShell/crates/openshell-supervisor-network/src/l7/path.rs
Line 272 in 0ea0d31
OpenShell/crates/openshell-supervisor-network/src/l7/path.rs
Line 187 in 0ea0d31
strip_path_parameters:OpenShell/crates/openshell-supervisor-network/src/l7/path.rs
Line 284 in 0ea0d31
The existing tests (
/public/..%3B/secret→/secret) cover the dot-segment case only; there is no test asserting that%3Binside an ordinary segment survives.Impact / Why This Matters
The agent's request is retargeted without any error or log entry:
DELETE /v1/objects/backup%3Boldis forwarded asDELETE /v1/objects/backup, which deletes a different object.GET/PUTon resource names, S3-style keys, or IDs that contain;read or overwrite the wrong resource.The only workaround today is setting
strip_path_parameters: falseon the endpoint, which also disables the intended;jsessionid-class mitigation.Acceptance Criteria
%3Binside a non-dot segment is preserved as%3Bin both the policy input and the forwarded request line (e.g./a/b%3Bc→/a/b%3Bc)...%3B/%2e%2e%3Btraversal is still resolved or rejected as today.%3Bin ordinary segments, including the trailing-segment case.Reproduction Steps
protocol: restendpoint for a test upstream (e.g. an HTTP echo server) and an allow rule for/**with method*.curl -X DELETE "http://<upstream>/objects/backup%3Bold"DELETE /objects/backup.Environment
main@ 0ea0d31 (code review; canonicalizer exercised standalone)openshell-supervisor-networkL7 REST relay, defaultCanonicalizeOptionsLogs