Skip to content

fix(gateway): rate-limit pre-auth bootstrap-token verify to prevent mutex DoS#77527

Merged
steipete merged 2 commits into
openclaw:mainfrom
fede-kamel:security/preauth-bootstrap-token-rate-limit
May 31, 2026
Merged

fix(gateway): rate-limit pre-auth bootstrap-token verify to prevent mutex DoS#77527
steipete merged 2 commits into
openclaw:mainfrom
fede-kamel:security/preauth-bootstrap-token-rate-limit

Conversation

@fede-kamel

@fede-kamel fede-kamel commented May 4, 2026

Copy link
Copy Markdown
Contributor

Closes #77978

Summary

  • Rate-limit the pre-auth bootstrap-token verify path before it reaches verifyDeviceBootstrapToken, which is withLock-serialized and does pairing-state read/write work per attempt.
  • Serialize bootstrap-token auth decisions per {scope, clientIp} with the existing rate-limit attempt serializer, so concurrent forged attempts cannot all pass check() before failures are recorded.
  • Defer bootstrap-token failure accounting until the final auth decision fails, so a valid device-token fallback does not burn the bootstrap-token bucket; a locked bootstrap bucket still skips the bootstrap verifier while allowing device-token fallback.
  • Keep the changelog untouched; release generation owns 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.ts
  • pnpm test src/gateway/server/ws-connection/auth-context.test.ts -- --reporter=verbose -> 17 passed
  • pnpm test src/gateway/server.preauth-bootstrap-token-rate-limit.test.ts -- --reporter=verbose -> 2 passed
  • pnpm test:changed -> 3 files, 43 passed
  • pnpm changed:lanes --json -> core + coreTests
  • pnpm 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 findings

Real behavior proof

  • Behavior addressed: unauthenticated clients could submit a valid self-signed device block plus a forged auth.bootstrapToken, causing every attempt to enter verifyDeviceBootstrapToken. That verifier shares a mutex with bootstrap pairing state, so concurrent pre-auth traffic could stall legitimate onboarding.
  • Real environment tested: local Node runtime from this PR branch, loading the real createAuthRateLimiter and resolveConnectAuthDecision modules directly from the checkout. No test runner, no mocked patched modules, no synthetic replacement for the limiter or decision helper.
  • Exact steps or command run after this patch: ran a standalone Node/tsx runtime probe from the repo root with 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 with maxAttempts=3, then checks device-token fallback while the bootstrap bucket is locked and after an invalid-bootstrap/valid-device fallback.
  • Evidence after fix: live terminal output from that runtime probe:
OpenClaw PR 77527 live runtime proof
real modules: createAuthRateLimiter + resolveConnectAuthDecision
maxAttempts=3, concurrent forged bootstrap attempts=8
forged burst reasons: ["bootstrap_token_invalid","bootstrap_token_invalid","bootstrap_token_invalid","rate_limited","rate_limited","rate_limited","rate_limited","rate_limited"]
verifier calls: 3
max concurrent verifier entries: 1
device fallback while bootstrap bucket locked: {"authOk":true,"authMethod":"device-token"}
invalid bootstrap plus valid device fallback: {"authOk":true,"authMethod":"device-token"}
next bootstrap-only attempt after fallback: bootstrap_token_invalid
  • Observed result after fix: only 3 of 8 forged concurrent attempts entered the bootstrap verifier, matching maxAttempts=3; the other 5 short-circuited as rate_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 returned bootstrap_token_invalid instead of rate_limited.
  • What was not tested: external non-loopback network deployment and live provider credentials. The proof covers the gateway auth decision path, local live WebSocket coverage in the focused gateway check, and CI-parity changed checks.

@fede-kamel fede-kamel requested a review from a team as a code owner May 4, 2026 21:00
@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: M labels May 4, 2026
@fede-kamel fede-kamel force-pushed the security/preauth-bootstrap-token-rate-limit branch from 17be453 to d6075cf Compare May 4, 2026 21:03
@clawsweeper

