Skip to content

fix(auth): prevent bootstrap pairing scope changes [AI]#80976

Merged
pgondhi987 merged 3 commits into
openclaw:mainfrom
pgondhi987:fix/fix-614
May 12, 2026
Merged

fix(auth): prevent bootstrap pairing scope changes [AI]#80976
pgondhi987 merged 3 commits into
openclaw:mainfrom
pgondhi987:fix/fix-614

Conversation

@pgondhi987
Copy link
Copy Markdown
Contributor

Summary

  • Problem: bound bootstrap token use could be repeated with a different requested operator scope set before the earlier request completed.
  • Why it matters: pending pairing approval should reflect the scope profile originally requested for that outstanding bootstrap use.
  • What changed: bootstrap token records now track the outstanding requested profile and reject same-device role or scope changes until that use is redeemed.
  • What did NOT change (scope boundary): existing pairing supersede behavior remains available for non-bootstrap paths and identical bootstrap retries still use the same pending profile.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

N/A

  • This PR fixes a bug or regression

Real behavior proof (required for external PRs)

  • Behavior or issue addressed: bootstrap-backed pending pairing no longer accepts a same-device role or scope change while an earlier bootstrap use is still pending.
  • Real environment tested: Not tested in a live OpenClaw setup.
  • Exact steps or command run after this patch: node scripts/run-vitest.mjs run --config test/vitest/vitest.infra.config.ts src/infra/device-bootstrap.test.ts src/infra/device-pairing.test.ts
  • Evidence after fix (screenshot, recording, terminal capture, console output, redacted runtime log, linked artifact, or copied live output): Test Files 2 passed (2); Tests 72 passed (72)
  • Observed result after fix: focused infra regression coverage passes and the pending pairing request remains scoped to the original operator request.
  • What was not tested: live Gateway WebSocket setup flow.
  • Before evidence (optional but encouraged): N/A

Root Cause (if applicable)

  • Root cause: bootstrap verification allowed a bound token to authenticate a later same-device request with a different requested role or scope profile before the previous use had completed bookkeeping.
  • Missing detection / guardrail: regression coverage only rejected requests outside the issued profile, not requests that widened within the issued profile while a prior use was pending.
  • Contributing context (if known): pending pairing supersede logic intentionally merges changed requests for normal pairing paths.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/infra/device-bootstrap.test.ts, src/infra/device-pairing.test.ts
  • Scenario the test should lock in: a bound bootstrap token cannot change from operator.read to broader operator scopes until the prior bootstrap use is redeemed, and the pending pairing request remains unchanged.
  • Why this is the smallest reliable guardrail: the vulnerable decision is made in bootstrap verification before pairing supersede logic can merge scopes.
  • Existing test that already covers this (if any): existing coverage checked issued-profile allowlist rejection, but not same-profile widening while pending.
  • If no new test is added, why not: N/A

User-visible / Behavior Changes

Bootstrap pairing retries that request a different role or scope set while a previous bootstrap use is pending are rejected. Identical retries remain valid.

Diagram (if applicable)

Before:
[bootstrap operator.read] -> [pending operator.read]
[same token operator.write + operator.approvals] -> [pending scopes widened]

After:
[bootstrap operator.read] -> [pending operator.read]
[same token operator.write + operator.approvals] -> [auth rejected] -> [pending unchanged]

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) Yes
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) Yes
  • If any Yes, explain risk + mitigation: bootstrap token records now persist an outstanding requested profile. The added state is limited to roles and scopes already present in the pairing flow, and it is cleared when the matching bootstrap use is redeemed.

Repro + Verification

Environment

  • OS: Linux
  • Runtime/container: Node test runner wrapper
  • Model/provider: N/A
  • Integration/channel (if any): N/A
  • Relevant config (redacted): N/A

Steps

  1. Run node scripts/run-vitest.mjs run --config test/vitest/vitest.infra.config.ts src/infra/device-bootstrap.test.ts src/infra/device-pairing.test.ts.
  2. Confirm bootstrap verification rejects a changed requested profile while the previous use is pending.
  3. Confirm pending pairing scopes remain operator.read.

Expected

  • Changed same-device bootstrap requests are rejected before pending scopes can change.

Actual

  • Focused infra tests passed with the expected rejection and unchanged pending scopes.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: focused infra tests for bootstrap token verification and pending pairing scope preservation.
  • Edge cases checked: identical bound-token retry remains accepted; changed profile after redemption is accepted when still within the issued profile.
  • What you did not verify: live Gateway WebSocket setup flow.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

  • Risk: legitimate clients retrying bootstrap pairing with a different role or scope set before the first use completes will now be rejected.
    • Mitigation: clients can retry the same requested profile, or start a fresh bootstrap flow for a different profile.

@openclaw-barnacle openclaw-barnacle Bot added size: S maintainer Maintainer-authored PR labels May 12, 2026
@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented May 12, 2026

Codex review: needs real behavior proof before merge.

Summary
The PR adds bootstrap-token pendingProfile state and tests so a bound token cannot change its requested role or operator scope set while an earlier use is still outstanding.

Reproducibility: yes. from source inspection: current main accepts a same-device bound bootstrap token without checking whether the requested profile changed, and the pairing layer can merge changed pending scopes. I did not run tests because this was a read-only review.

Real behavior proof
Needs real behavior proof before merge: The PR provides focused test output only; the contributor should add a terminal capture, recording, live output, linked artifact, or redacted runtime log from a real setup, then update the PR body for a fresh review.

Next step before merge
The protected maintainer label, auth/security behavior change, and missing real behavior proof make this an owner-review item rather than a ClawSweeper repair candidate.

Security
Cleared: The diff narrows bootstrap-token reuse and persists only normalized role/scope profile state, with no new dependency, network, workflow, secret exposure, or code-execution surface.

Review details

Best possible solution:

Keep the guard at the bootstrap-token verification boundary, preserve generic non-bootstrap pairing supersede behavior, and require maintainer/security review plus real runtime proof before merge.

Do we have a high-confidence way to reproduce the issue?

Yes, from source inspection: current main accepts a same-device bound bootstrap token without checking whether the requested profile changed, and the pairing layer can merge changed pending scopes. I did not run tests because this was a read-only review.

Is this the best way to solve the issue?

Yes with maintainer approval: checking the outstanding requested profile in device-bootstrap.ts is narrower than changing generic pending-pairing supersede behavior. The final policy call should stay with the protected auth/security owner review.

Acceptance criteria:

  • node scripts/run-vitest.mjs run --config test/vitest/vitest.infra.config.ts src/infra/device-bootstrap.test.ts src/infra/device-pairing.test.ts
  • Live or packaged Gateway WebSocket bootstrap pairing proof showing a same-token retry with changed operator scopes is rejected while the original pending request remains unchanged.

What I checked:

  • Current main accepts bound same-device bootstrap reuse: After a bootstrap token is bound, current verifyDeviceBootstrapToken only checks the bound device/public key and re-persists the record; it does not compare the new requested role/scopes with the earlier accepted request. (src/infra/device-bootstrap.ts:377, ea05be12b4d6)
  • Current main merges changed pending pairing scopes: When the pending approval snapshot differs, requestDevicePairing builds a replacement pending request with roles and scopes merged from the existing same-device pending requests plus the incoming request. (src/infra/device-pairing.ts:540, ea05be12b4d6)
  • Gateway pending path can leave redemption incomplete: The gateway returns a pairing-required failure before the later bootstrap redemption bookkeeping, which makes an outstanding pending bootstrap request a real source-visible state, not only a test artifact. (src/gateway/server/ws-connection/message-handler.ts:1157, ea05be12b4d6)
  • PR diff targets the verifier boundary: The PR patch adds pendingProfile, compares it with a normalized requested profile on bound-token reuse, clears it after matching redemption, and updates focused bootstrap/pairing tests for same-profile widening. (src/infra/device-bootstrap.ts:23, 94195b31b60d)
  • Protected PR state and proof gap: Live PR metadata shows the PR is open, labeled maintainer, changes three files, and the body states the live Gateway WebSocket setup flow was not tested; the only proof provided is focused test output. (94195b31b60d)
  • Relevant current-main history: The available local history attributes the central bootstrap, pairing, and gateway call path in current main to commit db30000 by Shakker. (src/infra/device-bootstrap.ts:330, db30000220e7)

Likely related people:

  • Shakker: Local blame and file history for current main attribute the bootstrap token verifier, pairing supersede path, and gateway bootstrap call path to this author across the central files. (role: recent area contributor; confidence: medium; commits: db30000220e7; files: src/infra/device-bootstrap.ts, src/infra/device-pairing.ts, src/gateway/server/ws-connection/message-handler.ts)

Remaining risk / open question:

  • The PR body explicitly says the live Gateway WebSocket setup flow was not tested, so the real runtime bootstrap pairing path still needs proof.
  • The behavior rejects legitimate same-token retries with a changed requested profile until redemption; that auth policy should be confirmed by the protected maintainer review.

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

