Skip to content

Conversation

@jo
Copy link
Contributor

@jo jo commented Jan 12, 2026

Move the previously introduced login breach-related fields time_of_last_breach and time_last_breach_alert_dismissed from LoginFields into LoginMeta struct, to group internally handled fields which are not directly updateable.
Add breachesL table (schema v4) to track breached passwords and enable cross-domain password reuse detection.

New APIs:

  • are_potentially_vulnerable_passwords(ids) - Batch check, returns GUIDs
  • is_potentially_vulnerable_password(id) - Single login check

Batch API decrypts breachesL once and uses HashSet for efficient lookups (O(M + N) vs O(M * N) for repeated single checks).

This is the Phabricator patch for Desktop support.

Pull Request checklist

  • Breaking changes: This PR follows our breaking change policy
    • This PR follows the breaking change policy:
      • This PR has no breaking API changes, or
      • There are corresponding PRs for our consumer applications that resolve the breaking changes and have been approved
  • Quality: This PR builds and tests run cleanly
    • Note:
      • For changes that need extra cross-platform testing, consider adding [ci full] to the PR title.
      • If this pull request includes a breaking change, consider cutting a new release after merging.
  • Tests: This PR includes thorough tests or an explanation of why it does not
  • Changelog: This PR includes a changelog entry in CHANGELOG.md or an explanation of why it does not need one
    • Any breaking changes to Swift or Kotlin binding APIs are noted explicitly
  • Dependencies: This PR follows our dependency management guidelines
    • Any new dependencies are accompanied by a summary of the due diligence applied in selecting them.

@jo jo force-pushed the breach-alert-meta branch 9 times, most recently from b3c471f to 03586ff Compare January 19, 2026 20:21
@jo jo changed the title Move login breach fields into meta record Move login breach fields into meta record and add password reuse detection for breach alerts Jan 19, 2026
@jo jo requested a review from bendk January 19, 2026 20:29
@jo jo force-pushed the breach-alert-meta branch 2 times, most recently from 0fe7fa6 to 0564fe4 Compare January 26, 2026 16:27
Copy link
Contributor

@bendk bendk left a comment

Choose a reason for hiding this comment

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

Looks good, just minor comments.

pub fn are_potentially_vulnerable_passwords(&self, ids: Vec<String>) -> ApiResult<Vec<String>> {
// Note: Vec<&str> is not supported with UDL, so we receive Vec<String> and convert
let db = self.lock_db()?;
let ids: Vec<&str> = ids.iter().map(|id| &**id).collect();
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you could eliminate the mapping by having the method input &[String]. I'm not sure if it's worth it or not, just wanted to point that out.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd like that (also for delete_many), but how does this work with uniffi, esp. what do I need to specify instead of sequence<string>? If I change above, I get a

error[E0308]: mismatched types
   --> /home/jo/moz/application-services/target/debug/build/logins-608812cf418f2f0d/out/logins.uniffi.rs:363:1
    |
363 | #[::uniffi::export_for_udl]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&[String]`, found `Vec<String>`
364 | impl r#LoginStore {
365 |     pub fn r#delete_many(
    |            ------------- arguments to this method are incorrect
    |
    = note: expected reference `&[std::string::String]`
                  found struct `Vec<std::string::String>`

Copy link
Contributor

Choose a reason for hiding this comment

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

I thought that &ids would work, but I'm not totally sure. I'd try that and if it doesn't work stay with the code you have.

)?),

3 => Ok(db.execute_batch(CREATE_LOCAL_BREACHES_TABLE_SQL)?),

Copy link
Contributor

Choose a reason for hiding this comment

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

I would expect this to populate the breachesL table. Maybe you can skip it since the feature hasn't been used in the wild? If so, please add a comment explaining that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't quite understand 🤔

It doesn't populate the table, it creates it. This changes introduces a new database version, so the table has to be created first, right?

The vulnerable passwords feature already exists on the desktop (this table is stored in a parallel structure within logins.json, in .potentiallyVulnerablePasswords), and in the course of migrating to AS logins, I will also need this feature in AS logins.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes you definitely need to create the table.

I think you might also want to populate the table by iterating through the loginsL table and finding rows that have been flagged as breached. It seems like there's an invariant that's not being upheld. For example:

  • Login A is flagged as breached on desktop
  • Login A is synced
  • This update runs
  • Login B is created on mobile with the same password
  • Would is_potentially_vulnerable_password return true for login B?

Also, I'm now seeing that this isn't hooked up to syncing logic, so maybe the same thing would happen if the update ran before the first step.

Is that analysis correct? If so it seems like we'll need to change the approach somehow. Sorry I missed this in the first review.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This PR prepares AS logins to store the vulnerable password data only on desktop. This list of breaches is ingested regularly from remote settings and consumed by the desktop itself, and is not synchronized (yet). The list of vulnerable passwords (including historical data) is also maintained by desktop. So the separate table breachesL is intentional, as we need to track historically compromised passwords even after a change by the user.

@jo jo force-pushed the breach-alert-meta branch from 0564fe4 to c25ef2c Compare January 26, 2026 17:39
@bendk bendk self-requested a review January 26, 2026 18:26
Copy link
Contributor

@bendk bendk left a comment

Choose a reason for hiding this comment

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

I'm going to re-flag this as requested changes based on the issues raised in my last comment.

jo added 2 commits January 27, 2026 16:25
Move the previously introduced login breach-related fields
`time_of_last_breach` and `time_last_breach_alert_dismissed` from
LoginFields into LoginMeta struct, to group internally handled fields
which are not directly updateable.
Add breachesL table (schema v4) to track breached passwords and enable
cross-domain password reuse detection.

New APIs:
- are_potentially_vulnerable_passwords(ids) - Batch check, returns GUIDs
- is_potentially_vulnerable_password(id) - Single login check

Batch API decrypts breachesL once and uses HashSet for efficient lookups
(O(M + N) vs O(M * N) for repeated single checks).
@jo jo force-pushed the breach-alert-meta branch from c25ef2c to bf14c22 Compare January 27, 2026 15:25
Copy link
Contributor

@bendk bendk left a comment

Choose a reason for hiding this comment

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

Thanks for the explanation, I think this is all ready to land.

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