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

mozilla / blurts-server / #13059

pending completion
#13059

push

circleci

Vinnl
Also use cached favicon on breach detail page

It'll be lower quality than the organic, free-range, hand-crafted
logos served from our CDN, but at least it'll free up Maxx to learn
some Qt.

282 of 1603 branches covered (17.59%)

Branch coverage included in aggregate %.

959 of 4325 relevant lines covered (22.17%)

1.85 hits per line

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

0.0
/src/views/partials/breach-detail.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 { getBreachLogo } from '../../utils/breach-logo.js'
6
import { getLocale, getMessage } from '../../utils/fluent.js'
7
import { getAllPriorityDataClasses, getAllGenericRecommendations } from '../../utils/recommendations.js'
8

9
function getBreachCategory (breach) {
10
  if (['Exactis', 'Apollo', 'YouveBeenScraped', 'ElasticsearchSalesLeads', 'Estonia', 'MasterDeeds', 'PDL'].includes(breach.Name)) {
×
11
    return 'data-aggregator-breach'
×
12
  }
13
  if (breach.IsSensitive) {
×
14
    return 'sensitive-breach'
×
15
  }
16
  if (breach.Domain !== '') {
×
17
    return 'website-breach'
×
18
  }
19
  return 'data-aggregator-breach'
×
20
}
21

22
function compareBreachDates (breach) {
23
  const breachDate = new Date(breach.BreachDate)
×
24
  const addedDate = new Date(breach.AddedDate)
×
25
  const timeDiff = Math.abs(breachDate.getTime() - addedDate.getTime())
×
26
  const dayDiff = Math.ceil(timeDiff / (1000 * 3600 * 24))
×
27
  if (dayDiff > 90) {
×
28
    return true
×
29
  }
30
  return false
×
31
}
32

33
function getSortedDataClasses (breach, isUserBrowserFirefox = false, isUserLocaleEnUs = false, isUserLocalEn = false, changePWLink = false) {
×
34
  const priorityDataClasses = getAllPriorityDataClasses(isUserBrowserFirefox, isUserLocaleEnUs, changePWLink)
×
35

36
  const sortedDataClasses = {
×
37
    priority: [],
38
    lowerPriority: []
39
  }
40

41
  const dataClasses = breach.DataClasses
×
42
  dataClasses.forEach(dataClass => {
×
43
    const dataType = getMessage(dataClass)
×
44
    if (priorityDataClasses[dataClass]) {
×
45
      priorityDataClasses[dataClass].dataType = dataType
×
46
      sortedDataClasses.priority.push(priorityDataClasses[dataClass])
×
47
      return
×
48
    }
49
    sortedDataClasses.lowerPriority.push(dataType)
×
50
  })
51
  sortedDataClasses.priority.sort((a, b) => { return b.weight - a.weight })
×
52
  sortedDataClasses.lowerPriority = sortedDataClasses.lowerPriority.join(', ')
×
53
  return sortedDataClasses
×
54
}
55

56
function makeDataSection (breach) {
57
  const dataClasses = getSortedDataClasses(breach)
×
58

59
  let output = dataClasses.priority.map(dataClass =>
×
60
    `<li>
×
61
      <img src="${dataClass.pathToGlyph}.svg" width="24" alt="">
62
      ${dataClass.dataType}
63
    </li>`
64
  ).join('')
65

66
  output +=
×
67
    `<li>
68
      <img src="/images/more.svg">
69
      ${dataClasses.lowerPriority}
70
    </li>`
71

72
  return output
×
73
}
74

75
function makeRecommendationCards (breach) {
76
  const dataClasses = getSortedDataClasses(breach)
×
77

78
  let output = dataClasses.priority.map(dataClass =>
×
79
    dataClass.recommendations?.map(r =>
×
80
    `<div class="breach-detail-recommendation ${r.recIconClassName}">
×
81
      <dt>${getMessage(r.recommendationCopy.subhead)}</dt>
82
      <dd>
83
        <p>${getMessage(r.recommendationCopy.body)}</p>
84
        ${r.recommendationCopy.cta ? `<a href="${r.ctaHref}" target="${r.ctaShouldOpenInNewTab ? '_blank' : '_self'}">${getMessage(r.recommendationCopy.cta)}</a>` : ''}
×
85
      </dd>
86
    </div>`).join('')
87
  ).join('')
88

89
  output += getAllGenericRecommendations().map(dataClass =>
×
90
    `<div class="breach-detail-recommendation ${dataClass.recIconClassName}">
×
91
      <dt>${getMessage(dataClass.recommendationCopy.subhead)}</dt>
92
      <dd>
93
        <p>${getMessage(dataClass.recommendationCopy.body)}</p>
94
        ${dataClass.recommendationCopy.cta ? `<a href="${dataClass.ctaHref}" target="${dataClass.ctaShouldOpenInNewTab ? '_blank' : '_self'}">${getMessage(dataClass.recommendationCopy.cta)}</a>` : ''}
×
95
      </dd>
96
    </div>`
97
  ).join('')
98

99
  return output
×
100
}
101

