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

mozilla / blurts-server / #13125

pending completion
#13125

push

circleci

Vinnl
All states for breach results

282 of 1655 branches covered (17.04%)

Branch coverage included in aggregate %.

55 of 55 new or added lines in 2 files covered. (100.0%)

959 of 4458 relevant lines covered (21.51%)

1.79 hits per line

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

0.0
/src/controllers/request-breach-scan.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/breach-logo.js'
7
import { UserInputError } from '../utils/error.js'
8
import { getSha1 } from '../utils/fxa.js'
9
import { getBreachesForEmail } from '../utils/hibp.js'
10

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

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

23
  // TODO could share this validation logic with add-email API?
24
  const email = req.body.email
×
25
  // Use the same regex as HTML5 email input type
26
  // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/email#basic_validation
27
  const emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
×
28

29
  if (!email || !emailRegex.test(email)) {
×
30
    throw new UserInputError(getMessage('user-add-invalid-email'))
×
31
  }
32

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

37
    /** @type {RequestBreachScanSuccessResponse} */
38
    const successResponse = {
×
39
      success: true,
40
      breaches: breaches.slice(0, 6),
41
      total: breaches.length,
42
      heading:
43
        // This is sent in the API response so we can replace the variables in
44
        // the Fluent string (because Fluent might change the strings depending
45
        // on the variables, specifically the count, and we don't run Fluent on
46
        // the client side):
47
        getMessage(
48
          'exposure-landing-result-hero-heading',
49
          {
50
            email,
51
            count: breaches.length
52
          }
53
        )
54
          .replace('<email>', `<span class="breach-result-email" title="${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