Skip to content

fix(egress): Add sandbox credentials only for placeholder requests - #2191

Draft
sentry-junior[bot] wants to merge 1 commit into
mainfrom
fix/egress-placeholder-gated-credentials
Draft

sentry-junior[bot] wants to merge 1 commit into
mainfrom
fix/egress-placeholder-gated-credentials

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

PoC for #2190.

The sandbox egress proxy added plugin credentials to every request to a plugin domain. A browser in the sandbox could not log in to a provider site as another account. For example, qa-sentry-frontend on sentry.io could not keep its own login. Sentry's DEFAULT_AUTHENTICATION runs token auth before SessionAuthentication, so the injected Bearer token wins over the session cookie.

Now the sandbox proxy adds credentials only when an auth header carries the host_managed_credential placeholder. All other requests go upstream without credentials and keep their own headers and cookies. These requests skip grant selection and lease issuance, so a browser request no longer causes a junior-auth-required 401.

What each client sends (I checked each one against a local capture server or GH_DEBUG):

Client Placeholder header Change
sentry CLI Authorization: Bearer host_managed_credential none
vercel CLI Authorization: Bearer host_managed_credential none
gh Authorization: token ghp_host_managed_credential none (matches the marker in any scheme)
pup (Datadog) DD-API-KEY / DD-APPLICATION-KEY = placeholder none
git none (credential.helper= + http.emptyAuth) sends Basic x-access-token:ghp_host_managed_credential through http.https://github.com/.extraHeader (in sandboxPrepare and in workspace clone/fetch)
tts speech.mjs none sends Authorization: Bearer host_managed_credential
plugin tools (ctx.egress.fetch) none no change: this host path does not use the placeholder check

Google already rejects sandbox egress. MCP-only plugins declare no domains.

Changes

  • auth-token-placeholder.ts: adds the HOST_MANAGED_CREDENTIAL_MARKER constant and hasHostManagedCredentialPlaceholder(). The function checks every header except Cookie and decodes Basic auth.
  • sandbox/egress/proxy.ts: requests with the placeholder go to executeCredentialedEgressRequest. All other requests go to the new forwardEgressRequestWithoutCredentials.
  • egress/headers.ts: request and response header building moved out of credentialed.ts, so that file stays under 1,000 lines.
  • policies/security.md: documents the rule. It also says the check is not a security edge, because the placeholder is public.

Known gaps (PoC)

  • The check is not a security boundary. A hostile page in the browser can still set the placeholder on a CORS-allowed request, for example to api.github.com. That is still stricter than before. A follow-up could use a random placeholder per sandbox.
  • No manifest-load check that custom auth-token-placeholder values contain the marker. All placeholders in this repo do. App-local plugins (junior-prod) are not checked.
  • The tts skill copy in junior-prod (skills/tts/scripts/speech.mjs) needs the same one-line header change after a release.
  • Not tested end to end in a deployed Sandbox. A live check (git clone and push of a private repo, gh, sentry, pup, and a browser login on sentry.io) is needed before this leaves draft.

Checks run

  • vitest run tests/integration/sandbox-egress-proxy.test.ts tests/component/handlers/sandbox-egress-proxy.test.ts tests/unit/misc/noninteractive-command.test.ts: 61 passed. The new integration case fails when the placeholder check is removed.
  • packages/junior-github vitest: all passed.
  • packages/junior-tts node --test: 5 passed.
  • pnpm typecheck, pnpm lint (includes file-length): passed.
  • Note: event-automations / slack-schedule-tools timed out under a 35-file parallel run. They pass when run alone, and this PR does not change them.

via David Cramer.

--

View Junior Session [Sentry]

The sandbox egress proxy added plugin credentials to every request to a
plugin domain. A browser in the sandbox could not keep its own login on
provider sites such as sentry.io, because the injected token won over the
session cookie.

The proxy now adds credentials only when an auth header carries a
`host_managed_credential` placeholder, in any scheme (Bearer, token, API key
header, or Basic password). Other requests are forwarded without
credentials and keep their cookies. Plugin tool egress on the host is
unchanged.

Git and the tts script sent no auth header and relied on injection, so they
now send the placeholder.

Refs #2190

Co-Authored-By: David Cramer <david@sentry.io>
@vercel

vercel Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
junior-docs Ready Ready Preview Oct 19, 2026 11:16am UTC

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

1 screenshot change — 1 changed · 0 added · 0 removed

Review screenshots in Frameshift

Automations · Mobile
Automations · Mobile
Changed

This branch was successfully deployed

1 active deployment
Preview – junior-docs — 08ca2137 Deployed Oct 19, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants