Skip to content

fix(history-sync): match our own pushname entry by JID user - #1203

Merged
jlucaso1 merged 1 commit into
oxidezap:mainfrom
blaueeiner:fix/own-pushname-jid-id
Aug 4, 2026
Merged

fix(history-sync): match our own pushname entry by JID user#1203
jlucaso1 merged 1 commit into
oxidezap:mainfrom
blaueeiner:fix/own-pushname-jid-id

Conversation

@blaueeiner

Copy link
Copy Markdown
Contributor

The bug

HistorySync.pushnames[].id is a JID string (15551234567@s.whatsapp.net). extract_own_pushname compares it verbatim against own_user, which is the bare user part of our own JID (process_history_sync(compressed, device.pn.to_non_ad().user, …) in src/history_sync.rs).

The two can never be equal, so own_pushname is always None and history sync cannot supply our own push name.

whatsmeow parses the same field with types.ParseJID(pushname.GetID()) before comparing, which is the behaviour this restores.

Why it matters

For a personal account the push name has exactly one other automatic source: the setting_pushName mutation in the critical_block app-state collection (a business account also gets it from business_name at pairing). When that collection fails to deliver it, history sync is the only remaining path — and it was dead.

Without a push name the client cannot send presence at all (features/presence.rs returns PresenceError::PushNameEmpty), so the account sits connected and unable to announce itself.

The test that hid it

test_nct_salt_and_pushname_coexist passed because it built id: Some("0000000000") — a bare user, which the server never sends. It now uses a JID, and two cases are added: a JID id and the bare form both match, and another user's entry is not taken as our own.

Verification

cargo test -p wacore --lib (1337 passed), cargo clippy -p wacore --all-targets -- -D warnings, cargo fmt --all --check — all clean. Fixtures use fictitious numbers per AGENTS.md.

Known limitation, deliberately not addressed here

The comparison is against the PN user only. For a LID-migrated account whose entry is keyed by the LID user this still won't match; fixing that means threading the own LID through process_history_sync, which changes the signature and felt out of scope for a comparison fix.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved pushname matching across JID-formatted and plain user IDs.
    • Added support for IDs with server and device suffixes.
    • Prevented pushnames belonging to other users from being incorrectly matched.
    • Rejected unsupported identifier namespaces to avoid incorrect pushname associations.
    • Improved compatibility with legacy user ID formats.

Walkthrough

Pushname matching now normalizes bare IDs and phone JIDs, removes device suffixes, and rejects non-phone namespaces. Tests cover owner matches, another user’s ID, and rejected namespaces.

Changes

Pushname matching

Layer / File(s) Summary
Normalize and validate pushname IDs
wacore/src/history_sync.rs
Pushname matching compares normalized user portions of IDs. Tests cover phone JIDs, bare IDs, device suffixes, another user’s ID, and non-phone namespaces.

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

Possibly related PRs

Suggested reviewers: jlucaso1

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the history-sync pushname matching fix and states the primary change.
Description check ✅ Passed The description directly explains the bug, implementation, tests, verification, and known limitation.
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

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 requested a review from jlucaso1 August 3, 2026 10:35
@greptile-apps

greptile-apps Bot commented Aug 3, 2026

Copy link
Copy Markdown

Greptile Summary

Fixes history-sync push-name matching.

  • Parses JID-form push-name IDs and compares their user component with the account’s bare PN user.
  • Restricts matches to phone-number namespaces while retaining legacy bare-user and c.us support.
  • Adds coverage for JID variants, device suffixes, other users, and non-phone namespaces.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
wacore/src/history_sync.rs Correctly normalizes phone-namespace push-name JIDs for comparison and adds focused regression coverage.

Reviews (3): Last reviewed commit: "fix(history-sync): match our own pushnam..." | Re-trigger Greptile

greptile-apps[bot]
greptile-apps Bot previously approved these changes Aug 3, 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 `@wacore/src/history_sync.rs`:
- Around line 764-768: Update pushname_id_is to validate non-bare IDs through
the repository JID helper and require the supported phone namespace
s.whatsapp.net before comparing the user portion; preserve the existing bare-ID
compatibility path and device-suffix handling. Add tests covering rejection of
`@lid` and an invalid namespace while retaining acceptance of valid bare and
phone-namespace IDs.
🪄 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: 0a38344b-a9d2-4036-8c47-59ada5541f84

📥 Commits

Reviewing files that changed from the base of the PR and between 13fce2f and 27de4f8.

📒 Files selected for processing (1)
  • wacore/src/history_sync.rs

Comment thread wacore/src/history_sync.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.

No issues found across 1 file

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Auto-approved: Focused bug fix: history-sync pushname IDs are JIDs, so the prior bare-user comparison dead-coded own_pushname; the diff compares on the user part and pins JID, bare-form, and foreign-entry cases with tests.

Re-trigger cubic

@jlucaso1 jlucaso1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for this one, and good to have you back after #1010.

I went and checked the server side before reading the diff. In the current WA Web bundle the history sync pushname handler does t.pushnames.forEach(e => WidFactory.createUserWidOrThrow(e.id || "")), so Pushname.id really is a WID and never a bare user. whatsmeow agrees, handleHistoricalPushNames parses it with types.ParseJID. Your premise holds, the old comparison could never match. I also ran the suite on your branch, 1337 passing.

Two things I would like changed before this goes in, both small:

  1. Use Jid instead of splitting by hand. extract_lid_mapping right below (the one you added in #1010) already does parse::<Jid>() and reads user_base(), and user_base() is exactly the split_once(':') your helper reimplements. Going through the same path keeps the two neighbours consistent and drops the manual string work.

  2. Reject the wrong namespace. Right now the server part is discarded entirely, so 15550000000@lid matches own_user = "15550000000". The odds are tiny, but it is a silent identity mixup and it costs nothing to avoid: once you have a Jid, requiring is_pn() || server == Server::Legacy is the same check extract_lid_mapping already makes. No perf worry here, I checked that no allocation guard covers this path and it runs once per entry, not per message.

Two optional ones:

  • parity_fixture still builds id: Some(own.to_string()) in the bare form, so the streaming vs full buffer parity test never sees a real JID. Would be nice to move it over too, it is the same unrealistic fixture your PR body calls out.
  • whatsmeow skips entries whose pushname is -. I looked for that sentinel in the bundle and did not find it, so it is not a WA Web rule, but if the server ever sends it for our own entry we would store - and announce it. Your call.

Good find, and the "the test that hid it" section made this very easy to review.

For what it is worth, the red Semver Checks (informational) is not yours. It is a pre existing waproto diff against the published 0.6.0 and the job says outright that it does not block the PR.

@blaueeiner
blaueeiner force-pushed the fix/own-pushname-jid-id branch from 27de4f8 to 40e9242 Compare August 3, 2026 12:24
@greptile-apps
greptile-apps Bot dismissed their stale review August 3, 2026 12:24

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

@blaueeiner

Copy link
Copy Markdown
Contributor Author

Fixed in 40e9242.

The namespace check is a good catch, and it is not only a hardening detail — for a LID-migrated account the same digits can appear as both …@s.whatsapp.net and …@lid, and own_user is the PN user, so a bare user-part comparison could have taken a LID entry's name as ours. Only the phone namespace can carry our PN user, so any other server is now rejected rather than compared:

let Some((user, server)) = id.split_once('@') else {
    return id == own_user;  // pre-JID bare form, still accepted
};
server == wacore_binary::DEFAULT_USER_SERVER
    && user.split(':').next().unwrap_or(user) == own_user

Added a_non_phone_namespace_is_not_our_entry, covering @lid, @c.us and @newsletter; the existing cases for a phone-namespace JID and the bare form still pass.

I used split_once plus the DEFAULT_USER_SERVER constant rather than a full Jid parse: this runs per entry inside the protobuf byte-walk, the only thing needed is the server and user, and a parse would allocate for a value thrown away on every non-matching entry.

cargo test -p wacore --lib (1338 passed), cargo clippy -p wacore --all-targets -- -D warnings, cargo fmt --all --check — clean.

@coderabbitai
coderabbitai Bot requested a review from jlucaso1 August 3, 2026 12:25
greptile-apps[bot]
greptile-apps Bot previously approved these changes Aug 3, 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 `@wacore/src/history_sync.rs`:
- Around line 774-777: Update the JID matching logic in
own_pushname_matches_a_jid_id to normalize a trailing numeric .device suffix for
s.whatsapp.net IDs, alongside the existing colon-suffix normalization. Confirm
the legacy dotted PN wire format from repository WhatsApp Web references, strip
only the valid numeric device suffix, and add the normalized ID to
own_pushname_matches_a_jid_id so dotted device JIDs match the base own user.
🪄 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: 8474ee11-5da6-48c2-b306-d3cf81547b4c

📥 Commits

Reviewing files that changed from the base of the PR and between 27de4f8 and 40e9242.

📒 Files selected for processing (1)
  • wacore/src/history_sync.rs

Comment thread wacore/src/history_sync.rs Outdated

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 40e9242684

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread wacore/src/history_sync.rs Outdated
};
// A device suffix (`user:3`) never appears on this field, but stripping it
// costs nothing and keeps the comparison total.
server == wacore_binary::DEFAULT_USER_SERVER

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Accept legacy @c.us pushname IDs

When a history-sync pushname entry is encoded as the legacy PN JID form (<own>@c.us), this check rejects it because only s.whatsapp.net is allowed. The same module still treats @c.us as the legacy phone-number namespace for history-sync mappings, so those blobs would continue to leave own_pushname unset even though the user part matches; include the legacy PN server in this comparison rather than pinning it as a non-phone namespace.

Useful? React with 👍 / 👎.

let end = pos.checked_add(len).filter(|&e| e <= data.len())?;
let id = smoothutf8::from_utf8(data.get(pos..end)?)?;
id_match = id == own_user;
id_match = pushname_id_is(id, own_user);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Skip missing pushname sentinels

When this newly matches real JID-form pushname entries, an entry whose pushname is "-" now flows through as own_pushname and src/history_sync.rs persists it as the device push name. That value is the history-sync missing-name sentinel (whatsmeow skips those entries), but it is non-empty here, so the presence gate can announce name="-" instead of keeping the push name unset; filter the sentinel before accepting the match.

Useful? React with 👍 / 👎.

`HistorySync.pushnames[].id` is a JID string, `own_user` is the bare user
part of our JID, and the two were compared verbatim — so the entry never
matched and history sync could not supply our own push name. For an account
whose critical app-state sync fails to deliver `setting_pushName` that is the
only remaining automatic source, and it was dead.

Compare on the user part. The unit test that covered this passed because it
built `id` as a bare user, which the server never sends; it now uses a JID
and a second case pins that another user's entry is not taken as our own.
@blaueeiner
blaueeiner force-pushed the fix/own-pushname-jid-id branch from 40e9242 to 2760359 Compare August 3, 2026 12:43
@greptile-apps
greptile-apps Bot dismissed their stale review August 3, 2026 12:43

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

@blaueeiner

Copy link
Copy Markdown
Contributor Author

Both parser findings were right, and the second one was a bug I introduced in the previous round. Fixed in 2760359.

@c.us is the legacy spelling of the phone namespace, not a different one. jid.rs:366 maps it to Server::Legacy and the parser's own test uses ("12345.6@c.us", "12345", 6, 0) as a PN with a device. My previous commit rejected it and asserted that rejection in a test — so I had encoded the wrong behaviour and pinned it. That test now lists @lid, @newsletter and @g.us, with a comment saying explicitly why @c.us is not among them.

The dotted device suffix is real too. jid.rs:115 — "s.whatsapp.net has no agent in the string form; a trailing dotted number is the legacy device spelling" — so 15550000000.3@s.whatsapp.net has user 15550000000, and my split(':') left it as 15550000000.3.

Rather than patch two more cases by hand I switched to the repository parser, which is what the first review round asked for:

let Some(jid) = wacore_binary::jid::parse_jid_ref(id) else { return false };
(jid.server.is_pn_family() || jid.server == Server::Legacy) && &*jid.user == own_user

I had declined that on allocation grounds. That was wrong on the facts: parse_jid_ref borrows, so nothing is allocated for the entries we discard — and two hand-rolled bugs in two rounds is the argument for using the thing that already knows the wire forms.

own_pushname_matches_a_jid_id now covers @s.whatsapp.net, the bare form, @c.us, :3, .3@s.whatsapp.net and .3@c.us. The two new spellings both fail against the previous implementation by inspection (@c.us fails the server equality, .3 leaves the user as 15550000000.3).

cargo test -p wacore --lib (1338 passed), cargo clippy -p wacore --all-targets -- -D warnings, cargo fmt --all --check — clean.

The "-" sentinel I have not implemented, and I would rather ask than guess. The claim is that a pushname of "-" is history sync's missing-name marker and whatsmeow skips it. I can't check it: there is no "-" handling anywhere in this repo to follow as precedent, and I have neither the WA Web bundle nor whatsmeow locally, so I would be adding behaviour on an assertion I cannot verify. The risk is symmetric — accept it and an account can announce name="-"; skip it and an account whose display name really is - never gets one — so it wants your reading rather than my guess. Happy to add the skip in this PR if you confirm the sentinel, or leave it out.

@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 `@wacore/src/history_sync.rs`:
- Line 805: Update the matching phone-JID path around pushname assignment,
including the corresponding logic at the other reported location, to treat
pushname "-" as absent and set own_pushname to None instead of Some("-"). Add a
regression case for a matching phone JID whose pushname is "-" and assert that
the result is None.
🪄 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: 45ab3e50-3aad-46cc-9821-713ce4241b9c

📥 Commits

Reviewing files that changed from the base of the PR and between 40e9242 and 2760359.

📒 Files selected for processing (1)
  • wacore/src/history_sync.rs

let end = pos.checked_add(len).filter(|&e| e <= data.len())?;
let id = smoothutf8::from_utf8(data.get(pos..end)?)?;
id_match = id == own_user;
id_match = pushname_id_is(id, own_user);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Treat the "-" pushname sentinel as absent.

When a matching phone JID has pushname == "-", this path returns Some("-") as the account name. Treat the sentinel as no pushname before setting own_pushname. Add a matching phone-JID regression case that asserts None.

Also applies to: 3083-3108

🤖 Prompt for 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.

In `@wacore/src/history_sync.rs` at line 805, Update the matching phone-JID path
around pushname assignment, including the corresponding logic at the other
reported location, to treat pushname "-" as absent and set own_pushname to None
instead of Some("-"). Add a regression case for a matching phone JID whose
pushname is "-" and assert that the result is None.

@Salientekill

Copy link
Copy Markdown
Contributor

Please review the reviews and check the fact-checks; one review failed.

@jlucaso1
jlucaso1 merged commit e6adf78 into oxidezap:main Aug 4, 2026
26 of 27 checks passed
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.

3 participants