Skip to content

fix(egress): allow single-layer %2f in raw request path (npm scoped packages)#1369

Open
Pangjiping wants to merge 1 commit into
mainfrom
fix/egress-mitmscripts-npm-scoped-slash
Open

fix(egress): allow single-layer %2f in raw request path (npm scoped packages)#1369
Pangjiping wants to merge 1 commit into
mainfrom
fix/egress-mitmscripts-npm-scoped-slash

Conversation

@Pangjiping

@Pangjiping Pangjiping commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

_path_is_ambiguous() in the egress credential proxy rejected every request whose raw path contained %2f. npm scoped packages are required by the registry protocol to send /@scope%2fname on the wire, so with Credential Vault active, npm install @scope/pkg in sandboxes was returning 403 (reported in Aone workitem 84550489 for @ali/orion-claude-plugin).

Split the two concerns:

  • Raw-request check (request()) — tolerate a single-layer %2f (allow_single_encoded_slash=True). Nested encodings (%252f, %252e%252e), raw backslash \, encoded backslash %5c, and dot-segments still trip the guard.
  • New _path_encoded_slash_changes_binding() — re-runs binding path matching against unquote(raw_path). If the encoded slash would move the request across a credential binding boundary, the request is still rejected with 403. Legit /@scope%2fname decodes into the same binding → allowed; a crafted /api/v8/projects/123%2f..%2f456/... decodes into a different scope → rejected (also caught by the pre-existing dot-segment check).
  • Post-substitution check (_apply_path_query_substitutions()) — keeps the strict semantics (allow_single_encoded_slash=False), because a %2f injected by the credential proxy itself always shifts the canonical path relative to what the client sent.

Also wire mitmscripts unit tests into egress-test.yml — the tests already existed but weren't being run in CI, so this class of regression had no signal.

Testing

  • Unit tests — python -m unittest discover -s components/egress/tests34 passed (28 existing + 6 new)

New tests cover:

  • npm scoped registry path /@scope%2fname (lowercase and uppercase %2F) receives credential injection.

  • Encoded %2f that stays inside a single binding scope is allowed.

  • Encoded %2f that spans two bindings (broad vs. narrow) is rejected with 403.

  • Double-encoded %252f and encoded backslash %5c remain rejected.

  • Integration tests

  • e2e / manual verification

Breaking Changes

  • None

The credential-proxy 403 semantics only change for a single-layer %2f that does not cross a binding boundary. That path previously always returned 403 and now succeeds — this is the intended fix. All other rejection paths (.., %252f, %5c, \, %2f crossing bindings, substitution introducing %2f) still return 403.

Checklist

  • Linked Issue or clearly described motivation
  • Added/updated docs (if needed) — behavior change is internal to credential proxy; no user-visible doc surface
  • Added/updated tests (if needed)
  • Security impact considered — see the "Split the two concerns" section above; the binding-consistency guard preserves the original threat model
  • Backward compatibility considered

@github-actions github-actions Bot added component/egress size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Jul 22, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4765a68b02

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread components/egress/mitmscripts/system.py
@Pangjiping
Pangjiping force-pushed the fix/egress-mitmscripts-npm-scoped-slash branch from 4765a68 to ed3a846 Compare July 22, 2026 04:12
@Pangjiping
Pangjiping force-pushed the fix/egress-mitmscripts-npm-scoped-slash branch from ed3a846 to c5893bf Compare July 22, 2026 07:25

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c5893bf02f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread components/egress/mitmscripts/system.py
_path_is_ambiguous() rejected every request whose raw path contained a
percent-encoded slash. npm scoped package registry paths are required to
be encoded as /@scope%2fname on the wire (npm registry protocol), so
credential injection for those requests started returning 403 as soon as
the Credential Vault was active — breaking 'npm install @scope/pkg' in
sandboxes.

Split the two concerns:

  * At the raw-request stage in request(), tolerate a single-layer %2f
    (allow_single_encoded_slash=True). Nested encodings (%252f etc.),
    raw backslash, encoded backslash and dot-segments still trip the
    guard.
  * A new _path_encoded_slash_changes_binding() re-runs binding path
    matching against unquote(raw_path). If the encoded slash would move
    the request across a credential binding boundary, the request is
    still rejected with 403.
  * The post-substitution check in _apply_path_query_substitutions()
    keeps the strict semantics (allow_single_encoded_slash=False), so
    substitutions that inject a %2f into the path are still refused.

Also wire mitmscripts unit tests into egress-test.yml so this regression
is caught in CI, and add a Python E2E case that drives the same wire
format through the real sidecar + credential vault + curl inside a
sandbox against the echo target.

Fixes: https://project.aone.alibaba-inc.com/v2/project/2135082/workitem/84550489
@Pangjiping
Pangjiping force-pushed the fix/egress-mitmscripts-npm-scoped-slash branch from c5893bf to e5323ce Compare July 22, 2026 07:37
@Pangjiping Pangjiping added the bug Something isn't working label Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working component/egress size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants