Route OAuth token HTTP through hosted egress guard#1101
Conversation
Cloudflare previewTorn down — the PR is closed. |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
executor-marketing | f5906e3 | Commit Preview URL Branch Preview URL |
Jun 23 2026, 06:12 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
executor-cloud | f5906e3 | Jun 23 2026, 06:13 PM |
a575a3e to
a186b3f
Compare
@executor-js/cli
@executor-js/config
@executor-js/execution
@executor-js/sdk
@executor-js/codemode-core
@executor-js/runtime-quickjs
@executor-js/plugin-file-secrets
@executor-js/plugin-graphql
@executor-js/plugin-keychain
@executor-js/plugin-mcp
@executor-js/plugin-onepassword
@executor-js/plugin-openapi
executor
commit: |
fe019f2 to
02ee1b1
Compare
a186b3f to
f3d7be5
Compare
02ee1b1 to
26b8f48
Compare
f3d7be5 to
14f2dbb
Compare
Greptile SummaryThis PR routes OAuth token-endpoint HTTP (authorization-code exchange, client-credentials, and token refresh) through the same DNS/SSRF guard that already protects the hosted
Confidence Score: 5/5Safe to merge — the change is additive, narrows the attack surface for OAuth token-endpoint SSRF, and does not alter any non-hosted code paths. All three OAuth grant helpers now receive the guarded fetch on the hosted path, and none of the existing non-hosted paths are affected. The No files require special attention. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant SE as scoped-executor
participant CE as createExecutor
participant OS as OAuthService
participant OH as oauth-helpers
participant GF as guardFetch (DNS guard)
participant O4W as oauth4webapi
participant TE as Token Endpoint
SE->>SE: makeHostedFetch(hostedHttpOptions)
SE->>CE: "createExecutor({ fetch: hostedFetch, httpClientLayer })"
Note over CE: Token refresh / client-credentials path
CE->>OH: "exchangeClientCredentials({ fetch: config.fetch })"
CE->>OH: "refreshAccessToken({ fetch: config.fetch })"
OH->>O4W: "customFetch = hostedFetch"
O4W->>GF: hostedFetch(tokenUrl, init)
GF->>GF: validateHostedOutboundUrl (DNS / private-IP check)
GF->>TE: underlying fetch (redirect: manual)
TE-->>GF: response
GF-->>O4W: response
Note over CE: Authorization-code path (via OAuthService)
CE->>OS: "makeOAuthService({ fetch: config.fetch })"
OS->>OH: "exchangeAuthorizationCode({ fetch })"
OH->>O4W: "customFetch = hostedFetch"
O4W->>GF: hostedFetch(tokenUrl, init)
GF->>GF: validateHostedOutboundUrl
GF->>TE: underlying fetch (redirect: manual)
TE-->>GF: response
GF-->>O4W: response
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant SE as scoped-executor
participant CE as createExecutor
participant OS as OAuthService
participant OH as oauth-helpers
participant GF as guardFetch (DNS guard)
participant O4W as oauth4webapi
participant TE as Token Endpoint
SE->>SE: makeHostedFetch(hostedHttpOptions)
SE->>CE: "createExecutor({ fetch: hostedFetch, httpClientLayer })"
Note over CE: Token refresh / client-credentials path
CE->>OH: "exchangeClientCredentials({ fetch: config.fetch })"
CE->>OH: "refreshAccessToken({ fetch: config.fetch })"
OH->>O4W: "customFetch = hostedFetch"
O4W->>GF: hostedFetch(tokenUrl, init)
GF->>GF: validateHostedOutboundUrl (DNS / private-IP check)
GF->>TE: underlying fetch (redirect: manual)
TE-->>GF: response
GF-->>O4W: response
Note over CE: Authorization-code path (via OAuthService)
CE->>OS: "makeOAuthService({ fetch: config.fetch })"
OS->>OH: "exchangeAuthorizationCode({ fetch })"
OH->>O4W: "customFetch = hostedFetch"
O4W->>GF: hostedFetch(tokenUrl, init)
GF->>GF: validateHostedOutboundUrl
GF->>TE: underlying fetch (redirect: manual)
TE-->>GF: response
GF-->>O4W: response
Reviews (2): Last reviewed commit: "Route OAuth token fetches through hosted..." | Re-trigger Greptile |
26b8f48 to
f5906e3
Compare
|
Superseded by batch merge #1106. |
What changed
createExecutoras the genericfetchboundary while normal SDK and plugin HTTP stays onhttpClientLayer.Why
OAuth4WebAPI requires a fetch-compatible hook for token endpoint calls. Hosted executors need that forced boundary to use the same private-network guard as the rest of hosted outbound HTTP.
Validation
bun --bun vitest run src/oauth-helpers.test.ts src/oauth-flow.test.ts src/hosted-http-client.test.tsfrompackages/core/sdkbun --bun run typecheckfrompackages/core/sdkbun --bun run typecheckfrompackages/core/apiStack
Base:
fix/hosted-egress-dns-guardPrevious: #1100
Next:
fix/mcp-hosted-egress-guard