clawsweeper Bot commented May 4, 2026

Copy link
Copy Markdown
Contributor

Codex review: found issues before merge. Reviewed May 31, 2026, 8:24 AM ET / 12:24 UTC.

Summary
Adds a bootstrap-token auth rate-limit scope, gates WebSocket bootstrap-token verification before the mutex-backed verifier, adds unit/integration coverage, and edits CHANGELOG.md.

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.

  • Auth limiter scopes: 1 added. A new pre-auth credential bucket changes lockout behavior, so maintainers should notice and approve the fallback and loopback semantics before merge.

Merge readiness
Overall: 🦐 gold shrimp
Proof: 🦞 diamond lobster
Patch quality: 🦐 gold shrimp
Result: needs maintainer review before merge.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Resolve whether bootstrap-token verification should use a dedicated non-exempt limiter or keep the current loopback envelope with explicit owner approval.
  • [P2] Either defer bootstrap-token failure accounting until all fallback auth fails, or add owner-approved tests documenting immediate accounting.
  • Remove the CHANGELOG.md edit and keep release-note context in the PR body or squash message.

Risk before merge

  • [P1] The bootstrap-token limiter currently inherits the ordinary auth limiter's loopback exemption for non-browser loopback callers, so the mutex-backed verifier remains reachable from that path unless gateway/security owners explicitly accept the envelope.
  • [P2] Immediate bootstrap-token failure accounting can consume or lock the bootstrap-token bucket for a client that also has a valid device-token fallback, which may block later legitimate bootstrap pairing from the same IP.
  • [P1] The PR still edits release-owned CHANGELOG.md, which should be removed before merge and kept as PR-body or squash-message release-note context instead.

Maintainer options:

  1. Make the mutex guard non-exempt
    Add a dedicated bootstrap-token limiter or equivalent path that does not inherit the ordinary non-browser loopback exemption, then cover that accepted envelope in tests.
  2. Defer bootstrap failure accounting
    Record bootstrap-token failures only if the overall handshake fails, or add explicit gateway/security-owner approval plus tests for immediate accounting with device-token fallback.
  3. Accept the current envelope
    Maintainers can choose to accept loopback exemption and immediate bootstrap-bucket accounting, but that should be an explicit security/compatibility decision before merge.

Next step before merge

  • Human gateway/security review should decide the bootstrap-token limiter envelope and fallback accounting policy before this branch is repaired or merged.

Security
Needs attention: The patch improves a real pre-auth DoS surface, but the limiter envelope and fallback accounting still leave security/availability decisions unresolved.

Review findings

  • [P1] Use a non-exempt bootstrap limiter — src/gateway/server/ws-connection/auth-context.ts:196-200
  • [P2] Defer bootstrap failures until fallback auth fails — src/gateway/server/ws-connection/auth-context.ts:230-231
  • [P3] Remove the release-owned changelog edit — CHANGELOG.md:2341
Review details

Best 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:

  • [P1] Use a non-exempt bootstrap limiter — src/gateway/server/ws-connection/auth-context.ts:196-200
    This check uses the ordinary selected auth limiter, which keeps the default loopback exemption for non-browser loopback clients. Because this verifier is the mutex-backed path the PR is trying to protect, local non-browser callers can still enter the serialized read/write loop unless this scope gets a dedicated non-exempt limiter or gateway/security owners explicitly accept that envelope with coverage.
    Confidence: 0.86
  • [P2] Defer bootstrap failures until fallback auth fails — src/gateway/server/ws-connection/auth-context.ts:230-231
    Recording the bootstrap-token failure here happens before device-token fallback. A client carrying a stale bootstrap token and a valid device token can still authenticate, but it will consume or lock the bootstrap-token bucket for that IP, which can block later legitimate bootstrap pairing; either defer the record until the overall handshake fails or add explicit owner-approved tests for this behavior.
    Confidence: 0.84
  • [P3] Remove the release-owned changelog edit — CHANGELOG.md:2341
    Normal PRs should keep release-note context in the PR body or squash message; CHANGELOG.md is release-owned in this repository. Please drop this entry from the branch before merge.
    Confidence: 0.95

