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

mozilla / blurts-server / #13281

pending completion
#13281

push

circleci

jswinarton
Merge branch 'main' into premium-scaffold

282 of 1687 branches covered (16.72%)

Branch coverage included in aggregate %.

188 of 188 new or added lines in 26 files covered. (100.0%)

959 of 4564 relevant lines covered (21.01%)

1.75 hits per line

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

0.0
/src/client/js/partials/exposureScan.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
/** @type {HTMLDivElement} */
6
// @ts-ignore: We guard against a null value by not calling init():
7
const exposureScanPartial = document.querySelector("[data-partial='exposureScan']")
×
8

9
if (exposureScanPartial) {
×
10
  init()
×
11
}
12

13
async function init () {
14
  const urlParams = new URLSearchParams(document.location.hash.substring(1))
×
15
  const emailFromUrl = urlParams.get('email')
×
16
  /** @type {HTMLTemplateElement} */
17
  const dataEl = exposureScanPartial.querySelector('#data')
×
18
  dataEl.dataset.email = emailFromUrl
×
19
  const sanitizedEmail = dataEl.dataset.email.trim()
×
20

21
  try {
×
22
    const res = await fetch('/api/v1/scan/', {
×
23
      headers: {
24
        'Content-Type': 'application/json',
25
        'x-csrf-token': dataEl.dataset.csrfToken,
26
        Accept: 'application/json'
27
      },
28
      mode: 'same-origin',
29
      method: 'POST',
30
      body: JSON.stringify({
31
        email: sanitizedEmail
32
      })
33
    })
34

35
    if (!res.ok) {
×
36
      throw new Error('Immediately caught to show an error message.')
×
37
    }
38

39
    /** @type {import("../../../controllers/requestBreachScan").RequestBreachScanResponse} */
40
    const responseBody = await res.json()
×
41

42
    if (!responseBody.success) {
×
43
      throw new Error('Immediately caught to show an error message.')
×
44
    }
45

46
    if (responseBody.total > 6) {
×
47
      showOverflowingBreachResults(responseBody, { sanitizedEmail })
×
48
    } else if (responseBody.total > 0) {
×
49
      showSomeBreachResults(responseBody, { sanitizedEmail })
×
50
    } else {
51
      showNoBreachesResult(responseBody, { sanitizedEmail })
×
52
    }
53
  } catch (e) {
54
    exposureScanPartial.querySelector('#exposure-scan-loading').hidden = true
×
55
    exposureScanPartial.querySelector('#exposure-scan-error').hidden = false
×
56
  }
57
}
58

59
/**
60
 * @param {import("../../../controllers/requestBreachScan").RequestBreachScanSuccessResponse} response
61
 * @param { sanitizedEmail: string } params
62
 */
63
function showOverflowingBreachResults (response, params) {
64
  const heading = document.createElement('h1')
×
65
  heading.innerHTML = response.heading
×
66
  const emailEl = heading.querySelector('.breach-result-email')
×
67
  emailEl.setAttribute('title', params.sanitizedEmail)
×
68
  emailEl.textContent = params.sanitizedEmail
×
69

70
  exposureScanPartial.querySelector('#exposure-scan-results-overflow .exposure-scan-hero-content').insertAdjacentElement('afterbegin', heading)
×
71
  const breachCards = response.breaches.map((breach, index) => getBreachCard(breach, response.logos[index], response.dataClassStrings[index]))
×
72
  const breachContainer = exposureScanPartial.querySelector('#exposure-scan-results-overflow .exposure-scan-breaches')
×
73
  breachCards.forEach(card => breachContainer.appendChild(card))
×
74

75
  exposureScanPartial.querySelector('#exposure-scan-loading').hidden = true
×
76
  exposureScanPartial.querySelector('#exposure-scan-results-overflow').hidden = false
×
77
}
78

79
/**
80
 * @param {import("../../../controllers/requestBreachScan").RequestBreachScanSuccessResponse} response
81
 * @param { sanitizedEmail: string } params
82
 */
83
function showSomeBreachResults (response, params) {
84
  const heading = document.createElement('h1')
×
85
  heading.innerHTML = response.heading
×
86
  const emailEl = heading.querySelector('.breach-result-email')
×
87
  emailEl.setAttribute('title', params.sanitizedEmail)
×
88
  emailEl.textContent = params.sanitizedEmail
×
89
  exposureScanPartial.querySelector('#exposure-scan-results-some .exposure-scan-hero-content').insertAdjacentElement('afterbegin', heading)
×
90
  const breachCards = response.breaches.map((breach, index) => getBreachCard(breach, response.logos[index], response.dataClassStrings[index]))
×
91
  const breachContainer = exposureScanPartial.querySelector('#exposure-scan-results-some .exposure-scan-breaches')
×
92
  breachCards.forEach(card => breachContainer.appendChild(card))
×
93

94
  exposureScanPartial.querySelector('#exposure-scan-loading').hidden = true
×
95
  exposureScanPartial.querySelector('#exposure-scan-results-some').hidden = false
×
96
}
97

98
/**
99
 * @param {import("../../../controllers/requestBreachScan").RequestBreachScanSuccessResponse} response
100
 * @param { sanitizedEmail: string } params
101
 */
102
function showNoBreachesResult (response, params) {
103
  const heading = document.createElement('h1')
×
104
  heading.innerHTML = response.heading
×
105
  const emailEl = heading.querySelector('.breach-result-email')
×
106
  emailEl.setAttribute('title', params.sanitizedEmail)
×
107
  emailEl.textContent = params.sanitizedEmail
×
108
  exposureScanPartial.querySelector('#exposure-scan-results-none .exposure-scan-hero-content').insertAdjacentElement('afterbegin', heading)
×
109

110
  exposureScanPartial.querySelector('#exposure-scan-loading').hidden = true
×
111
  exposureScanPartial.querySelector('#exposure-scan-results-none').hidden = false
×
112
}
113

114
function getBreachCard (breach, logo, dataClassStrings) {
115
  const newCard = document.getElementById('breach-template').content.cloneNode(true)
×
116
  newCard.querySelector('.exposure-scan-breach-company-logo').innerHTML = logo
×
117
  newCard.querySelector('.exposure-scan-breach-company-name').textContent = breach.Title
×
118
  newCard.querySelector('.exposure-scan-breach-added dd').textContent = new Date(breach.AddedDate).toLocaleString(navigator.languages, { year: 'numeric', month: 'long', day: 'numeric' })
×
119
  newCard.querySelector('.exposure-scan-breach-data dd').textContent = formatList(dataClassStrings)
×
120

121
  return newCard
×
122
}
123

124
function formatList (list) {
125
  if (typeof Intl.ListFormat === 'undefined') {
×
126
    return list.join(', ')
×
127
  }
128

129
  return (new Intl.ListFormat(navigator.languages, { type: 'unit', style: 'short' })).format(list)
×
130
}
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