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

mozilla / blurts-server / #11898

pending completion
#11898

push

circleci

web-flow
Merge pull request #2770 from mozilla/license

Add license headers in source files

282 of 1138 branches covered (24.78%)

Branch coverage included in aggregate %.

959 of 3049 relevant lines covered (31.45%)

2.55 hits per line

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

0.0
/src/controllers/hibp.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 AppConstants from '../app-constants.js'
6
import { getBreachByName, loadBreachesIntoApp } from '../utils/hibp.js'
7
import mozlog from '../utils/log.js'
8
const log = mozlog('controllers.hibp')
×
9

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

29
  const { breachName } = req.body
×
30

31
  //   const reqHashPrefix = req.body.hashPrefix.toLowerCase()
32
  let breachAlert = getBreachByName(req.app.locals.breaches, breachName)
×
33

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

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

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

60
  //   const utmID = 'breach-alert'
61
  //   const notifiedRecipients = []
62

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

71
  //     const requestedLanguage = signupLanguage ? acceptedLanguages(signupLanguage) : ''
72
  //     const supportedLocales = negotiateLanguages(
73
  //       requestedLanguage,
74
  //       req.app.locals.AVAILABLE_LANGUAGES,
75
  //       { defaultLocale: 'en' }
76
  //     )
77

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

107
  // TODO: remove after email
108
  res.status(200).json({
×
109
    info: 'Not yet... but will notify subscribers of breach when email is fixed'
110
  })
111
}
112

113
export {
114
  notify
115
}
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