Skip to content

fix: harden credential and runtime trust boundaries (rebase of #916) - #936

Draft
lidge-jun wants to merge 4 commits into
devfrom
codex/916-trust-boundaries
Draft

fix: harden credential and runtime trust boundaries (rebase of #916)#936
lidge-jun wants to merge 4 commits into
devfrom
codex/916-trust-boundaries

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Rebase of #916 (@Ingwannu) onto current dev, with the three defects a branch review found.

Draft on purpose. This touches authentication, credential handling, GitHub workflows, and the durable launcher — MAINTAINERS.md requires explicit security review for exactly that surface. The review below is an audit, not that review, and an agent does not substitute for it. Do not merge on my say-so.

The defects are real on current dev

Confirmed by direct probe rather than by reading the PR description:

  • Vertex authority injection. src/adapters/google.ts builds the request host from provider.location with no validation, then attaches an ADC bearer. A location of attacker.example:443/capture# yields authority attacker.example — I reproduced this against dev.
  • Durable Bun executable injection. overrideBunPath() rereads OPENCODEX_BUN_PATH after Bun has loaded project dotenv, so every durableBunRuntime() caller — service, shim, WinSW, tray — can persist a repository-chosen executable.
  • Claude destination injection. An ambient ANTHROPIC_BASE_URL survived credential stripping and reached Claude, which can redirect an OAuth-bearing request.
  • Management-token disclosure. src/oauth/health.ts attached the admin token after only a forgeable /healthz identity check.

None of these were superseded by #917. That PR resolved the inbound management principal — who may call the star route. This is the outbound listener: whether the thing answering on the port deserves the token. Different boundary.

What the review changed

src/cli/claude.ts no-context fallback — narrowed. As submitted, a launch with no trusted launcher context deleted all three ambient Anthropic slots. bun src/cli/index.ts is a documented entry point (structure/01_runtime.md:9) and has no launcher context, so a user with a shell-exported ANTHROPIC_API_KEY simply lost it.

The two slot classes are not symmetric. ANTHROPIC_BASE_URL stays fail-closed — a dotenv-only destination plus subscription auth is precisely how the OAuth bearer leaves for a repository-chosen host, and losing a legitimate custom destination costs a flag rather than an account. Credentials are preserved: the destination is already pinned by the time they are read, so stripping them defends against a project file that could equally well have supplied the key being blamed. The test that mandated the old behavior is replaced by two that pin the asymmetry.

structure/06_docs-and-release.md — the ci.yml row still described the pre-#899 hosted-Windows selector. Rewritten against the real job graph: four Linux shards plus gates, full macOS, Windows only at the shipping boundary, and the aggregate ci job asserting Windows actually succeeded there.

structure/01_runtime.md decision log — recorded the rejected fail-closed-for-everything behavior; now describes the split.

Rebase

One conflict, src/server/index.ts, resolved keeping both #917's managementPrincipal() dispatch and this branch's attestation imports.

Evidence

  • bun run test: 7602 pass / 0 fail across 505 files
  • bun x tsc --noEmit exit 0, bun run privacy:scan passed
  • Focused security suites: 124 pass / 0 fail across the seven affected files

Authorship on the original commits is preserved. @Ingwannu — the substance is yours; the three changes above are the review response, and I would rather you disagree with them here than have them land silently.


Security review round — my narrowing was wrong

The adversarial review returned FAIL on the one change I made to @Ingwannu's behavior, and it was right.

I had preserved ambient credentials when no launcher context exists, reasoning that the destination is pinned before they are read, so a dotenv-supplied key could only ever reach the local proxy. That reasoning does not survive the subscription path. CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST is only set when opencodex owns an auth token — asserting it otherwise logs a subscriber out (#253) — so Claude Code's settings.env merge can still replace ANTHROPIC_BASE_URL after buildClaudeEnv returns. A preserved key travels to that host.

This repository already documents the destination residual for subscription mode, with a test asserting it so it cannot drift into an assumed guarantee. Preserving credentials would have quietly widened that documented residual into a credential leak.

So all three slots fail closed again without provenance, exactly as @Ingwannu submitted it. Direct bun src/cli/index.ts loses ambient Anthropic values — a real cost to a documented entry point, and the honest trade. The escape hatch is running through the published ocx bin, where genuine shell exports survive by proof.

The gap that let my bad revision pass: the suite tested no-context credential handling and settings-hijack separately, never combined. It does now — a no-context ambient key must be absent after the merge that hijacks the destination. Reintroducing the narrowing fails 6 tests.

The review also cleared the rest: the Vertex validator is a strict lowercase 1–63 byte DNS label rejecting dots, Unicode, percent-encoding, fragments, paths, uppercase and overlong values while accepting every legitimate location, enforced on both the adapter and the management write path; local attestation binds a fresh 256-bit challenge plus PID and port, fails closed on malformed proofs, and compares timing-safely; the Bun launcher stamps before Bun starts on every documented invocation including the Windows .cmd shim; and the rebase kept managementPrincipal() with the correct handleManagementAPI arity.

Updated: bun run test 7602 pass / 0 fail across 505 files, typecheck exit 0, privacy scan passed.

Still draft, still needs maintainer security review. Two rounds of adversarial audit are not that review.

Ingwannu and others added 3 commits August 3, 2026 16:16
Review of this branch rejected three things. Two are docs; this is the behavior
one.

With no trusted launcher context, the branch deleted all three ambient Anthropic
slots. That breaks a documented entry point: `bun src/cli/index.ts` is supported
(structure/01_runtime.md:9) and has no launcher context, so a user who exported
ANTHROPIC_API_KEY in their shell simply loses it.

The two slot classes are not symmetric. ANTHROPIC_BASE_URL stays fail-closed — a
dotenv-only destination combined with subscription auth is exactly how Claude's
OAuth bearer and prompt leave for a host the repository chose, and losing a
legitimate custom destination costs a flag rather than an account. Credentials
are preserved: the destination is already pinned by the time they are read, so
stripping them defends against a project file that could equally well have
supplied the key it is being blamed for.

The test that mandated the old behavior is replaced by two: an ambient key
survives without context, and an ambient base URL is still replaced. That pair
is the contract.

Also corrected two structure docs the review flagged as stale: the ci.yml row
still described the pre-#899 hosted-Windows selector, and the runtime decision
log recorded the rejected fail-closed-for-everything behavior.
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4fb7f23b-a204-4176-9942-e66e60d57949

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the bug Something isn't working label Aug 3, 2026
The security review rejected my narrowing, correctly.

I had preserved ambient credentials when no launcher context exists, reasoning
that the destination is pinned before they are read so a dotenv key would only
reach the local proxy. That reasoning does not survive the subscription path:
CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST is only set when opencodex owns an auth
token — asserting it otherwise logs a subscriber out (#253) — so Claude Code's
settings.env merge can still replace ANTHROPIC_BASE_URL after buildClaudeEnv
returns. A preserved key travels to that host. The repository documents the
destination residual for subscription mode; preserving credentials would have
widened it into a credential leak.

So all three slots fail closed again without provenance. Direct
`bun src/cli/index.ts` loses ambient Anthropic values, which is a real cost to a
documented entry point; the escape hatch is the published `ocx` bin, where
genuine shell exports survive by proof.

The gap that let the bad revision pass: the suite tested no-context credential
handling and settings-hijack separately, never combined. It does now — a
no-context ambient key must be absent after the merge that hijacks the
destination. Reintroducing the narrowing fails 6 tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants