Skip to content

fix(security): add IPC policy entries for daemon OAuth connect routes#29612

Merged
credence-the-bot[bot] merged 1 commit into
credence-the-bot/oauth-connect-daemon-flowfrom
credence/oauth-ipc-policy-fix
May 5, 2026
Merged

fix(security): add IPC policy entries for daemon OAuth connect routes#29612
credence-the-bot[bot] merged 1 commit into
credence-the-bot/oauth-connect-daemon-flowfrom
credence/oauth-ipc-policy-fix

Conversation

@credence-the-bot
Copy link
Copy Markdown
Contributor

@credence-the-bot credence-the-bot Bot commented May 5, 2026

Summary

Addresses Codex P1 on PR #29596: the new daemon-owned OAuth connect routes (internal_oauth_connect_start, internal_oauth_connect_status) registered in assistant/src/runtime/routes/oauth-connect-routes.ts are gateway-only on the daemon HTTP path (internal.write + svc_gateway), but the gateway IPC proxy default-allows operationIds without an entry in POLICY_TABLE.

Without these entries, an authenticated edge JWT can reach the new routes by setting X-Vellum-Proxy-Server: ipc, bypassing the daemon's HTTP router entirely.

Same class of bug as #29571 fixed for the MCP OAuth routes a few hours earlier. Same fix pattern.

Changes

Test results

6 pass
0 fail
18 expect() calls

All existing tests in this file still pass (admin_rollback + 3 MCP entries + 2 new OAuth connect entries = 6 cases).

Why this targets the feature branch and not main

This branch (credence-the-bot/oauth-connect-daemon-flow) is the feature branch for #29596 — the OAuth connect daemon-flow work that introduces the new routes. Routing this fix into the feature branch keeps the security guarantee atomic with the route introduction.


Open in Devin Review

Codex P1 on PR #29596:
#29596 (comment)... (see latest review)

The new daemon-owned OAuth connect HTTP routes added in this branch
(internal_oauth_connect_start, internal_oauth_connect_status) are
gateway-only on the daemon HTTP path (internal.write + svc_gateway,
enforced via daemon-side route-policy), but the gateway IPC proxy
default-allows operationIds without an entry in this policy table.

Without these entries, an authenticated edge JWT could reach the new
routes by setting X-Vellum-Proxy-Server: ipc, bypassing the daemon's
HTTP router entirely (same class of bug as #29571 fixed for the MCP
OAuth routes).

Mirrors Noa's fix in #29571.
@credence-the-bot
Copy link
Copy Markdown
Contributor Author

@chatgpt-codex-connector review

This is the targeted fix for the P1 you flagged on #29596 (IPC policy entries for the new daemon OAuth connect routes). 4-line additive change, mirrors the pattern from #29571. All 6 tests pass locally.

Once this lands, #29596 auto-updates and is ready for your re-review pass.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 1 additional finding.

Open in Devin Review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

ℹ️ 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".

@credence-the-bot credence-the-bot Bot merged commit 7403265 into credence-the-bot/oauth-connect-daemon-flow May 5, 2026
14 checks passed
@credence-the-bot credence-the-bot Bot deleted the credence/oauth-ipc-policy-fix branch May 5, 2026 06:34
noanflaherty pushed a commit that referenced this pull request May 5, 2026
…ay` through daemon IPC (#29596)

* fix(oauth): route `assistant oauth connect --callback-transport=gateway` through daemon IPC (#29570)

* fix(oauth): route assistant oauth connect --callback-transport=gateway through daemon IPC

* fix(oauth-connect): pass lint, type-check, and openapi-check

- Sort imports per simple-import-sort/imports rule (autofix)
- Add eslint-disable for the intentionally-let resolvedState binding
- Narrow OAuthConnectResult union before reading .error (split into two if branches)
- Narrow OAuthConnectStatusResponse before reading .error in CLI poll loop
- Regenerate openapi.yaml to include the 2 new internal routes

* fix: use operationId and structured params for cliIpcCall (Devin/Codex P1 review)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: thread grantedScopes through IPC path to avoid empty-scopes inconsistency (Devin review)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(oauth): align IPC poll timeout with 5-min loopback OAuth window (Codex review)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: credence-the-bot <credence@vellum.ai>
Co-authored-by: credence-the-bot[bot] <183148327+credence-the-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: surface daemon errors instead of silent fallback; add gateway transport test and policy tests (#29590)

Co-authored-by: credence-the-bot <credence@vellum.ai>

* fix: add defensive guards for poll daemon errors and ok:true missing auth_url (#29593)

Co-authored-by: credence-the-bot <credence@vellum.ai>

* test: add missing test coverage for oauth-connect daemon flow (round-3) (#29595)

Co-authored-by: credence-the-bot <credence@vellum.ai>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: suppress browser-wait log in JSON mode; sweep expired OAuth state on insertion (#29597)

* fix: suppress browser-wait log in JSON mode; sweep expired state on insert

* test: make JSON-mode log suppression test exercise the actual if (!jsonMode) guard

Configure the logger mock to write to process.stdout in the specific test so
the test would fail if the guard were removed from connect.ts. Also reset
mockLogInfo to no-op in beforeEach for test isolation.

---------

Co-authored-by: credence-the-bot <credence@vellum.ai>

* fix(oauth): replace 'Daemon' with 'assistant' in user-facing error strings (#29603)

* fix(oauth): replace 'Daemon' with 'assistant' in user-facing error strings

* test: update assertion to match new 'assistant' string from devin nit fix

The previous commit replaced 'Daemon' with 'assistant' in user-facing
error strings (per Devin review), but missed updating the test that
asserts on the old 'Daemon returned unexpected response' wording.

---------

Co-authored-by: credence-the-bot <credence@vellum.ai>
Co-authored-by: credence-the-bot[bot] <credence-the-bot[bot]@users.noreply.github.com>

* fix(security): add IPC policy entries for daemon OAuth connect routes (#29612)

Closes Codex P1 on #29596 by adding IPC policy entries for the new daemon OAuth connect routes (`internal_oauth_connect_start`, `internal_oauth_connect_status`). Mirrors the pattern from #29571 for the MCP OAuth routes. 4-line additive change, 6/6 tests pass. Both Codex and Devin re-reviewed clean.

---------

Co-authored-by: credence-the-bot[bot] <277301654+credence-the-bot[bot]@users.noreply.github.com>
Co-authored-by: credence-the-bot <credence@vellum.ai>
Co-authored-by: credence-the-bot[bot] <183148327+credence-the-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: credence-the-bot[bot] <credence-the-bot[bot]@users.noreply.github.com>
Co-authored-by: credence-the-bot[bot] <credence-the-bot@vellum.ai>
credence-the-bot Bot pushed a commit that referenced this pull request May 6, 2026
…outes

The gateway IPC proxy default-allows operationIds that have no entry in
`gateway/src/auth/ipc-route-policy.ts`. Daemon routes restricted to
`svc_gateway` on the HTTP path are bypassable via IPC unless their
operationId is also locked down here — an authenticated edge JWT can
reach them by setting `X-Vellum-Proxy-Server: ipc`, sidestepping the
daemon HTTP router entirely.

This bug class has shipped twice already and been caught by Codex both
times:
  - PR #29571 — `internal_mcp_auth_start/status/reload`
  - PR #29612 — `internal_oauth_connect_start/status`

While auditing the daemon route policy I found six more gateway-only
operationIds with no IPC policy entry:
  - `channel_inbound` (ingress.write, the only non-internal.write here)
  - `emit_event`
  - `internal_oauth_callback`
  - `internal_twilio_connect_action`
  - `internal_twilio_status`
  - `internal_twilio_voice_webhook`
  - `upgrade_broadcast`
  - `workspace_commit`

This PR:
  1. Adds the eight missing entries to `POLICY_TABLE`.
  2. Adds a comment block in `ipc-route-policy.ts` documenting the
     invariant, so the next contributor sees it.
  3. Extends the explicit test list in `ipc-route-policy.test.ts`.
  4. Adds a new `ipc-route-policy-coverage.test.ts` lint test that
     walks the daemon route source files and asserts every gateway-only
     operationId has a matching IPC policy entry. Catches this class
     of regression without relying on Codex.

The lint is text-based (regex over source files) because the gateway
and assistant packages don't share source-level imports — they
communicate via `@vellumai/service-contracts`. False positives only
add coverage; false negatives would defeat the lint, so regexes are
intentionally loose.

Verification: simulated removing the existing `internal_mcp_reload`
entry — lint correctly flagged it. Simulated removing one of the new
entries — lint correctly flagged it. Both 32 tests pass on the final
state.
noanflaherty pushed a commit that referenced this pull request May 6, 2026
…outes (#29754)

* fix(gateway): close IPC policy coverage gap for gateway-only daemon routes

The gateway IPC proxy default-allows operationIds that have no entry in
`gateway/src/auth/ipc-route-policy.ts`. Daemon routes restricted to
`svc_gateway` on the HTTP path are bypassable via IPC unless their
operationId is also locked down here — an authenticated edge JWT can
reach them by setting `X-Vellum-Proxy-Server: ipc`, sidestepping the
daemon HTTP router entirely.

This bug class has shipped twice already and been caught by Codex both
times:
  - PR #29571 — `internal_mcp_auth_start/status/reload`
  - PR #29612 — `internal_oauth_connect_start/status`

While auditing the daemon route policy I found six more gateway-only
operationIds with no IPC policy entry:
  - `channel_inbound` (ingress.write, the only non-internal.write here)
  - `emit_event`
  - `internal_oauth_callback`
  - `internal_twilio_connect_action`
  - `internal_twilio_status`
  - `internal_twilio_voice_webhook`
  - `upgrade_broadcast`
  - `workspace_commit`

This PR:
  1. Adds the eight missing entries to `POLICY_TABLE`.
  2. Adds a comment block in `ipc-route-policy.ts` documenting the
     invariant, so the next contributor sees it.
  3. Extends the explicit test list in `ipc-route-policy.test.ts`.
  4. Adds a new `ipc-route-policy-coverage.test.ts` lint test that
     walks the daemon route source files and asserts every gateway-only
     operationId has a matching IPC policy entry. Catches this class
     of regression without relying on Codex.

The lint is text-based (regex over source files) because the gateway
and assistant packages don't share source-level imports — they
communicate via `@vellumai/service-contracts`. False positives only
add coverage; false negatives would defeat the lint, so regexes are
intentionally loose.

Verification: simulated removing the existing `internal_mcp_reload`
entry — lint correctly flagged it. Simulated removing one of the new
entries — lint correctly flagged it. Both 32 tests pass on the final
state.

* test(ipc-route-policy-coverage): normalize :params + assert scope parity

Address Codex P2 findings on PR #29754:

1. **Normalize parameterized endpoints before matching.** Daemon routes
   declare endpoints like `internal/oauth/connect/status/:state` while
   the daemon's route-policy keys drop the param segment. The previous
   match silently excluded `:state`, `:serverId`, `:runId` routes
   from the lint — exactly the parameterized gateway-only routes it
   was meant to guard. Strip `/:param` segments before lookup.

2. **Assert IPC requiredScopes match daemon requiredScopes.** Previously
   only principals were checked. If a future gateway-only route's IPC
   entry uses a broader scope than the daemon HTTP path requires, the
   IPC path is more permissive than HTTP — recreating the scope-bypass
   class this guard was designed to prevent. Now each test asserts the
   IPC scope set equals the daemon scope set.

Coverage now includes the previously-missed parameterized routes
(internal_mcp_auth_status, internal_oauth_connect_status, and the
four profiler_runs_by_runId_* routes). Total: 19 gateway-only routes
linted with 56 assertions, up from 17 routes / 35 assertions.

* fix(test): widen Scope[] to string[] for toEqual comparison

bun's typed expect inferred Scope[] from policy!.requiredScopes, then
rejected daemonScopes (string[] from text-parsed daemon source) at the
toEqual call. Both compare structurally as strings — annotate both
locals as string[] to satisfy tsc.

---------

Co-authored-by: credence-the-bot[bot] <207016121+credence-the-bot[bot]@users.noreply.github.com>
Co-authored-by: credence-the-bot[bot] <credence-the-bot[bot]@users.noreply.github.com>
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