Skip to content

feat(tctoken): attach tctoken in usync status/about and spam-report IQs - #969

Merged
jlucaso1 merged 2 commits into
mainfrom
claude/whatsapp-rust-token-review-7jsnxz
Jul 3, 2026
Merged

feat(tctoken): attach tctoken in usync status/about and spam-report IQs#969
jlucaso1 merged 2 commits into
mainfrom
claude/whatsapp-rust-token-review-7jsnxz

Conversation

@jlucaso1

@jlucaso1 jlucaso1 commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

A compliance sweep of the captured WA Web JS for every site that attaches a trusted-contact token found two outgoing requests where the lib omitted it, so they fail for privacy-restricted accounts. This closes both, following the same pattern the merged #966 established for 1:1 messages.

Verified against the captured JS: WA/Smax/OutSpamTCTokenMixin.js, WA/Smax/OutSpamIndividualReportRequest.js, WAWeb/Usync/Status.js, WAWeb/Get/AboutQueryJob.js, WAWeb/Usync/User.js.

1. Usync status/about query — missing per-user <tctoken>

WA Web's USyncStatusProtocol.getUserElement attaches a per-user <tctoken> to the usync <user> node (gated on profile_scraping_privacy_token_in_about_usync); getAbout builds the status usync via USyncUser.withTcToken. Without it, a restricted contact's status/about comes back empty/401.

UserInfoSpec now carries tc_tokens (keyed by the query JID's user part) and attaches <tctoken> to the matching <user> node. get_user_info populates them from lookup_tc_token_for_jid, gated on the same AB prop.

2. Spam report IQ — missing <tctoken>

WA Web's spam report (OutSpamIndividualReportRequest) merges OutSpamTCTokenMixin (gated on enable_spam_report_iq_with_privacy_token) to attach the reported contact's <tctoken>. SpamReportSpec now takes an optional tctoken and send_spam_report resolves it for request.from_jid.

Already compliant (unchanged)

1:1 messages (tctoken + cstoken), presence subscribe, profile-picture GET, and group create/participant-add already attach the token. The t timestamp attribute is intentionally omitted — tctokenT is never populated anywhere in the captured bundle.

Known follow-up (not in this PR)

VoIP call offers: WA Web's StartCall calls sendTcToken per participant. The VoIP subsystem doesn't yet issue/attach tctokens (already a TODO in wacore/src/iq/tctoken.rs); left for a dedicated change.

Changes

  • wacore/src/iq/usync.rs: UserInfoSpec::with_tc_tokens; per-user <tctoken> in build_iq.
  • wacore/src/iq/spam_report.rs: SpamReportSpec::with_tc_token; <tctoken> child in build_iq.
  • src/features/contacts.rs: get_user_info resolves per-user tctokens under the usync AB prop.
  • src/spam_report.rs: send_spam_report resolves the reported contact's tctoken under the spam AB prop.

Validation

  • cargo clippy -p wacore -p whatsapp-rust --tests clean; cargo fmt --all --check clean.
  • New unit tests: usync attaches/omits per-user tctoken; spam report attaches tctoken when set.
  • cargo test -p wacore --lib and cargo test -p whatsapp-rust --lib (909) green.

Generated by Claude Code

Two remaining WA Web privacy-token paths were missing the trusted-contact
token, so those requests failed for privacy-restricted accounts:

- Usync user-info query: WA Web's USyncStatusProtocol.getUserElement attaches
  a per-user <tctoken> (gated on profile_scraping_privacy_token_in_about_usync)
  so a restricted contact's status/about is returned. UserInfoSpec now carries
  per-user tctokens and get_user_info populates them.
- Spam report IQ: WA Web's OutSpamTCTokenMixin attaches the reported contact's
  <tctoken> (gated on enable_spam_report_iq_with_privacy_token). SpamReportSpec
  now takes an optional tctoken and send_spam_report resolves it.

Both reuse the existing lookup_tc_token_for_jid resolution and are gated by the
same AB props WA Web uses. Presence, profile-picture, 1:1 messages and groups
already attached the token.
@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 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@jlucaso1, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 8 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

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

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b6495e5d-7f46-4c67-8158-d60b5039bbe8

📥 Commits

Reviewing files that changed from the base of the PR and between a7e71ac and e2bfbbe.

📒 Files selected for processing (2)
  • src/features/contacts.rs
  • wacore/src/iq/usync.rs
📝 Walkthrough

Walkthrough

Adds optional trusted-contact token support to SpamReportSpec and UserInfoSpec IQ builders, each gaining a token field, builder method, and conditional <tctoken> node emission. Client code (Contacts::get_user_info, Client::send_spam_report) attaches tokens behind feature flags via lookup_tc_token_for_jid.

Changes

TC Token Privacy Feature

Layer / File(s) Summary
SpamReportSpec tc_token support
wacore/src/iq/spam_report.rs
Adds tc_token: Option<Vec<u8>> field and with_tc_token(...) builder; build_iq conditionally appends a tctoken node; new import and unit test cover the added behavior.
UserInfoSpec tc_tokens support
wacore/src/iq/usync.rs
Adds tc_tokens: HashMap<String, Vec<u8>> field and with_tc_tokens(...) builder; build_iq attaches per-user tctoken nodes keyed by jid.user; new import and unit tests validate presence/absence.
Client-side feature-flagged wiring
src/features/contacts.rs, src/spam_report.rs
get_user_info and send_spam_report conditionally look up TC tokens via lookup_tc_token_for_jid behind feature flags and attach them to the specs before sending IQs.

Estimated code review effort: 2 (Simple) | ~15 minutes

Possibly related PRs

  • oxidezap/whatsapp-rust#187: Introduced the original Client::send_spam_report/SpamReportSpec IQ flow that this PR extends with tctoken support.
  • oxidezap/whatsapp-rust#280: Introduced the tc-token lookup/storage infrastructure this PR relies on for get_user_info and send_spam_report.

Suggested labels: api-design

Look, I need this tctoken flow to work flawlessly — privacy isn't optional, it's the mission. Both SpamReportSpec and UserInfoSpec now carry tokens correctly, gated behind feature flags, tested. That's the kind of disciplined execution I expect. Ship it, but ship it right.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding tctoken handling to usync and spam-report IQs.
Description check ✅ Passed The description directly matches the changeset, explaining the usync and spam-report tctoken fixes and their feature gates.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/whatsapp-rust-token-review-7jsnxz

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.

@greptile-apps

greptile-apps Bot commented Jul 3, 2026

Copy link
Copy Markdown

Greptile Summary

This PR attaches trusted-contact tokens (<tctoken>) to two previously-uncovered outgoing IQ paths — the usync status/about query and the spam-report IQ — both gated on the corresponding AB props, matching WA Web's OutSpamTCTokenMixin and USyncStatusProtocol.getUserElement behavior.

  • wacore/src/iq/usync.rs: UserInfoSpec gains a tc_tokens: HashMap<String, Vec<u8>> field (default empty) and with_tc_tokens builder; build_iq attaches a <tctoken> child to the per-user <user> node when a token is present.
  • wacore/src/iq/spam_report.rs: SpamReportSpec gains an optional tc_token and with_tc_token builder; build_iq appends a <tctoken> child to the IQ's children list.
  • src/features/contacts.rs / src/spam_report.rs: Caller-side wiring resolves tokens via lookup_tc_token_for_jid under their respective AB prop gates, using let-chains and concurrent join_all lookups.

Confidence Score: 5/5

Safe to merge — the token attachment paths are gated behind AB props, token absence is handled gracefully, and both new code paths are covered by unit tests.

The changes are narrowly scoped: two new optional fields on existing IQ specs, both defaulting to empty/None, with callers that short-circuit cleanly when the AB prop is off or no token is stored. The concurrent token lookup in contacts.rs uses join_all awaited immediately in scope, the let-chain in spam_report.rs is idiomatic, and the keying scheme (non-AD JID string) is applied consistently between the caller and build_iq. Tests cover both the attach and omit cases.

No files require special attention.

Important Files Changed

Filename Overview
wacore/src/iq/usync.rs Adds tc_tokens HashMap to UserInfoSpec and attaches per-user tctoken child nodes in build_iq; keying is consistent between caller and builder; new tests cover attach/omit paths.
wacore/src/iq/spam_report.rs Adds optional tc_token field to SpamReportSpec with a builder method; appends tctoken node in build_iq when set; well-tested with byte-content assertion.
src/features/contacts.rs Resolves per-user tc tokens concurrently under PROFILE_SCRAPING_PRIVACY_TOKEN_IN_ABOUT_USYNC AB gate; logic is sound, concurrent join_all pattern correct.
src/spam_report.rs AB-gated tctoken lookup for spam report using let-chains; import placed inside function body rather than at file top.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant C as Client
    participant AB as ABProps
    participant DB as TcTokenStore
    participant WA as WA Server

    Note over C,WA: Usync status/about query
    C->>AB: is_enabled(PROFILE_SCRAPING_PRIVACY_TOKEN_IN_ABOUT_USYNC)
    AB-->>C: true/false
    alt AB prop enabled
        C->>DB: join_all lookup_tc_token_for_jid per JID
        DB-->>C: Map of JID to token bytes
        C->>C: UserInfoSpec with_tc_tokens
    end
    C->>WA: usync IQ with per-user tctoken child nodes
    WA-->>C: status/about result

    Note over C,WA: Spam report IQ
    C->>AB: is_enabled(ENABLE_SPAM_REPORT_IQ_WITH_PRIVACY_TOKEN)
    AB-->>C: true/false
    alt AB prop enabled and from_jid set
        C->>DB: lookup_tc_token_for_jid
        DB-->>C: Option token bytes
        C->>C: SpamReportSpec with_tc_token
    end
    C->>WA: spam IQ with spam_list and optional tctoken
    WA-->>C: SpamReportResult
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 C as Client
    participant AB as ABProps
    participant DB as TcTokenStore
    participant WA as WA Server

    Note over C,WA: Usync status/about query
    C->>AB: is_enabled(PROFILE_SCRAPING_PRIVACY_TOKEN_IN_ABOUT_USYNC)
    AB-->>C: true/false
    alt AB prop enabled
        C->>DB: join_all lookup_tc_token_for_jid per JID
        DB-->>C: Map of JID to token bytes
        C->>C: UserInfoSpec with_tc_tokens
    end
    C->>WA: usync IQ with per-user tctoken child nodes
    WA-->>C: status/about result

    Note over C,WA: Spam report IQ
    C->>AB: is_enabled(ENABLE_SPAM_REPORT_IQ_WITH_PRIVACY_TOKEN)
    AB-->>C: true/false
    alt AB prop enabled and from_jid set
        C->>DB: lookup_tc_token_for_jid
        DB-->>C: Option token bytes
        C->>C: SpamReportSpec with_tc_token
    end
    C->>WA: spam IQ with spam_list and optional tctoken
    WA-->>C: SpamReportResult
Loading

Reviews (2): Last reviewed commit: "Fix per-user tctoken lookup concurrency ..." | Re-trigger Greptile

Comment thread src/features/contacts.rs Outdated

@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/features/contacts.rs`:
- Around line 233-252: The per-JID token fetches inside the `get_user_info` flow
are serialized in a `for` loop, which makes bulk contact sync unnecessarily
slow. Refactor the `lookup_tc_token_for_jid` calls to run concurrently using
`futures::join_all` or an equivalent helper, then build the `tc_tokens` map from
the collected results before calling `spec.with_tc_tokens`. Make sure to verify
the `futures` crate (or an existing equivalent) is already available before
changing the `UserInfoSpec` token attachment logic.
🪄 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: fc2dfad7-d853-4932-bd42-077f3e2b6fc5

📥 Commits

Reviewing files that changed from the base of the PR and between fde503f and a7e71ac.

📒 Files selected for processing (4)
  • src/features/contacts.rs
  • src/spam_report.rs
  • wacore/src/iq/spam_report.rs
  • wacore/src/iq/usync.rs

Comment thread src/features/contacts.rs

@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.

3 issues found across 4 files

Confidence score: 3/5

  • In src/features/contacts.rs within get_user_info, keying the tctoken map by only the JID user can mismatch tokens when PN/LID variants share that user part, which can send the wrong per-user token and return incorrect contact data—key and lookup by full JID (including domain) before merging.
  • Also in src/features/contacts.rs (get_user_info), the new tctoken lookup loop is sequential, so response time grows roughly one round-trip per contact and can noticeably slow large batches—run lookups concurrently (with an optional concurrency limit) to de-risk latency regressions.
  • The std::collections::HashMap qualification in src/features/contacts.rs is a style-only inconsistency and low merge risk; it can be cleaned up to the existing HashMap import for readability.

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

Re-trigger cubic

Comment thread src/features/contacts.rs Outdated
Comment thread src/features/contacts.rs Outdated
Comment thread src/features/contacts.rs Outdated
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

📦 Binary size report

Metric main PR Δ
bin size (stripped) 10.70 MiB 10.70 MiB 0
bin .text 8.72 MiB 8.72 MiB 0
bin allocated (text+data+bss) 10.70 MiB 10.70 MiB 0
llvm-lines wacore 501,968 502,227 +259 (+0.05%) 🔺
llvm-lines wacore copies 17,211 17,218 +7 (+0.04%) 🔺
llvm-lines whatsapp-rust lib 730,809 730,809 0
llvm-lines whatsapp-rust lib copies 23,703 23,703 0
deps crates (Cargo.lock) 466 466 0
.text per crate
Crate main PR Δ
.text whatsapp_rust 1.57 MiB 1.57 MiB 0
.text wacore 531.88 KiB 531.88 KiB 0
.text wacore_binary 157.49 KiB 157.49 KiB 0
.text wacore_libsignal 174.99 KiB 174.99 KiB 0
.text wacore_appstate 156.10 KiB 156.10 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 506.56 KiB 506.56 KiB 0
.text whatsapp_rust_tokio_transport 43.50 KiB 43.50 KiB 0
.text whatsapp_rust_ureq_http_client 9.08 KiB 9.08 KiB 0
.text std 1014.41 KiB 1014.41 KiB 0
.text other deps 2.94 MiB 2.94 MiB 0

Baseline: fde503fe7 (latest main run) · Head: 921a7a68c · Graphs

Look up per-JID tctokens concurrently in get_user_info instead of
sequentially, and key the usync tc_tokens map by the JID's non-ad string
form so PN and LID JIDs sharing a user part no longer collide.

@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 2 files (changes from recent commits).

Requires human review: Implements business logic changes to attach tctoken in usync status/about and spam-report IQs. Such protocol changes require human review despite thorough testing.

Re-trigger cubic

@jlucaso1
jlucaso1 merged commit 0b1d349 into main Jul 3, 2026
19 checks passed
@jlucaso1
jlucaso1 deleted the claude/whatsapp-rust-token-review-7jsnxz branch July 3, 2026 21:59
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