v0.41.3.0 fix(security/mcp): OAuth CORS lockdown + pre-register without DCR + validator surface#1403
Merged
Merged
Conversation
…t DCR, validator surface
Three expanded cherry-picks plus codex-surfaced live-CORS fix, parser
rewrite, atomicity fix, DCR validator gate, SECURITY.md reconciliation.
What ships
- gbrain auth register-client gets --redirect-uri (repeatable) and
--token-endpoint-auth-method flags so the SECURITY.md-recommended
"pre-register without --enable-dcr" path actually works for claude.ai
and ChatGPT custom connectors.
- ALLOWED_TOKEN_ENDPOINT_AUTH_METHODS = {client_secret_post,
client_secret_basic, none} validator gates all three registration
entry points (CLI, admin endpoint, DCR /register) so --enable-dcr is
no longer the looser path.
- Live Express OAuth server (/mcp, /token, /authorize, /register,
/revoke) was using default-wide-open cors() middleware — every
origin could complete a token exchange from a logged-in operator's
browser. Now default-deny; allowlist via GBRAIN_HTTP_CORS_ORIGIN.
- GBRAIN_HTTP_TRUST_PROXY env var on Express server with the same
semantics as the legacy bearer transport already had. Default
'loopback' preserved. SECURITY.md doc rewritten to match reality
(was lying that trust proxy was "disabled by default" while code
hardcoded 'loopback').
- Admin endpoint registration now atomic — INSERT-then-UPDATE for
public clients replaced with single INSERT via the new
registerClientManual(..., tokenEndpointAuthMethod) parameter (codex
outside-voice F4 catch).
- Legacy transport corsHeaders + corsPreflightHeaders consolidated
into one function gated on the allowlist for BOTH Allow-Origin and
Allow-Methods/Headers (codex F1; #983 thematically).
Surfaced by D7 codex outside-voice review on the v0.41.3 plan:
F1 (live Express CORS wide-open), F2 (indexOf parser couldn't do
repeatable flags), F3 (client_secret_basic missing from validator),
F4 (admin endpoint INSERT-then-UPDATE atomicity), F5 (DCR path
bypassed validator), F6 (env var already existed on legacy transport),
F7 (SECURITY.md vs impl doc disagreement).
Tests: 183 directly-touched cases green. Three new test files
(test/serve-http-trust-proxy.test.ts, test/serve-http-cors.test.ts,
test/auth-register-client-args.test.ts) + 18 new oauth.test.ts cases
+ 4 IRON RULE CORS preflight regressions.
Plan: ~/.claude/plans/system-instruction-you-are-working-wise-piglet.md
(D1-D11 captured, codex outside-voice integrated, GSTACK REVIEW REPORT
verdict CLEARED).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This was referenced May 25, 2026
…cp-fix-wave # Conflicts: # CHANGELOG.md # VERSION # package.json
writer.log() uses real `new Date()` for filename computation, but the test mocked `now` to 2026-05-22. When CI runs on a date in a different ISO week (e.g. 2026-05-25 W22 vs the mocked W21), log() writes to one file but readRecent(now) reads a different one — zero events overlap, expect(2).toBe(0) fails. Fix: write events directly to the file matching the test's mocked `now` via writer.computeFilename(now), same pattern the cross-week straddle test (line 234+) already used for the previous-week event. Pre-existing test bug, surfaced when CI rolled past the week boundary the original author wrote against. Not introduced by v0.41.3.0; fix included here because /ship found it. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…cp-fix-wave # Conflicts: # CHANGELOG.md # VERSION # package.json
garrytan
added a commit
that referenced
this pull request
May 25, 2026
….9.0 Master moved from v0.41.6.0 to v0.41.3.0 since the last ship: - v0.41.2.0: lens packs + epistemology unification (#1364) - v0.41.3.0: OAuth CORS lockdown + pre-register without DCR (#1403) Master's v0.40.4.0+ audit-writer fix (ts-aware filename selection) supersedes my v0.41.6.0 workaround in test/audit/audit-writer.test.ts. Resolved conflict by keeping master's superior fix. Version retarget per user request: 0.41.6.0 → 0.41.9.0 to claim a clean slot beyond master's v0.41.3.0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mgunnin
added a commit
to mgunnin/gbrain
that referenced
this pull request
May 28, 2026
* upstream/master: (22 commits) v0.41.4.0 wave: local providers + cross-platform stdin + gateway-routed dream judge (6 community PRs) (garrytan#1377) v0.41.3.0 fix(security/mcp): OAuth CORS lockdown + pre-register without DCR + validator surface (garrytan#1403) v0.41.2.0 feat: lens packs + epistemology unification — atoms + concepts as first-class units, calibration profile widening, gstack-learnings bridge (garrytan#1364) v0.41.1.0 feat: eval-loop wave — gbrain bench publish + gbrain eval gate close the LOOP (garrytan#1352) v0.41.0.0 feat(minions): fleet you supervise (4 field bugs + cathedral) (garrytan#1367) v0.40.10.0 feat: content sanity defense — junk-pattern throw + oversize-skip-embed (garrytan#1351) v0.40.9.0 feat(chunker): .sql indexing via tree-sitter + code-def on SQL DDL (garrytan#1173) (garrytan#1350) v0.40.8.1 docs: README rewrite + personal-brain + company-brain tutorials (garrytan#1345) v0.40.8.0 test: e2e + unit gap coverage + master flake root-cause fixes (garrytan#1313) v0.40.6.1 docs(todos): file v0.41 wave commitments + 7 verified-missing items (garrytan#1333) v0.40.7.0 Schema Cathedral v3 — agent-on-ramp + production rebuild of PR garrytan#1321 (garrytan#1327) v0.40.6.0 feat(sync): parallel sync --all + per-source lock invariant + sources status dashboard (productionized from PR garrytan#1314) (garrytan#1324) v0.40.5.0 Federated Sync v2 — parallel source sync + push triggers + per-source health (garrytan#1322) v0.40.4.0 feat(search): selective graph signals + per-stage attribution + audit-writer unification (garrytan#1300) v0.40.3.0 feat: contextual retrieval + cache invalidation gate + 4 deferred-item closures (garrytan#1323) v0.40.2.0 feat: trajectory routing for temporal + knowledge_update (gbrain think + LongMemEval) (garrytan#1296) v0.40.1.0 Track D — eval infrastructure (catch retrieval regressions, prove answer-quality wins) (garrytan#1298) v0.40.0.0 feat: agent-voice (Mars + Venus) + copy-into-host-repo skillpack paradigm (garrytan#1128) v0.39.3.0: productionize the v0.38 ingestion cathedral (smoke-test fix wave from PR garrytan#1299) (garrytan#1308) v0.39.2.0 feat(autopilot): per-source fan-out + cycle lock primitive + phase taxonomy (garrytan#1295) ...
garrytan-agents
pushed a commit
to garrytan-agents/gbrain
that referenced
this pull request
Jun 13, 2026
…ut DCR + validator surface (garrytan#1403) * v0.41.3.0 fix(security/mcp): OAuth CORS lockdown, pre-register without DCR, validator surface Three expanded cherry-picks plus codex-surfaced live-CORS fix, parser rewrite, atomicity fix, DCR validator gate, SECURITY.md reconciliation. What ships - gbrain auth register-client gets --redirect-uri (repeatable) and --token-endpoint-auth-method flags so the SECURITY.md-recommended "pre-register without --enable-dcr" path actually works for claude.ai and ChatGPT custom connectors. - ALLOWED_TOKEN_ENDPOINT_AUTH_METHODS = {client_secret_post, client_secret_basic, none} validator gates all three registration entry points (CLI, admin endpoint, DCR /register) so --enable-dcr is no longer the looser path. - Live Express OAuth server (/mcp, /token, /authorize, /register, /revoke) was using default-wide-open cors() middleware — every origin could complete a token exchange from a logged-in operator's browser. Now default-deny; allowlist via GBRAIN_HTTP_CORS_ORIGIN. - GBRAIN_HTTP_TRUST_PROXY env var on Express server with the same semantics as the legacy bearer transport already had. Default 'loopback' preserved. SECURITY.md doc rewritten to match reality (was lying that trust proxy was "disabled by default" while code hardcoded 'loopback'). - Admin endpoint registration now atomic — INSERT-then-UPDATE for public clients replaced with single INSERT via the new registerClientManual(..., tokenEndpointAuthMethod) parameter (codex outside-voice F4 catch). - Legacy transport corsHeaders + corsPreflightHeaders consolidated into one function gated on the allowlist for BOTH Allow-Origin and Allow-Methods/Headers (codex F1; garrytan#983 thematically). Surfaced by D7 codex outside-voice review on the v0.41.3 plan: F1 (live Express CORS wide-open), F2 (indexOf parser couldn't do repeatable flags), F3 (client_secret_basic missing from validator), F4 (admin endpoint INSERT-then-UPDATE atomicity), F5 (DCR path bypassed validator), F6 (env var already existed on legacy transport), F7 (SECURITY.md vs impl doc disagreement). Tests: 183 directly-touched cases green. Three new test files (test/serve-http-trust-proxy.test.ts, test/serve-http-cors.test.ts, test/auth-register-client-args.test.ts) + 18 new oauth.test.ts cases + 4 IRON RULE CORS preflight regressions. Plan: ~/.claude/plans/system-instruction-you-are-working-wise-piglet.md (D1-D11 captured, codex outside-voice integrated, GSTACK REVIEW REPORT verdict CLEARED). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(test): audit-writer readRecent calendar-boundary flake writer.log() uses real `new Date()` for filename computation, but the test mocked `now` to 2026-05-22. When CI runs on a date in a different ISO week (e.g. 2026-05-25 W22 vs the mocked W21), log() writes to one file but readRecent(now) reads a different one — zero events overlap, expect(2).toBe(0) fails. Fix: write events directly to the file matching the test's mocked `now` via writer.computeFilename(now), same pattern the cross-week straddle test (line 234+) already used for the previous-week event. Pre-existing test bug, surfaced when CI rolled past the week boundary the original author wrote against. Not introduced by v0.41.3.0; fix included here because /ship found it. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Pre-register Claude and ChatGPT clients without
--enable-dcr— the SECURITY.md-recommended setup actually works now.The wave shipped three expanded cherry-picks (#894, #983, #817) plus codex outside-voice catches:
gbrain auth register-clientgets--redirect-uri(repeatable) and--token-endpoint-auth-method. Argv parser rewritten fromindexOf-based lookahead (which silently dropped repeated flags) to a proper loop.corsHeaders+corsPreflightHeadersin the legacy transport, AND closed the BIGGER bug codex found: the live Express OAuth server (/mcp,/token,/authorize,/register,/revoke) was using default-wide-opencors()middleware. Any web origin could complete a token exchange from a logged-in operator's browser. Now default-deny viaGBRAIN_HTTP_CORS_ORIGINallowlist.GBRAIN_HTTP_TRUST_PROXYenv on Express server (the legacy bearer transport already read this env var; the two transports now agree on a single source of truth).SECURITY.mdrewritten to match reality — was lying that trust proxy was "disabled by default" while code hardcoded'loopback'.ALLOWED_TOKEN_ENDPOINT_AUTH_METHODS = {client_secret_post, client_secret_basic, none}gated at all three registration entry points (CLI, admin endpoint, DCR/register) so--enable-dcris no longer the looser path. Plus an atomicity fix on the admin endpoint: pre-fix it didINSERT (confidential) → UPDATE (NULL out secret_hash)for public clients, which left a stranded confidential row if the UPDATE failed mid-flight (codex F4).Defers PR #1316's "Phase 4 multi-agent hardening" cathedral. Its three real wins (deny-by-default fine-grained scopes, real operation names in
mcp_request_log,access_tokens.last_used_atLRU debounce) are filed as TODOS T13a/T13b/T13c. Its RLS posture rewrite stays deferred because it changes the v0.26.7 auto-RLS event trigger thatgbrain doctor'srls_event_triggercheck treats as load-bearing.Closes community PRs #685 (chipoto69, superseded by #1316), #876 (toilalesondev, already merged via v0.34.1.0 #996), #1076 (lukejduncan, already in master), #1077 (lukejduncan, already in master), #620 (ArshyaAI, already in master).
Test Coverage
183/183 directly-touched tests pass in 1.9s. Three new test files + extensions:
test/oauth.test.tsclient_secret_basicregressiontest/http-transport.test.tstest/serve-http-trust-proxy.test.tsresolveTrustProxy()env value mappingtest/serve-http-cors.test.tsparseCorsAllowlistOAuth()+resolveCorsOrigin()test/auth-register-client-args.test.ts--redirect-uriparser, smart--grant-typesdefaulttest/fix-wave-structural.test.tstest/oauth-confidential-client.test.tsclientSecret?Full local fast-loop suite (4 shards) hit infrastructure timeout on 2/4 shards at the per-shard 1500s cap — pre-existing macOS issue, not introduced by this wave. CI's per-matrix timing won't reproduce it.
Pre-Landing Review
Covered by the
/plan-eng-reviewrun that produced the plan file: 7 issues found across Step 0 scope challenge (4 PRs already shipped —#1076,#1077,#620,#876— confirmed via master state inspection), Architecture (env naming D3, CORS DRY D4), Code Quality (validator scope D5), Tests (10 gaps; 5 IRON RULE regressions auto-included). All resolved via D1-D11 with user decisions captured in plan file. Plus codex outside-voice surfaced 7 substantive findings (F1-F7) that became D9/D10/D11 expansions — all integrated.Verification Results
bun run verifyclean (typecheck + 4 shell pre-checks).gbrain auth register-client claude-ai --scopes "read write" --redirect-uri ...thenGBRAIN_HTTP_CORS_ORIGIN=https://claude.ai gbrain serve --http; OPTIONS preflight from non-allowlisted origin returns no Allow-Methods header (was leaking pre-v0.41.3).Plan Completion
D1-D11 all answered. T1-T10 + T12 done in commit. T11 (close 5 community PRs with attribution) deferred to post-merge per CLAUDE.md "Executing actions with care" — closing community PRs is irreversible external state and gets handled after this PR lands so close comments can reference the merged replacement.
TODOS
Three new entries filed under "v0.41.3 security/MCP fix wave follow-ups":
mcp_request_logfrom fix: harden multi-agent MCP access #1316access_tokens.last_used_atLRU debounce from fix: harden multi-agent MCP access #1316PR #1316's RLS posture rewrite deliberately NOT filed (load-bearing v0.26.7 doctor check, needs its own plan).
Test plan
bun run verifycleangbrain serve --httpwith claude.ai allowlisted🤖 Generated with Claude Code