Skip to content

fix(pair-code): correct link_code_pairing_nonce byte and close WA Web stage-2 gaps - #976

Merged
jlucaso1 merged 6 commits into
mainfrom
claude/whatsapp-rust-pair-code-bugs-ezguso
Jul 4, 2026
Merged

fix(pair-code): correct link_code_pairing_nonce byte and close WA Web stage-2 gaps#976
jlucaso1 merged 6 commits into
mainfrom
claude/whatsapp-rust-pair-code-bugs-ezguso

Conversation

@jlucaso1

@jlucaso1 jlucaso1 commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

What

Makes phone-number pair-code linking (link_code_companion_reg) behave against the WhatsApp server, cross-checked against the captured WhatsApp Web bundle (WAWeb/Alt/DeviceLinking*.js, WA/Smax/*CompanionHello/Finish/RefreshCode*.js).

The full crypto path (PBKDF2 2<<16, AES-CTR ephemeral wrap, HKDF arg order for the bundle key + adv_secret, GCM tag placement, the pair-success HMAC) was audited against the bundle and already matched WA Web — the changes below cover the deviations and the hardening found in an adversarial review.

1. link_code_pairing_nonce wire byte

companion_hello sent the nonce as b"0" (ASCII 0x30). WA Web's Alt/DeviceLinkingIq.js sends new Uint8Array(1) — a single 0x00 byte — and whatsmeow sends []byte{0}. Now emits the zero byte.

2. Stage-2 notification handling

handle_pair_code_notification assumed every notification was a primary_hello and marked the flow Completed after the first. Brought in line with WA Web DeviceLinkingApi.handlePrimaryHelloInternal / handleAltDeviceLinkingNotification:

Gap WA Web ref Now
Dispatch by child stage routes on t[0].attrs.stage dispatches primary_hello vs refresh_code (unknown → ignore)
Verify link_code_pairing_ref InvalidRefError rejects a primary_hello whose ref ≠ the ref cached from companion_hello
~180s code validity OldCodeError (I=180) stamps code_generation_ts (before companion_hello, matching startAltLinkingFlow) and drops an out-of-window primary_hello
≤3 attempts per code MaxPrimaryHelloError (T=3) counts only genuine (ref-matching, in-window) attempts, checked before the bump so stale/foreign notifications can't exhaust the budget; state retained for retries
refresh_code stage refreshAltLinkingCode / forceManualRefresh new Event::PairingCodeRefresh { force_manual } (ref-gated) + Bot::on_pair_code_refresh helper

3. Concurrency + event hardening (adversarial-review follow-ups)

  • Serialize stage 2. The transport dispatches notification stanzas on concurrent detached tasks, so two primary_hello for the same code could each derive a different random adv_secret and race SetAdvSecretKey (last-write-wins), desyncing the persisted secret from the companion_finish the server acts on → pair-success HMAC failure. The pair_code_state lock is now held across the whole of stage 2 (derive → persist → send), making concurrent notifications sequential — matching WA Web's single-threaded model.
  • Accurate code timeout. Because code_generation_ts now starts before stage 1, the PairingCode event advertises the remaining window (not the full code_validity()), so a consumer's countdown can't outlast the real expiry.

Testing

  • Regression tests driving the top-level handle_pair_code_notification: ref mismatch (rejected, no retry slot spent, state preserved), expired code (rejected, no slot), over-cap 4th attempt (rejected, counter bounded at max), a valid in-window retry reaching stage 2, stale mismatches not blocking the valid one, refresh_code dispatch/ignore + absent-force_manual default, and unknown-stage ignore. The nonce byte is guarded by the wacore companion_hello_iq_shape test.
  • cargo test -p whatsapp-rust --lib pair_code (11) and cargo test -p wacore --lib pair_code (40) green; cargo fmt --all --check and cargo clippy -p whatsapp-rust -p wacore --tests clean.

Not in scope

No replication of WA Web's QPL markers or telemetry pings; no change to the QR pairing path or the pair-success crypto.

claude added 2 commits July 4, 2026 14:46
The companion_hello IQ sent a `link_code_pairing_nonce` of `b"0"` (byte
0x30, the ASCII digit). The captured WA Web bundle (Alt/DeviceLinkingIq.js)
sends `new Uint8Array(1)` — a single 0x00 byte — and whatsmeow sends
`[]byte{0}`, so the previous "matching whatsmeow/baileys" comment was
inaccurate for whatsmeow. Emit the zero byte to match WA Web wire behavior.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdZLyCWkYyEwPtgtdE6DFo
…dling

Bring the phone-number linking companion flow in line with WA Web's
DeviceLinkingApi / handleAltDeviceLinkingNotification:

- Dispatch link_code_companion_reg notifications on the child `stage`
  attribute instead of assuming primary_hello.
- Verify the primary_hello `link_code_pairing_ref` matches the ref cached
  from companion_hello (WA Web InvalidRefError); ignore mismatches.
- Enforce the ~180s code-validity window (WA Web OldCodeError).
- Allow up to 3 primary_hello attempts per code, re-deriving fresh key
  material each time, instead of completing after the first (WA Web
  MaxPrimaryHelloError, T=3). The state is retained after sending
  companion_finish; the terminal transition still happens on pair-success.
- Handle the refresh_code stage: surface a new Event::PairingCodeRefresh
  (with force_manual) when the ref matches the outstanding flow, plus a
  Bot::on_pair_code_refresh convenience handler.

Adds regression tests driving the top-level handler for each guard
(ref mismatch, expired code, attempt cap, valid in-window retry proceeds)
and for refresh_code dispatch/ignore and unknown-stage handling.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdZLyCWkYyEwPtgtdE6DFo
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds PairingCodeRefresh handling end to end: event types, pair-code state and stage-2 guards, bot registration, and the stage-1 nonce wire-format change with matching tests.

Changes

Pairing code refresh and stage-2 retry handling

Layer / File(s) Summary
PairingCodeRefresh event definition
wacore/src/types/events.rs
Adds EventKind::PairingCodeRefresh and Event::PairingCodeRefresh { force_manual }, and extends Event::kind() mapping.
Pair code state machine and nonce format updates
wacore/src/pair_code.rs
Adds PAIR_CODE_MAX_PRIMARY_HELLO_ATTEMPTS and max_primary_hello_attempts(), extends WaitingForPhoneConfirmation with primary_hello_attempt_count, changes the stage-1 nonce to a single zero byte, and updates the related test.
Stage-2 notification handling with retry/refresh guards
src/pair_code.rs
Persists code generation timestamp and attempt count on pair_with_code; restructures handle_pair_code_notification to dispatch by stage; adds ref/expiry/attempt guards in handle_primary_hello; adds handle_refresh_code dispatching Event::PairingCodeRefresh; expands regression tests.
Bot handler registration
src/bot.rs
Adds BotBuilder::on_pair_code_refresh, wiring EventKind::PairingCodeRefresh into on_event_for.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested labels: api-design, breaking-change

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main changes: nonce byte correction and stage-2 pair-code handling fixes.
Description check ✅ Passed The description is directly related to the PR and accurately describes the pair-code linking changes and follow-up hardening.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/whatsapp-rust-pair-code-bugs-ezguso

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added api-design size-increase-ok Accepted binary-size increase: downgrades the per-PR size gate to a warning labels Jul 4, 2026
@greptile-apps

greptile-apps Bot commented Jul 4, 2026

Copy link
Copy Markdown

Greptile Summary

This PR corrects the link_code_pairing_nonce wire byte from ASCII '0' (0x30) to a single 0x00 byte, matching WA Web's new Uint8Array(1) and whatsmeow's []byte{0}. It also brings the stage-2 primary_hello handler up to WA Web parity: ref validation, ~180 s expiry check, and a 3-attempt cap all gate the attempt counter correctly, while refresh_code notifications are dispatched as a new PairingCodeRefresh event only when the ref matches the outstanding flow.

  • Nonce fix (wacore/src/pair_code.rs): single 0x00 byte replaces b"0"; guarded by the updated companion_hello_iq_shape test.
  • Stage-2 gaps (src/pair_code.rs): handle_pair_code_notification now dispatches on stage; ref, expiry, and cap checks precede the counter bump so stale/foreign notifications cannot exhaust the retry budget; the state lock is held across the full stage-2 pipeline to serialise concurrent notifications and prevent adv-secret races.
  • New event + helper (wacore/src/types/events.rs, src/bot.rs): Event::PairingCodeRefresh { force_manual } and BotBuilder::on_pair_code_refresh expose the refresh signal to consumers.

Confidence Score: 5/5

The two targeted fixes are narrow and well-tested; all validation gates precede the attempt counter bump, and the previous reviewer concern about counter contamination is covered by an explicit assertion in the regression suite.

Both changes are self-contained and cross-checked against the WA Web bundle. The critical ordering (ref → expiry → cap → bump) is confirmed by seven targeted regression tests covering mismatch, expiry, over-cap, valid retry, refresh dispatch, absent attribute default, and unknown-stage ignore.

No files require special attention; src/pair_code.rs carries the most logic but the tests provide thorough guard coverage.

Important Files Changed

Filename Overview
src/pair_code.rs Stage-2 dispatcher added; ref validation, expiry check, and attempt cap all gate before the counter bump; lock intentionally held across PBKDF2 + persist + send for serialisation; comprehensive regression tests added
wacore/src/pair_code.rs Nonce corrected from ASCII '0' (0x30) to a single zero byte (0x00) and two new fields added to PairCodeState; well-tested in companion_hello_iq_shape
wacore/src/types/events.rs New PairingCodeRefresh event and EventKind variant added; dispatch in Event::kind() wired correctly
src/bot.rs on_pair_code_refresh builder helper added; follows the same on_event_for pattern as peer helpers

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant App
    participant Client
    participant WA Server
    participant Phone

    App->>Client: pair_with_code(phone, code)
    Note over Client: stamp code_generation_ts
    Client->>WA Server: companion_hello IQ (nonce=0x00)
    WA Server-->>Client: pairing_ref
    Client->>App: "Event::PairingCode { remaining timeout }"
    App->>Phone: User enters 8-char code

    alt "stage = primary_hello"
        WA Server->>Client: notification (stage=primary_hello, ref, wrapped_ephemeral)
        Note over Client: acquire pair_code_state lock
        Client->>Client: "check ref == pairing_ref"
        Client->>Client: "check age <= 180s"
        Client->>Client: "check attempt_count < 3, then bump"
        Client->>Client: PBKDF2 decrypt (spawn_blocking)
        Client->>Client: prepare_key_bundle + persist adv_secret
        Client->>WA Server: companion_finish IQ
        Note over Client: release lock
        WA Server-->>Client: pair-success
    else "stage = refresh_code"
        WA Server->>Client: notification (stage=refresh_code, ref, force_manual)
        Client->>Client: check ref matches (brief lock)
        Client->>App: "Event::PairingCodeRefresh { force_manual }"
    else unknown stage
        WA Server->>Client: warn + ignore
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant App
    participant Client
    participant WA Server
    participant Phone

    App->>Client: pair_with_code(phone, code)
    Note over Client: stamp code_generation_ts
    Client->>WA Server: companion_hello IQ (nonce=0x00)
    WA Server-->>Client: pairing_ref
    Client->>App: "Event::PairingCode { remaining timeout }"
    App->>Phone: User enters 8-char code

    alt "stage = primary_hello"
        WA Server->>Client: notification (stage=primary_hello, ref, wrapped_ephemeral)
        Note over Client: acquire pair_code_state lock
        Client->>Client: "check ref == pairing_ref"
        Client->>Client: "check age <= 180s"
        Client->>Client: "check attempt_count < 3, then bump"
        Client->>Client: PBKDF2 decrypt (spawn_blocking)
        Client->>Client: prepare_key_bundle + persist adv_secret
        Client->>WA Server: companion_finish IQ
        Note over Client: release lock
        WA Server-->>Client: pair-success
    else "stage = refresh_code"
        WA Server->>Client: notification (stage=refresh_code, ref, force_manual)
        Client->>Client: check ref matches (brief lock)
        Client->>App: "Event::PairingCodeRefresh { force_manual }"
    else unknown stage
        WA Server->>Client: warn + ignore
    end
Loading

Reviews (5): Last reviewed commit: "fix(pair-code): advertise remaining vali..." | Re-trigger Greptile

Comment thread src/pair_code.rs Outdated
Comment thread src/pair_code.rs Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 4 files

Confidence score: 3/5

  • In src/pair_code.rs, the retry counter appears to increment before validating primary_hello reference/expiry, so stale or unrelated notifications can consume legitimate pair-code attempts and cause premature pairing failure for real users; move the increment to occur only after those checks pass (and add a regression test for stale-notification handling) before merging.

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/pair_code.rs Outdated
@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown

📦 Binary size report

Metric main PR Δ
bin size (stripped) 10.77 MiB 10.77 MiB +4.19 KiB (+0.04%) 🔺
bin .text 8.77 MiB 8.78 MiB +4.06 KiB (+0.05%) 🔺
bin allocated (text+data+bss) 10.77 MiB 10.77 MiB +4.17 KiB (+0.04%) 🔺
llvm-lines wacore 503,173 503,158 -15 (-0.00%) 🔽
llvm-lines wacore copies 17,243 17,243 0
llvm-lines whatsapp-rust lib 745,608 746,771 +1,163 (+0.16%) 🔺
llvm-lines whatsapp-rust lib copies 24,265 24,288 +23 (+0.09%) 🔺
deps crates (Cargo.lock) 466 466 0
.text per crate
Crate main PR Δ
.text whatsapp_rust 1.60 MiB 1.61 MiB +3.58 KiB (+0.22%) 🔺
.text wacore 528.64 KiB 528.64 KiB 0
.text wacore_binary 157.49 KiB 157.49 KiB 0
.text wacore_libsignal 178.30 KiB 178.73 KiB +442 B (+0.24%) 🔺
.text wacore_appstate 156.42 KiB 156.42 KiB 0
.text wacore_noise 26.05 KiB 26.05 KiB 0
.text waproto 1.60 MiB 1.60 MiB 0
.text whatsapp_rust_sqlite_storage 509.25 KiB 509.25 KiB 0
.text whatsapp_rust_tokio_transport 43.61 KiB 43.61 KiB 0
.text whatsapp_rust_ureq_http_client 10.47 KiB 10.47 KiB 0
.text std 1.00 MiB 1.00 MiB +35 B (+0.00%) 🔺
.text other deps 2.94 MiB 2.94 MiB 0
Top movers (cargo-bloat attribution)
Crate main PR Δ
whatsapp_rust 1.60 MiB 1.61 MiB +3.58 KiB (+0.22%)

Baseline: 4def65976 (latest main run) · Head: 419f89bff · Graphs

… retry cap

Review follow-up (#976): the per-code attempt counter was bumped before the
ref and expiry checks, so a stale/foreign or late `primary_hello` — neither of
which triggers a `companion_finish` — could silently exhaust the 3-try budget
and cause the genuine attempt to be rejected as MaxPrimaryHelloError. WA Web
increments first and shares that window; we now validate (matching ref +
unexpired code) before spending a slot.

Also tightens the pair-code comments to explain "why" per AGENTS.md, and adds
regression coverage: the ref-mismatch and expired-code paths assert the counter
stays at 0, plus a test that several stale mismatched hellos don't block the
subsequent valid one from reaching stage 2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdZLyCWkYyEwPtgtdE6DFo
@coderabbitai coderabbitai Bot added breaking-change and removed size-increase-ok Accepted binary-size increase: downgrades the per-PR size gate to a warning labels Jul 4, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pair_code.rs`:
- Around line 356-363: The retry budget in
PairCodeUtils::handle_primary_hello_attempts is being incremented before
checking the cap, so a rejected attempt still mutates state and can push the
counter past the limit. Reorder the logic to check primary_hello_attempt_count
against PairCodeUtils::max_primary_hello_attempts() before incrementing, and
only increment when the attempt is still allowed; keep the existing warn! and
false return path for exhausted retries.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d03c0a98-ef54-4fab-9fc0-de0f17f21c88

📥 Commits

Reviewing files that changed from the base of the PR and between ed15bdb and 97fa1b1.

📒 Files selected for processing (2)
  • src/pair_code.rs
  • wacore/src/pair_code.rs

Comment thread src/pair_code.rs Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 2 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/pair_code.rs Outdated
…ounter

Review follow-up (#976): the attempt counter was bumped before the cap
comparison, so a rejected over-cap notification still mutated state and let the
counter grow unbounded past the limit. Compare against the max first (`>=`) and
only increment when the attempt is still allowed, keeping the counter bounded at
max. Accept/reject decisions are unchanged (3 processed, 4+ rejected); added an
assertion that a rejected over-cap attempt leaves the counter at max.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdZLyCWkYyEwPtgtdE6DFo

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

0 issues found across 1 file (changes from recent commits).

Requires human review: This PR modifies core pairing protocol logic: nonce byte change, stage-2 notification dispatch with attempt/expiry gates, and a new event type. Even with zero AI issues, the blast radius of a bug in pairing flow (auth, retry, or state machine) is high and requires human judgment.

Re-trigger cubic

greptile-apps[bot]
greptile-apps Bot previously approved these changes Jul 4, 2026
…race

Adversarial review follow-up (#976). The `<notification>` transport dispatches
each stanza on its own detached task (client/node_io.rs), so two `primary_hello`
for the same code can run concurrently. The earlier switch from `mem::take`
(atomic first-wins) to a non-consuming clone (needed for retries) dropped that
atomicity: both notifications could pass the guard, derive a *different* random
adv_secret, and race `SetAdvSecretKey` (last-write-wins) while sending divergent
`companion_finish` bundles — leaving the persisted secret out of sync with the
bundle the server acts on, so pair-success HMAC verification fails.

Hold the `pair_code_state` lock across the whole of `handle_primary_hello`
(derive → persist → send) so concurrent notifications are processed
sequentially, matching WA Web's single-threaded model; the persisted adv_secret
then always corresponds to the last companion_finish sent. `async_lock::Mutex`
is async-aware, so holding it across the spawn_blocking PBKDF2 + IQ send is safe.

Also, for exact WA Web parity, stamp `code_generation_ts` before companion_hello
(WA Web `startAltLinkingFlow`) instead of after the stage-1 round-trip, and add a
regression test that an absent `force_manual_refresh` maps to `force_manual:
false`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdZLyCWkYyEwPtgtdE6DFo
@greptile-apps
greptile-apps Bot dismissed their stale review July 4, 2026 16:06

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 1 file (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread src/pair_code.rs
greptile-apps[bot]
greptile-apps Bot previously approved these changes Jul 4, 2026
Review follow-up (#976). Now that code_generation_ts is stamped before the
stage-1 round-trip (WA Web parity), the ~180s expiry the server (and
handle_primary_hello) enforce is measured from that earlier instant. The
PairingCode event, dispatched after stage 1, was still advertising the full
code_validity(), so a consumer's countdown would outlast the real window by the
stage-1 elapsed time and show a code as valid after it would already be rejected.
Advertise the remaining window (code_validity - elapsed) instead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdZLyCWkYyEwPtgtdE6DFo
@greptile-apps
greptile-apps Bot dismissed their stale review July 4, 2026 16:14

Dismissed because a newer commit was pushed; Greptile will re-review the current head.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

0 issues found across 1 file (changes from recent commits).

Requires human review: Changes to critical pairing logic: nonce byte, stage dispatch, ref validation, expiry, attempt limits, and new event/handler. Requires human review for correctness and security.

Re-trigger cubic

@codspeed-hq

codspeed-hq Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 183 untouched benchmarks


Comparing claude/whatsapp-rust-pair-code-bugs-ezguso (9214c31) with main (4def659)

Open in CodSpeed

@jlucaso1
jlucaso1 merged commit d394551 into main Jul 4, 2026
18 checks passed
@jlucaso1
jlucaso1 deleted the claude/whatsapp-rust-pair-code-bugs-ezguso branch July 4, 2026 16:34
jlucaso1 added a commit that referenced this pull request Jul 4, 2026
…anionOs

WhatsApp's server validates companion_platform_display and rejects a non-OS string
with bad-request — so a consumer that overrides DeviceProps::os for branding (e.g.
"Veloz") could pair by QR but never by phone code. Empirically probed against the
live server (fresh-device companion_hello, stage 1 is pre-auth): real OS names pass
(Linux/Mac/macOS/Mac OS/Ubuntu/Fedora/Windows), non-OS strings and empty are
rejected; the browser half and platform_id are validated too but always come from
CompanionWebClientType, and bad-request is additionally returned on per-number
rate-limiting (~5/number) — noted so callers don't misread it.

Replace the free-form os passthrough with a typed CompanionOs enum (the single
source of truth for the accepted set) + from_hint() that classifies the free-form
DeviceProps::os, collapsing anything unrecognized to Linux (always accepted). QR /
device-name branding is untouched — companion_platform_display is pair-code-only.
Updates the two tests that encoded the old "Mac" passthrough (now "Mac OS") and adds
alias-table + "Veloz"->Linux regressions. Builds on the nonce fix in PR #976.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants