Skip to content

fix: prevent nonce poisoning (WAPI-1121)#69

Open
chakra-guy wants to merge 1 commit intomainfrom
cyfrin/wapi-1121
Open

fix: prevent nonce poisoning (WAPI-1121)#69
chakra-guy wants to merge 1 commit intomainfrom
cyfrin/wapi-1121

Conversation

@chakra-guy
Copy link
Collaborator

@chakra-guy chakra-guy commented Feb 25, 2026

Summary

Prevents nonce poisoning attacks by deferring nonce persistence until after successful decryption. Previously, nonces were saved immediately on message receipt, allowing an attacker to send high-nonce messages that fail decryption but permanently block legitimate messages.

  • Nonce is now confirmed via callback only after successful decryption in BaseClient
  • Added MAX_NONCE_JUMP (100) to reject suspiciously large nonce jumps from known senders
  • Added NaN recovery in nonce storage
  • Added mutex around confirmNonce to prevent concurrent race conditions

Jira

Test plan

  • Unit tests for confirmNonce, NaN recovery, nonce regression prevention
  • All existing unit tests pass (63/63)
  • Lint passes

Note

Medium Risk
Changes transport-level deduplication/nonce persistence behavior and introduces a new confirmNonce handshake between transport and client; mistakes could drop or permanently block legitimate messages (e.g., missed confirmations or nonce-jump threshold).

Overview
Hardens inbound message handling against nonce-poisoning by deferring persistence of received nonces until after successful decryption: WebSocketTransport now emits message events with a confirmNonce() callback, and BaseClient calls it only when decryptMessage succeeds.

Adds additional nonce safety rails: rejects suspicious large nonce jumps from known senders (MAX_NONCE_JUMP), tracks pending nonces to avoid double-processing before confirmation, clears pending state on clear(), and makes nonce storage more robust via NaN recovery plus a mutex-protected confirmNonce path. Tests and changelog are updated accordingly.

Written by Cursor Bugbot for commit fd3a662. This will update automatically on new commits. Configure here.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

adonesky1
adonesky1 previously approved these changes Feb 27, 2026
Copy link

@adonesky1 adonesky1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few minor comments otherwise LGTM

chakra-guy added a commit that referenced this pull request Feb 27, 2026
Address review feedback on #69:

- Wrap storage.confirmNonce() in try/catch inside the transport's
  callback lambda so a transient KV store failure emits an error
  instead of silently dropping a successfully decrypted message.
- Move pendingNonces cleanup outside the try block so it always
  runs regardless of storage success/failure.
- Expand MAX_NONCE_JUMP doc comment to explain why skipping the
  check for new senders is safe (spoofed messages fail decryption
  and never get confirmed).
@chakra-guy chakra-guy requested a review from adonesky1 February 27, 2026 10:18
chakra-guy added a commit that referenced this pull request Feb 27, 2026
Address review feedback on #69:

- Wrap storage.confirmNonce() in try/catch inside the transport's
  callback lambda so a transient KV store failure emits an error
  instead of silently dropping a successfully decrypted message.
- Move pendingNonces cleanup outside the try block so it always
  runs regardless of storage success/failure.
- Expand MAX_NONCE_JUMP doc comment to explain why skipping the
  check for new senders is safe (spoofed messages fail decryption
  and never get confirmed).
…ryption (WAPI-1121)

Nonces are no longer persisted immediately on message receipt. Instead,
a confirmNonce callback is emitted with each message and called by
BaseClient only after successful decryption. This prevents attackers
from poisoning the nonce tracker with high-nonce messages that fail
decryption, which would permanently block legitimate messages.

- Add MAX_NONCE_JUMP (100) to reject suspiciously large nonce jumps
- Add in-memory pendingNonces set to prevent duplicate processing
- Add NaN recovery in nonce storage
- Add mutex around confirmNonce to prevent race conditions
- Wrap confirmNonce in try/catch so storage failures don't drop messages
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants