Skip to content

Commit dce3dab

Browse files
H4adwraithgar
authored andcommitted
fix: faster stream verification
1 parent 3e72ec0 commit dce3dab

File tree

3 files changed

+100
-8
lines changed

3 files changed

+100
-8
lines changed

lib/index.js

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,39 @@ class IntegrityStream extends MiniPass {
2929
this.#getOptions()
3030

3131
// options used for calculating stream. can't be changed.
32-
const algorithms = opts?.algorithms || DEFAULT_ALGORITHMS
33-
this.algorithms = Array.from(
34-
new Set(algorithms.concat(this.algorithm ? [this.algorithm] : []))
35-
)
32+
if (opts?.algorithms) {
33+
this.algorithms = Array.from(
34+
new Set(opts.algorithms.concat(this.algorithm ? [this.algorithm] : []))
35+
)
36+
} else {
37+
this.algorithms = DEFAULT_ALGORITHMS
38+
39+
if (this.algorithm !== null && this.algorithm !== DEFAULT_ALGORITHMS[0]) {
40+
this.algorithms.push(this.algorithm)
41+
}
42+
}
43+
3644
this.hashes = this.algorithms.map(crypto.createHash)
3745
}
3846

3947
#getOptions () {
4048
// For verification
4149
this.sri = this.opts?.integrity ? parse(this.opts?.integrity, this.opts) : null
4250
this.expectedSize = this.opts?.size
43-
this.goodSri = this.sri ? !!Object.keys(this.sri).length : false
44-
this.algorithm = this.goodSri ? this.sri.pickAlgorithm(this.opts) : null
51+
52+
if (!this.sri) {
53+
this.algorithm = null
54+
} else if (this.sri.isIntegrity) {
55+
this.goodSri = !this.sri.isEmpty()
56+
57+
if (this.goodSri) {
58+
this.algorithm = this.sri.pickAlgorithm(this.opts)
59+
}
60+
} else if (this.sri.isHash) {
61+
this.goodSri = true
62+
this.algorithm = this.sri.algorithm
63+
}
64+
4565
this.digests = this.goodSri ? this.sri[this.algorithm] : null
4666
this.optString = getOptString(this.opts?.options)
4767
}
@@ -159,6 +179,24 @@ class Hash {
159179
return this.toString()
160180
}
161181

182+
match (integrity, opts) {
183+
const other = parse(integrity, opts)
184+
if (!other) {
185+
return false
186+
}
187+
if (other instanceof Integrity) {
188+
const algo = other.pickAlgorithm(opts)
189+
const foundHash = other[algo].find(hash => hash.digest === this.digest)
190+
191+
if (foundHash) {
192+
return foundHash
193+
}
194+
195+
return false
196+
}
197+
return other.digest === this.digest ? other : false
198+
}
199+
162200
toString (opts) {
163201
if (opts?.strict) {
164202
// Strict mode enforces the standard as close to the foot of the
@@ -399,7 +437,7 @@ function fromStream (stream, opts) {
399437
sri = s
400438
})
401439
istream.on('end', () => resolve(sri))
402-
istream.on('data', () => {})
440+
istream.resume()
403441
})
404442
}
405443

@@ -466,7 +504,7 @@ function checkStream (stream, sri, opts) {
466504
verified = s
467505
})
468506
checker.on('end', () => resolve(verified))
469-
checker.on('data', () => {})
507+
checker.resume()
470508
})
471509
}
472510

test/check.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ test('checkStream', t => {
160160
})
161161
}).then(res => {
162162
t.same(res, meta, 'Accepts Hash-like SRI')
163+
return ssri.checkStream(fileStream(), `sha512-${hash(TEST_DATA, 'sha512')}`, { single: true })
164+
}).then(res => {
165+
t.same(res, meta, 'Process successfully with single option')
163166
return ssri.checkStream(
164167
fileStream(),
165168
`sha512-nope sha512-${hash(TEST_DATA, 'sha512')}`

test/match.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'use strict'
2+
3+
const crypto = require('crypto')
4+
const fs = require('fs')
5+
const test = require('tap').test
6+
7+
const ssri = require('..')
8+
9+
const TEST_DATA = fs.readFileSync(__filename)
10+
11+
function hash (data, algorithm) {
12+
return crypto.createHash(algorithm).update(data).digest('base64')
13+
}
14+
15+
test('hashes should match when valid', t => {
16+
const sha = hash(TEST_DATA, 'sha512')
17+
const integrity = `sha512-${sha}`
18+
const parsed = ssri.parse(integrity, { single: true })
19+
t.same(
20+
parsed.match(integrity, { single: true }),
21+
parsed,
22+
'should return the same algo when digest is equal (single option)'
23+
)
24+
t.same(
25+
parsed.match('sha-233', { single: true }),
26+
false,
27+
'invalid integrity should not match (single option)'
28+
)
29+
t.same(
30+
parsed.match(null, { single: true }),
31+
false,
32+
'null integrity just returns false (single option)'
33+
)
34+
35+
t.same(
36+
parsed.match(integrity),
37+
parsed,
38+
'should return the same algo when digest is equal'
39+
)
40+
t.same(
41+
parsed.match('sha-233'),
42+
false,
43+
'invalid integrity should not match'
44+
)
45+
t.same(
46+
parsed.match(null),
47+
false,
48+
'null integrity just returns false'
49+
)
50+
t.end()
51+
})

0 commit comments

Comments
 (0)