@pgondhi987
Copy link
Copy Markdown
Contributor Author

Not applicable to this automation stage; changelog/release-note and external real behavior proof requirements are handled outside auto-pr stages.

Quoted comment from @clawsweeper:

Codex review: needs real behavior proof before merge.

Summary
The PR adds bootstrap-token pendingProfile state and tests so a bound token cannot change its requested role or operator scope set while an earlier use is still outstanding.

Reproducibility: yes. from source inspection: current main accepts a same-device bound bootstrap token without checking whether the requested profile changed, and the pairing layer can merge changed pending scopes. I did not run tests because this was a read-only review.

Real behavior proof
Needs real behavior proof before merge: The PR provides focused test output only; the contributor should add a terminal capture, recording, live output, linked artifact, or redacted runtime log from a real setup, then update the PR body for a fresh review.

Next step before merge
The protected maintainer label, auth/security behavior change, and missing real behavior proof make this an owner-review item rather than a ClawSweeper repair candidate.

Security
Cleared: The diff narrows bootstrap-token reuse and persists only normalized role/scope profile state, with no new dependency, network, workflow, secret exposure, or code-execution surface.

Review details

Best possible solution:

Keep the guard at the bootstrap-token verification boundary, preserve generic non-bootstrap pairing supersede behavior, and require maintainer/security review plus real runtime proof before merge.

Do we have a high-confidence way to reproduce the issue?

Yes, from source inspection: current main accepts a same-device bound bootstrap token without checking whether the requested profile changed, and the pairing layer can merge changed pending scopes. I did not run tests because this was a read-only review.

Is this the best way to solve the issue?

Yes with maintainer approval: checking the outstanding requested profile in device-bootstrap.ts is narrower than changing generic pending-pairing supersede behavior. The final policy call should stay with the protected auth/security owner review.

Acceptance criteria:

  • node scripts/run-vitest.mjs run --config test/vitest/vitest.infra.config.ts src/infra/device-bootstrap.test.ts src/infra/device-pairing.test.ts
  • Live or packaged Gateway WebSocket bootstrap pairing proof showing a same-token retry with changed operator scopes is rejected while the original pending request remains unchanged.

What I checked:

  • Current main accepts bound same-device bootstrap reuse: After a bootstrap token is bound, current verifyDeviceBootstrapToken only checks the bound device/public key and re-persists the record; it does not compare the new requested role/scopes with the earlier accepted request. (src/infra/device-bootstrap.ts:377, ea05be12b4d6)
  • Current main merges changed pending pairing scopes: When the pending approval snapshot differs, requestDevicePairing builds a replacement pending request with roles and scopes merged from the existing same-device pending requests plus the incoming request. (src/infra/device-pairing.ts:540, ea05be12b4d6)
  • Gateway pending path can leave redemption incomplete: The gateway returns a pairing-required failure before the later bootstrap redemption bookkeeping, which makes an outstanding pending bootstrap request a real source-visible state, not only a test artifact. (src/gateway/server/ws-connection/message-handler.ts:1157, ea05be12b4d6)
  • PR diff targets the verifier boundary: The PR patch adds pendingProfile, compares it with a normalized requested profile on bound-token reuse, clears it after matching redemption, and updates focused bootstrap/pairing tests for same-profile widening. (src/infra/device-bootstrap.ts:23, 94195b31b60d)
  • Protected PR state and proof gap: Live PR metadata shows the PR is open, labeled maintainer, changes three files, and the body states the live Gateway WebSocket setup flow was not tested; the only proof provided is focused test output. (94195b31b60d)
  • Relevant current-main history: The available local history attributes the central bootstrap, pairing, and gateway call path in current main to commit db30000 by Shakker. (src/infra/device-bootstrap.ts:330, db30000220e7)

Likely related people:

  • Shakker: Local blame and file history for current main attribute the bootstrap token verifier, pairing supersede path, and gateway bootstrap call path to this author across the central files. (role: recent area contributor; confidence: medium; commits: db30000220e7; files: src/infra/device-bootstrap.ts, src/infra/device-pairing.ts, src/gateway/server/ws-connection/message-handler.ts)

Remaining risk / open question:

  • The PR body explicitly says the live Gateway WebSocket setup flow was not tested, so the real runtime bootstrap pairing path still needs proof.
  • The behavior rejects legitimate same-token retries with a changed requested profile until redemption; that auth policy should be confirmed by the protected maintainer review.

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

@pgondhi987 pgondhi987 merged commit 2d00bed into openclaw:main May 12, 2026
21 of 22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintainer Maintainer-authored PR size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant