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

mozilla / blurts-server / #12078

pending completion
#12078

push

circleci

web-flow
Merge pull request #2798 from mozilla/MNTOR-978-2

MNTOR-978: user.breach_resolution can be null

282 of 1227 branches covered (22.98%)

Branch coverage included in aggregate %.

1 of 1 new or added line in 1 file covered. (100.0%)

959 of 3296 relevant lines covered (29.1%)

2.36 hits per line

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

0.0
/src/utils/breaches.js
1
import { getUserEmails } from '../db/tables/email_addresses.js'
2
import { getBreachesForEmail, filterBreaches } from './hibp.js'
3
import { getSha1 } from './fxa.js'
4
import { filterBreachDataTypes } from './breach-resolution.js'
5

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

28
  // get new breaches since last shown
29
  for (const emailEntry of verifiedEmails) {
×
30
    const newBreachesForEmail = emailEntry.breaches.filter(breach => breach.AddedDate >= user.breaches_last_shown)
×
31

32
    for (const newBreachForEmail of newBreachesForEmail) {
×
33
      newBreachForEmail.NewBreach = true // add "NewBreach" property to the new breach.
×
34
      emailEntry.hasNewBreaches = newBreachesForEmail.length // add the number of new breaches to the email
×
35
    }
36
  }
37

38
  return { verifiedEmails, unverifiedEmails }
×
39
}
40

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

53
async function bundleVerifiedEmails (options) {
54
  const { user, email, recordId, recordVerified, allBreaches } = options
×
55
  const lowerCaseEmailSha = getSha1(email.toLowerCase())
×
56

57
  // find all breaches relevant to the current email
58
  const foundBreaches = await getBreachesForEmail(lowerCaseEmailSha, allBreaches, true, false)
×
59

60
  // TODO: remove after migration MNTOR-978
61
  // adding index to breaches based on recency
62
  const foundBreachesWithRecency = addRecencyIndex(foundBreaches)
×
63

64
  // get v2 "breach_resolution" object
65
  const breachResolutionV2 = user.breach_resolution
×
66
    ? user.breach_resolution[email] ? user.breach_resolution[email] : {}
×
67
    : []
68

69
  const useBreachId = user.breach_resolution?.useBreachId
×
70

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

83
    // filter breach types based on the 13 types we care about
84
    breach.DataClasses = filterBreachDataTypes(breach.DataClasses)
×
85
  }
86

87
  // filter out irrelevant breaches based on HIBP
88
  const filteredAnnotatedFoundBreaches = filterBreaches(foundBreachesWithRecency)
×
89

90
  const emailEntry = {
×
91
    email,
92
    breaches: filteredAnnotatedFoundBreaches,
93
    primary: email === user.primary_email,
94
    id: recordId,
95
    verified: recordVerified
96
  }
97

98
  return emailEntry
×
99
}
100

101
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