-
Notifications
You must be signed in to change notification settings - Fork 279
Description
Problem Statement
The L7 proxy's SecretResolver only rewrites openshell:resolve:env:* placeholders in HTTP headers. Some API clients (notably the Slack Node SDK) send auth tokens in the POST body (e.g., token=xoxb-... as form-encoded data). When the sandbox child process uses a generic provider to obfuscate secrets, the placeholder string leaks verbatim to the upstream API, causing invalid_auth errors.
This was discovered with OpenClaw (Slack bot) crashing on startup with An API error occurred: invalid_auth from POST /api/auth.test to slack.com:443, despite having L7 REST with TLS terminate active and the proxy correctly intercepting requests.
Proposed Design
Extend the existing SecretResolver to also scan and rewrite request bodies when:
- A resolver is present (providers are configured)
- The body size is within a safety limit (1 MiB) to prevent unbounded memory use
- The body is valid UTF-8 (binary bodies are passed through)
Changes
crates/openshell-sandbox/src/secrets.rs
- Add
MAX_REWRITE_BODY_BYTESconstant (1 MiB) - Add
SecretResolver::rewrite_body(&self, body: &[u8]) -> Option<Vec<u8>>— scans body bytes for placeholder substrings and replaces them with real secrets. ReturnsNoneif no placeholders found (zero-copy fast path). - Add
rewrite_content_length(headers: &[u8], new_length: u64) -> Vec<u8>— updates Content-Length header when body size changes after rewriting.
crates/openshell-sandbox/src/l7/rest.rs
- Modify
relay_http_request_with_resolver()to buffer the body (for Content-Length <= 1 MiB), callrewrite_body(), update Content-Length if the body size changed, then forward. Bodies exceeding the limit or chunked bodies that exceed the limit during buffering fall back to the existing streaming behavior.
crates/openshell-sandbox/src/proxy.rs
- Modify
rewrite_forward_request()to apply body rewriting to overflow body bytes in the forward proxy path.
Edge cases
- Bodies > 1 MiB: streamed without rewriting (existing behavior)
- Non-UTF-8 bodies: passed through unmodified
- No placeholders in body: no allocation, unchanged
- Content-Length adjustment: header updated to match rewritten body size
- Chunked transfer: de-chunk, rewrite, send as Content-Length (within size limit)
Alternatives Considered
- Client-side fix: Configure the Slack SDK to send tokens via
Authorizationheader instead of body. This works for Slack specifically but doesn't solve the general problem for other APIs that embed tokens in request bodies. - Body rewriting with no size limit: Rejected due to memory exhaustion risk on large file uploads.
Agent Investigation
Root cause traced through the codebase:
SecretResolver::rewrite_header_value()atcrates/openshell-sandbox/src/secrets.rs:37-48handles only header values (exact match or<scheme> <placeholder>pattern).rewrite_http_header_block()atsecrets.rs:55-86iterates header lines only; line 84 passes the body through as-is.relay_http_request_with_resolver()atcrates/openshell-sandbox/src/l7/rest.rs:129-172streams the body directly viarelay_fixed/relay_chunkedwithout inspection.- The existing test
relay_request_without_resolver_leaks_placeholdersatrest.rs:1096-1170explicitly documents that placeholders leak when no resolver is present, but there is no corresponding test for body content.