102
function getBreachDetail (categoryId) {
103
  if (categoryId === 'data-aggregator-breach') {
×
104
    return {
×
105
      subhead: getMessage('what-is-data-agg'),
106
      body: getMessage('what-is-data-agg-blurb')
107

108
    }
109
  } else if (categoryId === 'sensitive-breach') {
×
110
    return {
×
111
      subhead: getMessage('sensitive-sites'),
112
      body: getMessage('sensitive-sites-copy')
113

114
    }
115
  } else {
116
    return {
×
117
      subhead: getMessage('what-is-a-website-breach'),
118
      body: getMessage('website-breach-blurb')
119
    }
120
  }
121
}
122

123
function makeBreachDetails (breach) {
124
  const breachDetail = getBreachDetail(breach)
×
125
  return `
×
126
  <h2>${breachDetail.subhead}</h2>
127
  <div>${breachDetail.body}</div>
128
  `
129
}
130

131
export const breachDetails = data => `
×
132
  <header class="breach-detail-header">
133
    ${getBreachLogo(data.breach, data.breachLogos)}
134
    <div class="breach-detail-meta">
135
      <h1>${data.breach.Title}</h1>
136
      ${getBreachCategory(data.breach) === 'website-breach'
×
137
        ? `<a href="https://${data.breach.Domain}" class="breach-detail-meta-domain" rel="nofollow noopener noreferrer" data-event-label="${data.breach.Domain}" data-event-action="Engage" data-event-category="Breach Detail: Website URL Link" target="_blank">${data.breach.Domain}</a>`
138
        : ''}
139
      <a href="#what-is-this-breach" class="breach-detail-meta-more-info">${getMessage(getBreachCategory(data.breach))}</a>
140
    </div>
141
  </header>
142

143
  <!-- Overview -->
144
  <div class="breach-detail-overview">
145
    <div class="breach-detail-overview-blurb">
146
      <h2>${getMessage('breach-overview-title')}</h2>
147
      <div>${getMessage(
148
        'breach-overview-new',
149
        {
150
          breachDate: data.breach.BreachDate.toLocaleString(getLocale(), { year: 'numeric', month: 'long', day: 'numeric' }),
151
          breachTitle: data.breach.Name,
152
          addedDate: data.breach.AddedDate.toLocaleString(getLocale(), { year: 'numeric', month: 'long', day: 'numeric' })
153
        })}</div>
154
      ${compareBreachDates(data.breach) ? `<a href="#delayed-reporting">${getMessage('delayed-reporting-headline')}</a>` : ''}
×
155
    </div>
156
    <!--Exposed Data Classes -->
157
    <div>
158
      <h3>${getMessage('what-data')}</h3>
159
      <ul class="breach-detail-compromised-list">
160
        ${makeDataSection(data.breach)}
161
      </ul>
162
      <p class="breach-detail-attribution">
163
      ${getMessage('email-2022-hibp-attribution', {
164
        'hibp-link-attr': 'href="https://haveibeenpwned.com/" target="_blank"'
165
      })}
166
      </p>
167
    </div>
168
  </div>
169

170
  <!-- Sign Up Banner -->
171
  <div class="breach-detail-sign-up">
172
    <img alt="" src="/images/breach-detail-scan.svg" width="120" />
173
    <div class="breach-detail-sign-up-content">
174
        <h2>${getMessage('find-out-if-2')}</h2>
175
        <span>${getMessage('find-out-if-description')}</span>
176
    </div>
177
    <div class="breach-detail-sign-up-cta-wrapper">
178
      <a href="/user/breaches" data-cta-id='breaches-detail' class="button primary">${getMessage('breach-detail-cta-signup')}</a>
179
    </div>
180
  </div>
181

182
  <!-- What to do tips -->
183
  <div id="what-to-do-next">
184
    <div class="breach-detail-recommendation-lead">
185
      <h2>${data.breach.DataClasses.includes('passwords') ? getMessage('rec-section-headline') : getMessage('rec-section-headline-no-pw')}</h2>
×
186
      <p>${data.breach.DataClasses.includes('passwords') ? getMessage('rec-section-subhead') : getMessage('rec-section-subhead-no-pw')}</p>
×
187
    </div>
188
    <dl class="breach-detail-recommendation-list">
189
      ${makeRecommendationCards(data.breach)}
190
    </dl>
191
  </div>
192

193
  <!-- What is this breach? / Why did it take you so long to report it?-->
194
  <div class="breach-detail-info">
195
    <div id="what-is-this-breach">
196
      ${makeBreachDetails(getBreachCategory(data.breach))}
197
    </div>
198
    ${compareBreachDates(data.breach)
×
199
      ? `
200
        <div id="delayed-reporting">
201
          <h2>${getMessage('delayed-reporting-headline')}</h2>
202
          ${getMessage('delayed-reporting-copy')}
203
        </div>`
204
      : ''
205
    }
206
  </div>
207
`
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