Overall correctness: patch is incorrect
Overall confidence: 0.86

AGENTS.md: found and applied where relevant.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 2e254005a016.

Label changes

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix terminal output from a Node/tsx reproducer importing the patched modules and showing over-budget attempts skip verifier entry, with the WebSocket transport covered by the added in-process integration test.

Label justifications:

  • P1: This is a pre-auth gateway availability/security fix for a mutex-backed verifier that can stall legitimate onboarding.
  • merge-risk: 🚨 compatibility: The patch changes bootstrap-token auth fallback/failure-accounting behavior for existing clients that may also carry a valid device token.
  • merge-risk: 🚨 availability: The new bucket can block bootstrap-token verification for an IP, and the current loopback envelope still leaves part of the mutex-stall surface reachable.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦞 diamond lobster and patch quality is 🦐 gold shrimp.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Sufficient (terminal): The PR body includes after-fix terminal output from a Node/tsx reproducer importing the patched modules and showing over-budget attempts skip verifier entry, with the WebSocket transport covered by the added in-process integration test.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix terminal output from a Node/tsx reproducer importing the patched modules and showing over-budget attempts skip verifier entry, with the WebSocket transport covered by the added in-process integration test.
Evidence reviewed

PR surface:

Source +36, Tests +219, Docs +1. Total +256 across 5 files.

View PR surface stats
Area Files Added Removed Net
Source 2 53 17 +36
Tests 2 219 0 +219
Docs 1 1 0 +1
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 5 273 17 +256

Security concerns:

  • [medium] Loopback non-browser verifier path remains exempt — src/gateway/server/ws-connection/auth-context.ts:196
    The new gate uses the ordinary auth limiter, whose default exempts loopback; for a mutex-backed pre-auth verifier, that leaves local non-browser callers able to reach the serialized path unless maintainers explicitly accept that threat model.
    Confidence: 0.84
  • [low] Successful fallback can still consume bootstrap bucket — src/gateway/server/ws-connection/auth-context.ts:231
    A rejected bootstrap token records a failure before device-token fallback, so ultimately successful clients can still lock out bootstrap-token verification for their IP.
    Confidence: 0.82

Acceptance criteria:

  • [P1] node scripts/run-vitest.mjs src/gateway/server.preauth-bootstrap-token-rate-limit.test.ts src/gateway/server/ws-connection/auth-context.test.ts.
  • [P2] Review/update gateway tests for loopback/default limiter behavior and stale-bootstrap-token plus valid-device-token fallback.

What I checked:

  • Current vulnerable source path: Current main calls verifyBootstrapToken directly when a bootstrap token and device identity are present, before the device-token fallback and without a bootstrap-token rate-limit check. (src/gateway/server/ws-connection/auth-context.ts:188, 2e254005a016)
  • Mutex-backed verifier: verifyDeviceBootstrapToken runs inside withLock and reads/writes bootstrap state, matching the PR's DoS root-cause description. (src/infra/device-bootstrap.ts:426, 2e254005a016)
  • Generic limiter keeps loopback exemption: Current main creates the ordinary auth limiter from rateLimitConfig/defaults and createAuthRateLimiter exempts loopback by default, while only the browser-origin limiter forces exemptLoopback:false. (src/gateway/server.impl.ts:450, 2e254005a016)
  • PR uses the ordinary selected limiter: The patch checks params.rateLimiter for the bootstrap-token bucket, so non-browser loopback callers still inherit the ordinary loopback exemption instead of a dedicated non-exempt mutex guard. (src/gateway/server/ws-connection/auth-context.ts:196, 69637a53b7b8)
  • Immediate bootstrap failure accounting: The patch records bootstrap-token failures immediately after verify rejects, before device-token fallback can succeed; the PR body also calls out this trade-off and says deferred semantics would be a small change. (src/gateway/server/ws-connection/auth-context.ts:231, 69637a53b7b8)
  • Release-owned changelog edit: The branch still adds a normal PR entry to CHANGELOG.md even though repository policy keeps CHANGELOG.md release-owned for normal PRs. (CHANGELOG.md:2341, 69637a53b7b8)

