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

mozilla / blurts-server / 626ef5b8-5a47-4baa-83a1-b284f02b4dbb

pending completion
626ef5b8-5a47-4baa-83a1-b284f02b4dbb

push

circleci

GitHub
Merge pull request #2735 from mozilla/MNTOR-976

278 of 1106 branches covered (25.14%)

Branch coverage included in aggregate %.

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

948 of 2981 relevant lines covered (31.8%)

5.1 hits per line

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

0.0
/src/controllers/hibp.js
1
import AppConstants from '../app-constants.js'
2
import { getBreachByName, loadBreachesIntoApp } from '../utils/hibp.js'
3
import mozlog from '../utils/log.js'
4
const log = mozlog('controllers.hibp')
×
5

6
/**
7
 * Whenever a breach is detected on the HIBP side, HIBP sends a request to this endpoint.
8
 * This function attempts to retrieve the breach info from the local cache, if not found
9
 * it retrieves it from the database
10
 * A breach notification contains the following parameters:
11
 * - breachName
12
 * - hashPrefix
13
 * - hashSuffixes
14
 * More about how account identities are anonymized: https://blog.mozilla.org/security/2018/06/25/scanning-breached-accounts-k-anonymity/
15
 */
16
async function notify (req, res) {
17
  if (!req.token || req.token !== AppConstants.HIBP_NOTIFY_TOKEN) {
×
18
    const errorMessage = 'HIBP notify endpoint requires valid authorization token.'
×
19
    throw new Error(errorMessage)
×
20
  }
21
  if (!['breachName', 'hashPrefix', 'hashSuffixes'].every(req.body?.hasOwnProperty, req.body)) {
×
22
    throw new Error('HIBP breach notification: requires breachName, hashPrefix, and hashSuffixes.')
×
23
  }
24

25
  const { breachName } = req.body
×
26

27
  //   const reqHashPrefix = req.body.hashPrefix.toLowerCase()
28
  let breachAlert = getBreachByName(req.app.locals.breaches, breachName)
×
29

30
  if (!breachAlert) {
×
31
    // If breach isn't found, try to reload breaches from HIBP
32
    log.debug('notify', 'Breach is not found, reloading breaches...')
×
33
    await loadBreachesIntoApp(req.app)
×
34
    breachAlert = getBreachByName(req.app.locals.breaches, breachName)
×
35
    if (!breachAlert) {
×
36
      // If breach *still* isn't found, we have a real error
37
      throw new Error('Unrecognized breach: ' + breachName)
×
38
    }
39
  }
40

41
  if (breachAlert.IsSpamList || breachAlert.IsFabricated || !breachAlert.IsVerified || breachAlert.Domain === '') {
×
42
    log.info(`${breachAlert.Name} is fabricated, a spam list, not associated with a website, or unverified. \n Breach Alert not sent.`)
×
43
    return res.status(200).json(
×
44
      { info: 'Breach loaded into database. Subscribers not notified.' }
45
    )
46
  }
47
  // TODO: send email notification to affected subscribers after loading new breaches
48
  // This part will be completed as a part of the email epic
49

50
  //   const hashes = req.body.hashSuffixes.map(suffix => reqHashPrefix + suffix.toLowerCase())
51
  //   const subscribers = await getSubscribersByHashes(hashes)
52
  //   const emailAddresses = await getEmailAddressesByHashes(hashes)
53
  //   const recipients = subscribers.concat(emailAddresses)
54
  //   log.info('notification', { length: recipients.length, breachAlertName: breachAlert.Name })
55

56
  //   const utmID = 'breach-alert'
57
  //   const notifiedRecipients = []
58

59
  //   for (const recipient of recipients) {
60
  //     log.info('notify', { recipient })
61
  //     // Get subscriber ID from "subscriber_id" property (if email_addresses record)
62
  //     // or from "id" property (if subscribers record)
63
  //     const subscriberId = recipient.subscriber_id || recipient.id
64
  //     const { recipientEmail, breachedEmail, signupLanguage } = getAddressesAndLanguageForEmail(recipient)
65
  //     const ctaHref = EmailUtils.getEmailCtaHref(utmID, 'dashboard-cta', subscriberId)
66

67
  //     const requestedLanguage = signupLanguage ? acceptedLanguages(signupLanguage) : ''
68
  //     const supportedLocales = negotiateLanguages(
69
  //       requestedLanguage,
70
  //       req.app.locals.AVAILABLE_LANGUAGES,
71
  //       { defaultLocale: 'en' }
72
  //     )
73

74
  //     const subject = LocaleUtils.fluentFormat(supportedLocales, 'breach-alert-subject')
75
  //     const heading = LocaleUtils.fluentFormat(supportedLocales, 'email-spotted-new-breach')
76
  //     const template = 'email-2022'
77
  //     if (!notifiedRecipients.includes(breachedEmail)) {
78
  //       await EmailUtils.sendEmail(
79
  //         recipientEmail, subject, template,
80
  //         {
81
  //           breachedEmail,
82
  //           recipientEmail,
83
  //           subscriberId,
84
  //           supportedLocales,
85
  //           breachAlert,
86
  //           SERVER_URL: AppConstants.SERVER_URL,
87
  //           unsubscribeUrl: EmailUtils.getUnsubscribeUrl(recipient, utmID),
88
  //           ctaHref,
89
  //           utmCampaign: utmID,
90
  //           whichPartial: 'email_partials/alert',
91
  //           heading
92
  //         }
93
  //       )
94
  //       notifiedRecipients.push(breachedEmail)
95
  //     }
96
  //   }
97
  //   log.info('notified', { length: notifiedRecipients.length })
98
  //   res.status(200)
99
  //   res.json(
100
  //     { info: 'Notified subscribers of breach.' }
101
  //   )
102

103
  // TODO: remove after email
104
  res.status(200).json({
×
105
    info: 'Not yet... but will notify subscribers of breach when email is fixed'
106
  })
107
}
108

109
export {
110
  notify
111
}
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