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

mozilla / blurts-server / 3301e8fe-9868-476c-990a-ab44e51f4a2d

pending completion
3301e8fe-9868-476c-990a-ab44e51f4a2d

push

circleci

GitHub
Merge pull request #3001 from mozilla/main

282 of 1768 branches covered (15.95%)

Branch coverage included in aggregate %.

451 of 451 new or added lines in 56 files covered. (100.0%)

959 of 4670 relevant lines covered (20.54%)

3.44 hits per line

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

0.0
/src/controllers/requestBreachScan.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 { getMessage } from '../utils/fluent.js'
6
import { getBreachLogo } from '../utils/breachLogo.js'
7
import { UserInputError } from '../utils/error.js'
8
import { getSha1 } from '../utils/fxa.js'
9
import { getBreachesForEmail } from '../utils/hibp.js'
10
import { validateEmailAddress } from '../utils/emailAddress.js'
11

12
/**
13
 * @typedef {{ success: false }} RequestBreachScanErrorResponse
14
 * @typedef {{ success: true, breaches: Array<unknown>, heading: string, logos: string[], dataClassStrings: string[][], total: number }} RequestBreachScanSuccessResponse
15
 * @typedef {RequestBreachScanErrorResponse | RequestBreachScanSuccessResponse} RequestBreachScanResponse
16
 */
17

18
/** @type {import('express').RequestHandler<unknown, RequestBreachScanResponse>} */
19
async function requestBreachScan (req, res, next) {
20
  if (req.method !== 'POST' || typeof req.body?.email !== 'string') {
×
21
    return next()
×
22
  }
23

24
  const validatedEmail = validateEmailAddress(req.body.email)
×
25

26
  if (validatedEmail === null) {
×
27
    res.status(400).send({ success: false })
×
28
    return
×
29
  }
30

31
  try {
×
32
    const allBreaches = req.app.locals.breaches
×
33
    const breaches = await getBreachesForEmail(getSha1(validatedEmail.email), allBreaches, false)
×
34

35
    /** @type {RequestBreachScanSuccessResponse} */
36
    const successResponse = {
×
37
      success: true,
38
      breaches: breaches.slice(0, 6),
39
      total: breaches.length,
40
      heading:
41
        // This is sent in the API response so we can replace the variables in
42
        // the Fluent string (because Fluent might change the strings depending
43
        // on the variables, specifically the count, and we don't run Fluent on
44
        // the client side):
45
        getMessage(
46
          'exposure-landing-result-hero-heading',
47
          {
48
            // Will be injected client-side, since this is derived from user
49
            // input and thus needs to be sanitized by the browser:
50
            email: '',
51
            count: breaches.length
52
          }
53
        )
54
          .replace('<email>', '<span class="breach-result-email">')
55
          .replace('</email>', '</span>')
56
          .replace('<count>', '<span class="breach-result-count">')
57
          .replace('</count>', '</span>'),
58
      // This is sent in the API response because we can't call `getBreachLogo`
59
      // client side, where it would expose AppConstants:
60
      logos: breaches.map(breach => getBreachLogo(breach, req.app.locals.breachLogoMap)),
×
61
      // This is sent in the API response because we don't have Fluent on the
62
      // client side, and thus can't dynamically localise breached data classes:
63
      dataClassStrings: breaches.map(breach => breach.DataClasses.map(/** @param {string} dataClass */ dataClass => getMessage(dataClass)))
×
64
    }
65
    res.json(successResponse)
×
66
    return
×
67
  } catch (ex) {
68
    res.status(500).send({ success: false })
×
69
  }
70
}
71

72
export { requestBreachScan }
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