Likely related people:

  • buerbaumer: Commit 30b6ecc added gateway auth rate limiting and the loopback-exempt default that this PR now reuses for a new bootstrap-token bucket. (role: introduced auth limiter behavior; confidence: high; commits: 30b6eccae5af; files: src/gateway/auth-rate-limit.ts)
  • ngutman: Commits a9140ab, 226ca1f, and 017bc52 are recent QR/bootstrap auth changes in the affected path. (role: bootstrap auth feature contributor; confidence: high; commits: a9140abea6d4, 226ca1f324a4, 017bc5261c9b; files: src/gateway/server/ws-connection/auth-context.ts, src/infra/device-bootstrap.ts)
  • steipete: Recent commits such as 54a27f4 and f5eca3f touched the gateway limiter/auth-context area and current lint/refactor state. (role: recent area contributor; confidence: high; commits: 54a27f4e5757, f5eca3f84cbf; files: src/gateway/auth-rate-limit.ts, src/gateway/server/ws-connection/auth-context.ts)
  • eleqtrizit: Commit b8372a7 recently changed bootstrap handoff scopes in the same bootstrap-token state surface. (role: adjacent bootstrap handoff contributor; confidence: medium; commits: b8372a714ccc; files: src/infra/device-bootstrap.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

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
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@byungskers

This comment was marked as low quality.

@fede-kamel fede-kamel force-pushed the security/preauth-bootstrap-token-rate-limit branch from d6075cf to 829d203 Compare May 4, 2026 21:10
@fede-kamel

fede-kamel commented May 4, 2026

Copy link
Copy Markdown
Contributor Author

Reproduced both: with no gateway.auth.rateLimit configured (Q1), 50/50 forged attempts hit the verify; on loopback with default exemptLoopback: true (Q2), 20/20 do.

Mirrored the existing AUTH_RATE_LIMIT_SCOPE_DEVICE_TOKEN reachability to keep the diff minimal — but you're right that bootstrap-token is the only scope behind a process-global mutex (withLock in device-bootstrap.ts:39) and the legitimate-failure rate is near zero, so always-on enforcement has no real UX cost.

Want me to extend this PR with a mandatory bootstrapRateLimiter (always created, hardcoded exemptLoopback: false)? Small change, closes both. Otherwise land as-is and I'll follow up separately.

Re-review progress:

@fede-kamel fede-kamel force-pushed the security/preauth-bootstrap-token-rate-limit branch 2 times, most recently from 708e399 to a5fa021 Compare May 5, 2026 16:31
@openclaw-barnacle openclaw-barnacle Bot added the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 5, 2026
@fede-kamel

fede-kamel commented May 5, 2026

Copy link
Copy Markdown
Contributor Author

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!

@fede-kamel

Copy link
Copy Markdown
Contributor Author

Rebased onto current origin/main (was 372 commits behind). The previous "Real behavior proof" CI failure was a missing-script artifact — scripts/github/real-behavior-proof-check.mjs was added in #77622 after this branch was first pushed, so the workflow could not load on the old base. The script is on HEAD now and CI is re-running.

@openclaw/openclaw-secops — flagging for review (pre-auth bootstrap-token rate limit; companion to #77492 and #76322).

@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 9, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 9, 2026
@openclaw-barnacle openclaw-barnacle Bot added scripts Repository scripts size: L and removed size: M labels May 9, 2026
@fede-kamel fede-kamel force-pushed the security/preauth-bootstrap-token-rate-limit branch from 8e415a2 to 30ce967 Compare May 9, 2026 17:58
@openclaw-barnacle openclaw-barnacle Bot added size: M and removed scripts Repository scripts size: L proof: sufficient ClawSweeper judged the real behavior proof convincing. labels May 9, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 9, 2026
@fede-kamel fede-kamel force-pushed the security/preauth-bootstrap-token-rate-limit branch from 30ce967 to 057fe27 Compare May 9, 2026 18:39
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 9, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 9, 2026
@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label May 31, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. labels May 31, 2026
@barnacle-openclaw barnacle-openclaw Bot removed the stale Marked as stale due to inactivity label May 31, 2026
@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels May 31, 2026
@fede-kamel fede-kamel force-pushed the security/preauth-bootstrap-token-rate-limit branch from 057fe27 to 69637a5 Compare May 31, 2026 12:18
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 31, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 31, 2026
@steipete steipete self-assigned this May 31, 2026
fede-kamel and others added 2 commits May 31, 2026 19:21
…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.
@steipete steipete force-pushed the security/preauth-bootstrap-token-rate-limit branch from 69637a5 to d5b4068 Compare May 31, 2026 18:34
@openclaw-barnacle openclaw-barnacle Bot added triage: mock-only-proof Candidate: PR proof only shows tests, mocks, snapshots, lint, typecheck, or CI. proof: supplied External PR includes structured after-fix real behavior proof. and removed proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. triage: mock-only-proof Candidate: PR proof only shows tests, mocks, snapshots, lint, typecheck, or CI. labels May 31, 2026
@steipete

Copy link
Copy Markdown
Contributor

Maintainer proof before landing:

  • Rebased on current origin/main and pushed maintainer fixup d5b406844ed3637130cc7366fd3919c70bc16ed1 to the PR branch.
  • Best-fix shape: the bootstrap-token path now serializes the rate-limit check, verifier call, and final failure accounting by bootstrap scope + client IP. Bootstrap verifier failures are only charged if final auth rejects, so valid device-token fallback still works and bootstrap lockout does not block device-token auth.
  • Removed the PR changelog edit; release-note context is in the PR body/merge message per repo policy.

Verification run:

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.ts
pnpm test src/gateway/server/ws-connection/auth-context.test.ts -- --reporter=verbose
pnpm test src/gateway/server.preauth-bootstrap-token-rate-limit.test.ts -- --reporter=verbose
pnpm test:changed
pnpm check:changed

pnpm check:changed passed through Crabbox/Testbox on Blacksmith: tbx_01kszmmane9czafzf6hv1rchqp, actions run https://github.com/openclaw/openclaw/actions/runs/26720817359.

Local live runtime proof using the real OpenClaw gateway auth modules, outside Vitest/mocks:

OpenClaw PR 77527 live runtime proof
real modules: createAuthRateLimiter + resolveConnectAuthDecision
maxAttempts=3, concurrent forged bootstrap attempts=8
forged burst reasons: ["bootstrap_token_invalid","bootstrap_token_invalid","bootstrap_token_invalid","rate_limited","rate_limited","rate_limited","rate_limited","rate_limited"]
verifier calls: 3
max concurrent verifier entries: 1
device fallback while bootstrap bucket locked: {"authOk":true,"authMethod":"device-token"}
invalid bootstrap plus valid device fallback: {"authOk":true,"authMethod":"device-token"}
next bootstrap-only attempt after fallback: bootstrap_token_invalid

Autoreview: clean, no accepted/actionable findings.

CI: PR head d5b406844ed3637130cc7366fd3919c70bc16ed1 completed successfully at https://github.com/openclaw/openclaw/actions/runs/26720994393. Latest real-behavior-proof and auto-response contexts are green.

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.

@steipete steipete merged commit ecbd97e into openclaw:main May 31, 2026
180 of 186 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway Gateway runtime merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P1 High-priority user-facing bug, regression, or broken workflow. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. size: M status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: pre-auth bootstrap-token verify allows mutex-stall DoS without rate limit

3 participants