Skip to content

Commit 85935ac

Browse files
authored
deps: ssri@10.0.2 (#6325)
1 parent f1388b4 commit 85935ac

File tree

4 files changed

+100
-94
lines changed

4 files changed

+100
-94
lines changed

node_modules/ssri/lib/index.js

+91-86
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
const crypto = require('crypto')
44
const MiniPass = require('minipass')
55

6-
const SPEC_ALGORITHMS = ['sha256', 'sha384', 'sha512']
6+
const SPEC_ALGORITHMS = ['sha512', 'sha384', 'sha256']
7+
const DEFAULT_ALGORITHMS = ['sha512']
78

89
// TODO: this should really be a hardcoded list of algorithms we support,
910
// rather than [a-z0-9].
@@ -12,80 +13,58 @@ const SRI_REGEX = /^([a-z0-9]+)-([^?]+)([?\S*]*)$/
1213
const STRICT_SRI_REGEX = /^([a-z0-9]+)-([A-Za-z0-9+/=]{44,88})(\?[\x21-\x7E]*)?$/
1314
const VCHAR_REGEX = /^[\x21-\x7E]+$/
1415

15-
const defaultOpts = {
16-
algorithms: ['sha512'],
17-
error: false,
18-
options: [],
19-
pickAlgorithm: getPrioritizedHash,
20-
sep: ' ',
21-
single: false,
22-
strict: false,
23-
}
24-
25-
const ssriOpts = (opts = {}) => ({ ...defaultOpts, ...opts })
26-
27-
const getOptString = options => !options || !options.length
28-
? ''
29-
: `?${options.join('?')}`
30-
31-
const _onEnd = Symbol('_onEnd')
32-
const _getOptions = Symbol('_getOptions')
33-
const _emittedSize = Symbol('_emittedSize')
34-
const _emittedIntegrity = Symbol('_emittedIntegrity')
35-
const _emittedVerified = Symbol('_emittedVerified')
16+
const getOptString = options => options?.length ? `?${options.join('?')}` : ''
3617

3718
class IntegrityStream extends MiniPass {
19+
#emittedIntegrity
20+
#emittedSize
21+
#emittedVerified
22+
3823
constructor (opts) {
3924
super()
4025
this.size = 0
4126
this.opts = opts
4227

4328
// may be overridden later, but set now for class consistency
44-
this[_getOptions]()
29+
this.#getOptions()
4530

4631
// options used for calculating stream. can't be changed.
47-
const { algorithms = defaultOpts.algorithms } = opts
32+
const algorithms = opts?.algorithms || DEFAULT_ALGORITHMS
4833
this.algorithms = Array.from(
4934
new Set(algorithms.concat(this.algorithm ? [this.algorithm] : []))
5035
)
5136
this.hashes = this.algorithms.map(crypto.createHash)
5237
}
5338

54-
[_getOptions] () {
55-
const {
56-
integrity,
57-
size,
58-
options,
59-
} = { ...defaultOpts, ...this.opts }
60-
39+
#getOptions () {
6140
// For verification
62-
this.sri = integrity ? parse(integrity, this.opts) : null
63-
this.expectedSize = size
41+
this.sri = this.opts?.integrity ? parse(this.opts?.integrity, this.opts) : null
42+
this.expectedSize = this.opts?.size
6443
this.goodSri = this.sri ? !!Object.keys(this.sri).length : false
6544
this.algorithm = this.goodSri ? this.sri.pickAlgorithm(this.opts) : null
6645
this.digests = this.goodSri ? this.sri[this.algorithm] : null
67-
this.optString = getOptString(options)
46+
this.optString = getOptString(this.opts?.options)
6847
}
6948

7049
on (ev, handler) {
71-
if (ev === 'size' && this[_emittedSize]) {
72-
return handler(this[_emittedSize])
50+
if (ev === 'size' && this.#emittedSize) {
51+
return handler(this.#emittedSize)
7352
}
7453

75-
if (ev === 'integrity' && this[_emittedIntegrity]) {
76-
return handler(this[_emittedIntegrity])
54+
if (ev === 'integrity' && this.#emittedIntegrity) {
55+
return handler(this.#emittedIntegrity)
7756
}
7857

79-
if (ev === 'verified' && this[_emittedVerified]) {
80-
return handler(this[_emittedVerified])
58+
if (ev === 'verified' && this.#emittedVerified) {
59+
return handler(this.#emittedVerified)
8160
}
8261

8362
return super.on(ev, handler)
8463
}
8564

8665
emit (ev, data) {
8766
if (ev === 'end') {
88-
this[_onEnd]()
67+
this.#onEnd()
8968
}
9069
return super.emit(ev, data)
9170
}
@@ -96,9 +75,9 @@ class IntegrityStream extends MiniPass {
9675
return super.write(data)
9776
}
9877

99-
[_onEnd] () {
78+
#onEnd () {
10079
if (!this.goodSri) {
101-
this[_getOptions]()
80+
this.#getOptions()
10281
}
10382
const newSri = parse(this.hashes.map((h, i) => {
10483
return `${this.algorithms[i]}-${h.digest('base64')}${this.optString}`
@@ -123,12 +102,12 @@ class IntegrityStream extends MiniPass {
123102
err.sri = this.sri
124103
this.emit('error', err)
125104
} else {
126-
this[_emittedSize] = this.size
105+
this.#emittedSize = this.size
127106
this.emit('size', this.size)
128-
this[_emittedIntegrity] = newSri
107+
this.#emittedIntegrity = newSri
129108
this.emit('integrity', newSri)
130109
if (match) {
131-
this[_emittedVerified] = match
110+
this.#emittedVerified = match
132111
this.emit('verified', match)
133112
}
134113
}
@@ -141,8 +120,7 @@ class Hash {
141120
}
142121

143122
constructor (hash, opts) {
144-
opts = ssriOpts(opts)
145-
const strict = !!opts.strict
123+
const strict = opts?.strict
146124
this.source = hash.trim()
147125

148126
// set default values so that we make V8 happy to
@@ -161,7 +139,7 @@ class Hash {
161139
if (!match) {
162140
return
163141
}
164-
if (strict && !SPEC_ALGORITHMS.some(a => a === match[1])) {
142+
if (strict && !SPEC_ALGORITHMS.includes(match[1])) {
165143
return
166144
}
167145
this.algorithm = match[1]
@@ -182,14 +160,13 @@ class Hash {
182160
}
183161

184162
toString (opts) {
185-
opts = ssriOpts(opts)
186-
if (opts.strict) {
163+
if (opts?.strict) {
187164
// Strict mode enforces the standard as close to the foot of the
188165
// letter as it can.
189166
if (!(
190167
// The spec has very restricted productions for algorithms.
191168
// https://www.w3.org/TR/CSP2/#source-list-syntax
192-
SPEC_ALGORITHMS.some(x => x === this.algorithm) &&
169+
SPEC_ALGORITHMS.includes(this.algorithm) &&
193170
// Usually, if someone insists on using a "different" base64, we
194171
// leave it as-is, since there's multiple standards, and the
195172
// specified is not a URL-safe variant.
@@ -203,11 +180,41 @@ class Hash {
203180
return ''
204181
}
205182
}
206-
const options = this.options && this.options.length
207-
? `?${this.options.join('?')}`
208-
: ''
209-
return `${this.algorithm}-${this.digest}${options}`
183+
return `${this.algorithm}-${this.digest}${getOptString(this.options)}`
184+
}
185+
}
186+
187+
function integrityHashToString (toString, sep, opts, hashes) {
188+
const toStringIsNotEmpty = toString !== ''
189+
190+
let shouldAddFirstSep = false
191+
let complement = ''
192+
193+
const lastIndex = hashes.length - 1
194+
195+
for (let i = 0; i < lastIndex; i++) {
196+
const hashString = Hash.prototype.toString.call(hashes[i], opts)
197+
198+
if (hashString) {
199+
shouldAddFirstSep = true
200+
201+
complement += hashString
202+
complement += sep
203+
}
204+
}
205+
206+
const finalHashString = Hash.prototype.toString.call(hashes[lastIndex], opts)
207+
208+
if (finalHashString) {
209+
shouldAddFirstSep = true
210+
complement += finalHashString
210211
}
212+
213+
if (toStringIsNotEmpty && shouldAddFirstSep) {
214+
return toString + sep + complement
215+
}
216+
217+
return toString + complement
211218
}
212219

213220
class Integrity {
@@ -224,21 +231,28 @@ class Integrity {
224231
}
225232

226233
toString (opts) {
227-
opts = ssriOpts(opts)
228-
let sep = opts.sep || ' '
229-
if (opts.strict) {
234+
let sep = opts?.sep || ' '
235+
let toString = ''
236+
237+
if (opts?.strict) {
230238
// Entries must be separated by whitespace, according to spec.
231239
sep = sep.replace(/\S+/g, ' ')
240+
241+
for (const hash of SPEC_ALGORITHMS) {
242+
if (this[hash]) {
243+
toString = integrityHashToString(toString, sep, opts, this[hash])
244+
}
245+
}
246+
} else {
247+
for (const hash of Object.keys(this)) {
248+
toString = integrityHashToString(toString, sep, opts, this[hash])
249+
}
232250
}
233-
return Object.keys(this).map(k => {
234-
return this[k].map(hash => {
235-
return Hash.prototype.toString.call(hash, opts)
236-
}).filter(x => x.length).join(sep)
237-
}).filter(x => x.length).join(sep)
251+
252+
return toString
238253
}
239254

240255
concat (integrity, opts) {
241-
opts = ssriOpts(opts)
242256
const other = typeof integrity === 'string'
243257
? integrity
244258
: stringify(integrity, opts)
@@ -252,7 +266,6 @@ class Integrity {
252266
// add additional hashes to an integrity value, but prevent
253267
// *changing* an existing integrity hash.
254268
merge (integrity, opts) {
255-
opts = ssriOpts(opts)
256269
const other = parse(integrity, opts)
257270
for (const algo in other) {
258271
if (this[algo]) {
@@ -268,7 +281,6 @@ class Integrity {
268281
}
269282

270283
match (integrity, opts) {
271-
opts = ssriOpts(opts)
272284
const other = parse(integrity, opts)
273285
if (!other) {
274286
return false
@@ -286,8 +298,7 @@ class Integrity {
286298
}
287299

288300
pickAlgorithm (opts) {
289-
opts = ssriOpts(opts)
290-
const pickAlgorithm = opts.pickAlgorithm
301+
const pickAlgorithm = opts?.pickAlgorithm || getPrioritizedHash
291302
const keys = Object.keys(this)
292303
return keys.reduce((acc, algo) => {
293304
return pickAlgorithm(acc, algo) || acc
@@ -300,7 +311,6 @@ function parse (sri, opts) {
300311
if (!sri) {
301312
return null
302313
}
303-
opts = ssriOpts(opts)
304314
if (typeof sri === 'string') {
305315
return _parse(sri, opts)
306316
} else if (sri.algorithm && sri.digest) {
@@ -315,7 +325,7 @@ function parse (sri, opts) {
315325
function _parse (integrity, opts) {
316326
// 3.4.3. Parse metadata
317327
// https://w3c.github.io/webappsec-subresource-integrity/#parse-metadata
318-
if (opts.single) {
328+
if (opts?.single) {
319329
return new Hash(integrity, opts)
320330
}
321331
const hashes = integrity.trim().split(/\s+/).reduce((acc, string) => {
@@ -334,7 +344,6 @@ function _parse (integrity, opts) {
334344

335345
module.exports.stringify = stringify
336346
function stringify (obj, opts) {
337-
opts = ssriOpts(opts)
338347
if (obj.algorithm && obj.digest) {
339348
return Hash.prototype.toString.call(obj, opts)
340349
} else if (typeof obj === 'string') {
@@ -346,8 +355,7 @@ function stringify (obj, opts) {
346355

347356
module.exports.fromHex = fromHex
348357
function fromHex (hexDigest, algorithm, opts) {
349-
opts = ssriOpts(opts)
350-
const optString = getOptString(opts.options)
358+
const optString = getOptString(opts?.options)
351359
return parse(
352360
`${algorithm}-${
353361
Buffer.from(hexDigest, 'hex').toString('base64')
@@ -357,9 +365,8 @@ function fromHex (hexDigest, algorithm, opts) {
357365

358366
module.exports.fromData = fromData
359367
function fromData (data, opts) {
360-
opts = ssriOpts(opts)
361-
const algorithms = opts.algorithms
362-
const optString = getOptString(opts.options)
368+
const algorithms = opts?.algorithms || DEFAULT_ALGORITHMS
369+
const optString = getOptString(opts?.options)
363370
return algorithms.reduce((acc, algo) => {
364371
const digest = crypto.createHash(algo).update(data).digest('base64')
365372
const hash = new Hash(
@@ -382,7 +389,6 @@ function fromData (data, opts) {
382389

383390
module.exports.fromStream = fromStream
384391
function fromStream (stream, opts) {
385-
opts = ssriOpts(opts)
386392
const istream = integrityStream(opts)
387393
return new Promise((resolve, reject) => {
388394
stream.pipe(istream)
@@ -399,10 +405,9 @@ function fromStream (stream, opts) {
399405

400406
module.exports.checkData = checkData
401407
function checkData (data, sri, opts) {
402-
opts = ssriOpts(opts)
403408
sri = parse(sri, opts)
404409
if (!sri || !Object.keys(sri).length) {
405-
if (opts.error) {
410+
if (opts?.error) {
406411
throw Object.assign(
407412
new Error('No valid integrity hashes to check against'), {
408413
code: 'EINTEGRITY',
@@ -416,7 +421,8 @@ function checkData (data, sri, opts) {
416421
const digest = crypto.createHash(algorithm).update(data).digest('base64')
417422
const newSri = parse({ algorithm, digest })
418423
const match = newSri.match(sri, opts)
419-
if (match || !opts.error) {
424+
opts = opts || {}
425+
if (match || !(opts.error)) {
420426
return match
421427
} else if (typeof opts.size === 'number' && (data.length !== opts.size)) {
422428
/* eslint-disable-next-line max-len */
@@ -440,7 +446,7 @@ function checkData (data, sri, opts) {
440446

441447
module.exports.checkStream = checkStream
442448
function checkStream (stream, sri, opts) {
443-
opts = ssriOpts(opts)
449+
opts = opts || Object.create(null)
444450
opts.integrity = sri
445451
sri = parse(sri, opts)
446452
if (!sri || !Object.keys(sri).length) {
@@ -465,15 +471,14 @@ function checkStream (stream, sri, opts) {
465471
}
466472

467473
module.exports.integrityStream = integrityStream
468-
function integrityStream (opts = {}) {
474+
function integrityStream (opts = Object.create(null)) {
469475
return new IntegrityStream(opts)
470476
}
471477

472478
module.exports.create = createIntegrity
473479
function createIntegrity (opts) {
474-
opts = ssriOpts(opts)
475-
const algorithms = opts.algorithms
476-
const optString = getOptString(opts.options)
480+
const algorithms = opts?.algorithms || DEFAULT_ALGORITHMS
481+
const optString = getOptString(opts?.options)
477482

478483
const hashes = algorithms.map(crypto.createHash)
479484

0 commit comments

Comments
 (0)