Skip to content

bug(network): L7 path canonicalization treats percent-encoded %3B as a path-parameter delimiter and silently truncates the forwarded path #3902

Description

@CloneOfAlex

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:

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

  • A %3B inside a non-dot segment is preserved as %3B in both the policy input and the forwarded request line (e.g. /a/b%3Bc → /a/b%3Bc).
  • ..%3B / %2e%2e%3B traversal is still resolved or rejected as today.
  • Alternatively, the request is rejected with a clear 4xx + OCSF event instead of being silently rewritten.
  • Unit tests cover %3B in ordinary segments, including the trailing-segment case.

Reproduction Steps

  1. 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 *.
  2. From inside the sandbox: curl -X DELETE "http://<upstream>/objects/backup%3Bold"
  3. 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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    state:triage-neededOpened without agent diagnostics and needs triage

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions