• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mozilla / blurts-server / #12333

pending completion
#12333

push

circleci

web-flow
Merge pull request #2838 from mozilla/MNTOR-1173-Settings-email-breaches-count

Fixes email breaches count on settings page

282 of 1371 branches covered (20.57%)

Branch coverage included in aggregate %.

5 of 5 new or added lines in 3 files covered. (100.0%)

959 of 3720 relevant lines covered (25.78%)

2.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/utils/breaches.js
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4

5
import { getUserEmails } from '../db/tables/email_addresses.js'
6
import { getBreachesForEmail, getFilteredBreaches } from './hibp.js'
7
import { getSha1 } from './fxa.js'
8
import { filterBreachDataTypes } from './breach-resolution.js'
9

10
/**
11
 * TODO: deprecate
12
 * Get all emails and breaches for a user via app.locals
13
 * This function will be replaced after 'breaches" table is created
14
 * and all records can be retrieved from the one table
15
 * @param {*} user
16
 * @param {*} allBreaches
17
 * @returns
18
 */
19
async function getAllEmailsAndBreaches (user, allBreaches) {
20
  const monitoredEmails = await getUserEmails(user.id)
×
21
  const verifiedEmails = []
×
22
  const unverifiedEmails = []
×
23
  verifiedEmails.push(await bundleVerifiedEmails({ user, email: user.primary_email, recordId: user.id, recordVerified: user.primary_verified, allBreaches }))
×
24
  for (const email of monitoredEmails) {
×
25
    if (email.verified) {
×
26
      verifiedEmails.push(await bundleVerifiedEmails({ user, email: email.email, recordId: email.id, recordVerified: email.verified, allBreaches }))
×
27
    } else {
28
      unverifiedEmails.push(email)
×
29
    }
30
  }
31

32
  // get new breaches since last shown
33
  for (const emailEntry of verifiedEmails) {
×
34
    const newBreachesForEmail = emailEntry.breaches.filter(breach => breach.AddedDate >= user.breaches_last_shown)
×
35

36
    for (const newBreachForEmail of newBreachesForEmail) {
×
37
      newBreachForEmail.NewBreach = true // add "NewBreach" property to the new breach.
×
38
      emailEntry.hasNewBreaches = newBreachesForEmail.length // add the number of new breaches to the email
×
39
    }
40
  }
41

42
  return { verifiedEmails, unverifiedEmails }
×
43
}
44

45
function addRecencyIndex (foundBreaches) {
46
  const annotatedBreaches = []
×
47
  // slice() the array to make a copy so before reversing so we don't
48
  // reverse foundBreaches in-place
49
  const oldestToNewestFoundBreaches = foundBreaches.slice().reverse()
×
50
  oldestToNewestFoundBreaches.forEach((annotatingBreach, index) => {
×
51
    const foundBreach = foundBreaches.find(foundBreach => foundBreach.Name === annotatingBreach.Name)
×
52
    annotatedBreaches.push(Object.assign({ recencyIndex: index }, foundBreach))
×
53
  })
54
  return annotatedBreaches.reverse()
×
55
}
56

57
async function bundleVerifiedEmails (options) {
58
  const { user, email, recordId, recordVerified, allBreaches } = options
×
59
  const lowerCaseEmailSha = getSha1(email.toLowerCase())
×
60

61
  // find all breaches relevant to the current email
62
  const foundBreaches = await getBreachesForEmail(lowerCaseEmailSha, allBreaches, true, false)
×
63

64
  // TODO: remove after migration MNTOR-978
65
  // adding index to breaches based on recency
66
  const foundBreachesWithRecency = addRecencyIndex(foundBreaches)
×
67

68
  // get v2 "breach_resolution" object
69
  const breachResolutionV2 = user.breach_resolution
×
70
    ? user.breach_resolution[email] ? user.breach_resolution[email] : {}
×
71
    : []
72

73
  const useBreachId = user.breach_resolution?.useBreachId
×
74

75
  for (const breach of foundBreachesWithRecency) {
×
76
    // if breach resolution json has `useBreachId` boolean, that means the migration has taken place
77
    // we will use breach id as the key. Otherwise, we fallback to using recency index for backwards compatibility
78
    if (useBreachId) {
×
79
      breach.IsResolved = breachResolutionV2[breach.Id]?.isResolved
×
80
      breach.ResolutionsChecked = breachResolutionV2[breach.Id]?.resolutionsChecked || []
×
81
    } else {
82
      // TODO: remove after MNTOR-978
83
      breach.IsResolved = breachResolutionV2[breach.recencyIndex]?.isResolved
×
84
      breach.ResolutionsChecked = breachResolutionV2[breach.recencyIndex]?.resolutionsChecked || []
×
85
    }
86

87
    // filter breach types based on the 13 types we care about
88
    breach.DataClasses = filterBreachDataTypes(breach.DataClasses)
×
89
  }
90

91
  // filter out irrelevant breaches based on HIBP
92
  const filteredAnnotatedFoundBreaches = getFilteredBreaches(foundBreachesWithRecency)
×
93

94
  const emailEntry = {
×
95
    email,
96
    breaches: filteredAnnotatedFoundBreaches,
97
    primary: email === user.primary_email,
98
    id: recordId,
99
    verified: recordVerified
100
  }
101

102
  return emailEntry
×
103
}
104

105
export { getAllEmailsAndBreaches }
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc