fix(gateway): rate-limit pre-auth bootstrap-token verify to prevent mutex DoS#77527
Conversation
17be453 to
d6075cf
Compare
|
Codex review: found issues before merge. Reviewed May 31, 2026, 8:24 AM ET / 12:24 UTC. Summary PR surface: Source +36, Tests +219, Docs +1. Total +256 across 5 files. Reproducibility: yes. from source inspection: current main enters verifyDeviceBootstrapToken without a bootstrap-token limiter, and that verifier runs under a shared async lock with state reads/writes. I did not run the in-process reproducer in this read-only review, but the PR's terminal proof and current source make the path high-confidence. Review metrics: 1 noteworthy metric.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review findings
Review detailsBest possible solution: Land the before-mutex bootstrap-token guard with owner-approved non-exempt loopback/default semantics, deferred-or-explicitly-approved failure accounting, focused tests for the accepted envelope, and no normal-PR CHANGELOG.md edit. Do we have a high-confidence way to reproduce the issue? Yes from source inspection: current main enters verifyDeviceBootstrapToken without a bootstrap-token limiter, and that verifier runs under a shared async lock with state reads/writes. I did not run the in-process reproducer in this read-only review, but the PR's terminal proof and current source make the path high-confidence. Is this the best way to solve the issue? No as submitted: the before-mutex gate is the right shape, but the selected limiter envelope and immediate failure accounting still need gateway/security owner approval or adjustment. The safer path is a dedicated non-exempt bootstrap-token guard plus fallback-aware failure accounting unless maintainers explicitly accept the current trade-off. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against 2e254005a016. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +36, Tests +219, Docs +1. Total +256 across 5 files. View PR surface stats
Security concerns:
Acceptance criteria:
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
This comment was marked as low quality.
This comment was marked as low quality.
d6075cf to
829d203
Compare
|
Reproduced both: with no Mirrored the existing Want me to extend this PR with a mandatory Re-review progress:
|
708e399 to
a5fa021
Compare
|
cc @steipete @byungskers and @openclaw/openclaw-secops when there is a moment — rebased onto main, mergeable. Sibling to #77492 (the rate-limit pair). No rush. Thanks! |
a5fa021 to
30ce967
Compare
|
Rebased onto current @openclaw/openclaw-secops — flagging for review (pre-auth bootstrap-token rate limit; companion to #77492 and #76322). |
8e415a2 to
30ce967
Compare
30ce967 to
057fe27
Compare
|
This pull request has been automatically marked as stale due to inactivity. |
057fe27 to
69637a5
Compare
…utex DoS verifyDeviceBootstrapToken is withLock-serialized in src/infra/device-bootstrap.ts and runs an fs read + fs write per attempt. Reaching it in resolveConnectAuthDecision required only a connect frame with a valid Ed25519 device signature (trivially producible by an attacker with their own keypair) plus auth.bootstrapToken — the device-token sibling path was rate-limited but bootstrap-token was not, so an unauthenticated attacker could keep the bootstrap mutex saturated and starve legitimate node onboarding during the attack. Adds AUTH_RATE_LIMIT_SCOPE_BOOTSTRAP_TOKEN with the same optimistic-check pattern as device-token: pre-check for lockout, run verify, recordFailure on mismatch, reset on success. The gate fires for browser-origin clients via the always-on browserRateLimiter (exemptLoopback: false) and for non-browser remote clients when gateway.auth.rateLimit is configured — same reachability envelope as the existing device-token bucket.
69637a5 to
d5b4068
Compare
|
Maintainer proof before landing:
Verification run:
Local live runtime proof using the real OpenClaw gateway auth modules, outside Vitest/mocks: Autoreview: clean, no accepted/actionable findings. CI: PR head Known gap: external non-loopback provider credentials were not exercised; the gateway auth decision path, loopback WebSocket path, changed tests, changed-lane Testbox run, and CI are covered. |
Closes #77978
Summary
verifyDeviceBootstrapToken, which iswithLock-serialized and does pairing-state read/write work per attempt.{scope, clientIp}with the existing rate-limit attempt serializer, so concurrent forged attempts cannot all passcheck()before failures are recorded.CHANGELOG.md.Test plan
pnpm exec oxfmt --write --threads=1 src/gateway/server/ws-connection/auth-context.ts src/gateway/server/ws-connection/auth-context.test.ts src/gateway/server.preauth-bootstrap-token-rate-limit.test.tspnpm test src/gateway/server/ws-connection/auth-context.test.ts -- --reporter=verbose-> 17 passedpnpm test src/gateway/server.preauth-bootstrap-token-rate-limit.test.ts -- --reporter=verbose-> 2 passedpnpm test:changed-> 3 files, 43 passedpnpm changed:lanes --json-> core + coreTestspnpm check:changed-> passed in Blacksmith Testbox through Crabbox,tbx_01kszmmane9czafzf6hv1rchqp, Actions run https://github.com/openclaw/openclaw/actions/runs/26720817359.agents/skills/autoreview/scripts/autoreview --mode local --prompt ...-> clean, no accepted/actionable findingsReal behavior proof
auth.bootstrapToken, causing every attempt to enterverifyDeviceBootstrapToken. That verifier shares a mutex with bootstrap pairing state, so concurrent pre-auth traffic could stall legitimate onboarding.createAuthRateLimiterandresolveConnectAuthDecisionmodules directly from the checkout. No test runner, no mocked patched modules, no synthetic replacement for the limiter or decision helper.node --import tsx /tmp/openclaw-pr-77527-live-proof.mts. The probe imports the patched modules, drives 8 concurrent forged bootstrap decisions for one IP withmaxAttempts=3, then checks device-token fallback while the bootstrap bucket is locked and after an invalid-bootstrap/valid-device fallback.maxAttempts=3; the other 5 short-circuited asrate_limited. The verifier never ran concurrently for the same{bootstrap-token, IP}key. A locked bootstrap bucket skipped bootstrap verification but still allowed valid device-token fallback. An invalid bootstrap submitted with valid device-token fallback did not consume the bootstrap bucket, because the next bootstrap-only attempt still reached the verifier and returnedbootstrap_token_invalidinstead ofrate_limited.