Skip to content

feat(sandbox): extend L7 credential injection — query params, Basic auth, URL paths #689

@johntmyers

Description

@johntmyers

Problem Statement

The L7 proxy's SecretResolver injects credentials into HTTP headers but cannot rewrite query parameters, Basic auth tokens, or URL paths. Three concrete use cases are blocked:

  1. Query parameter APIs (YouTube Data API, Google APIs) — authenticate via ?key=VALUE in the URL
  2. Basic auth APIs (container registries, legacy REST) — require base64-encoded username:password in the Authorization: Basic header, with the credential embedded inside the encoded token
  3. URL path APIs (Telegram Bot API) — embed the token in the URL path (/bot{TOKEN}/sendMessage)

NemoClaw is actively migrating service credentials to openshell provider create. Discord and Slack work today (header-based). Telegram is the first concrete blocker — the token is in the path, not a header. Query param and Basic auth gaps also block Google API and registry integrations.

Related

What Exists Today

SecretResolver (crates/openshell-sandbox/src/secrets.rs) replaces openshell:resolve:env:{KEY} placeholders in HTTP header values. The child process gets placeholder tokens in its env instead of real secrets. When the proxy intercepts an outbound request, rewrite_http_header_block iterates header lines and substitutes real values. The request line and body are passed through untouched.

Key Code Locations

Location What It Does
crates/openshell-sandbox/src/secrets.rs:8-48 SecretResolver — placeholder map, resolve_placeholder(), rewrite_header_value()
crates/openshell-sandbox/src/secrets.rs:55-97 rewrite_http_header_block / rewrite_header_line — per-line header rewriting
crates/openshell-sandbox/src/l7/rest.rs:~152-196 relay_http_request_with_resolver — calls rewrite before upstream write
crates/openshell-sandbox/src/l7/relay.rs:~108-111 L7RequestInforeq.target passed to OPA for path-based L7 policy eval
crates/openshell-sandbox/src/proxy.rs:~1594-1681 Forward proxy rewrite_forward_request — another rewrite call site

Recommendations

Tier 1: Do Now (no schema changes, follows existing patterns)

These extend the implicit placeholder resolution model already used for headers. The child process places openshell:resolve:env:* in the natural location, the proxy rewrites it. No policy YAML or proto changes needed.

1a. Query parameter rewriting
Resolve placeholders in URL query param values. PR #631 has a working implementation: rewrite_request_line parses METHOD URI HTTP/x.x, splits the URI at ?, iterates key=value pairs, resolves placeholder values, and percent-encodes per RFC 3986. Absorb this work.

1b. Basic auth decode/resolve/re-encode
When Authorization: Basic <base64> contains a placeholder in the decoded user:password string, decode → resolve → re-encode. PR #631 has a working implementation via rewrite_basic_auth_token. Absorb this work.

1c. URL path placeholder resolution
Extend rewrite_request_line (from 1a) to also scan the path component for openshell:resolve:env:* and resolve. This is the Telegram blocker. The insertion point is clear — rewrite_request_line already parses the URI; add a rewrite_uri_path step before the query param step.

Security hardening specific to path rewriting:

  • Fail-closed: if a path placeholder can't be resolved, reject the request (don't forward the raw placeholder — it leaks into server logs and error pages, unlike header placeholders which just cause a 401)
  • Sanitize resolved values: reject or escape credential values containing ../, null bytes, or query delimiters (?, #) to prevent path traversal
  • Percent-encode resolved values per RFC 3986 path segment rules (different character set than query params)

Tier 2: Defer (requires architectural changes)

These patterns either lack immediate demand signal or require changes to the proxy's streaming relay model.

Request body rewriting — Some webhook APIs put credentials in JSON bodies. This requires buffering the entire body (today it streams via relay_fixed/relay_chunked), rewriting, recalculating Content-Length, and re-chunking. Fundamentally changes the relay model. No current demand signal.

Cookie injectionCookie headers have key=value; key2=value2 structure that rewrite_header_value doesn't parse. Low demand. Could be added later without architectural changes if needed.

Explicit credential_injection policy config — Adding a credential_injection block to NetworkEndpoint in proto/YAML (as proposed in #538/#541). This was rejected for being too broad. May be worth revisiting if/when body rewriting or more complex injection patterns are needed, but the implicit placeholder model covers Tier 1 cleanly.

Composite auth schemes — HMAC signing, AWS Signature V4, OAuth token refresh. These require stateful computation, not just string substitution. Out of scope for the placeholder model entirely.

Open Questions

  1. OPA evaluation order for path rewriting — L7 Rego evaluates request.path as-is from the client. If the path contains a placeholder, OPA sees /botopenshell:resolve:env:TOKEN/sendMessage. Should path rewriting happen before or after OPA eval? Before means OPA sees the real secret (logging risk). After means L7 rules must match placeholder patterns. Recommend: rewrite before OPA, but strip the resolved token from log output.

  2. Fail-closed consistency — Header placeholder leakage fails open today (placeholder forwarded, upstream returns 401). Path rewriting should fail closed. Should we also make header/query rewriting fail-closed for consistency, or accept the divergence?

  3. PR feat(sandbox): L7 credential injection — query param rewriting and Basic auth encoding #631 disposition — The implementation in PR feat(sandbox): L7 credential injection — query param rewriting and Basic auth encoding #631 covers 1a and 1b. Options: (a) absorb the diff into a new branch with 1c added, (b) merge feat(sandbox): L7 credential injection — query param rewriting and Basic auth encoding #631 as-is and follow up with 1c. Recommend (a) for a single coherent PR.

Scope Assessment

  • Complexity: Low-Medium (Tier 1 only)
  • Confidence: High — clear insertion points, follows existing patterns
  • Estimated files to change: 3-4 (secrets.rs, rest.rs, Cargo.toml, tests)
  • Issue type: feat

Test Considerations

  • Unit tests for path placeholder detection, resolution, and percent-encoding (RFC 3986 path segment rules)
  • Security tests: path traversal via ../, null bytes, query delimiters in credential values
  • Fail-closed test: unresolvable path placeholder → request rejected
  • Integration test with mock Telegram-style /bot{TOKEN}/method endpoint
  • Absorb PR feat(sandbox): L7 credential injection — query param rewriting and Basic auth encoding #631's 11 existing tests for query param and Basic auth
  • Regression: existing header rewriting still works

Created by spike investigation. Absorbs PR #631 scope. Use build-from-issue to implement.

Metadata

Metadata

Assignees

Labels

area:policyPolicy engine and policy lifecycle workarea:sandboxSandbox runtime and isolation workarea:supervisorProxy and routing-path workspikestate:agent-readyApproved for agent implementationstate:in-progressWork is currently in progressstate:review-readyReady for human reviewtopic:l7Application-layer policy and inspection work

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions