Skip to content

Commit 2301e74

Browse files
committed
feat(check): return IntegrityMetadata on check success
BREAKING CHANGE: `checkData`, `checkStream`, and `createCheckerStream` now yield a whole IntegrityMetadata instance representing the first successful hash match.
1 parent a06455f commit 2301e74

3 files changed

Lines changed: 59 additions & 43 deletions

File tree

README.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ ssri.fromStream(fs.createReadStream('index.js'), {
249249
}) // succeeds
250250
```
251251

252-
#### <a name="check-data"></a> `> ssri.checkData(data, sri, [opts]) -> Algorithm|false`
252+
#### <a name="check-data"></a> `> ssri.checkData(data, sri, [opts]) -> IntegrityMetadata|false`
253253

254254
Verifies `data` integrity against an `sri` argument. `data` may be either a
255255
`String` or a `Buffer`, and `sri` can be any subresource integrity
@@ -302,17 +302,22 @@ const integrity = ssri.fromData(fs.readFileSync('index.js'))
302302
ssri.checkStream(
303303
fs.createReadStream('index.js'),
304304
integrity
305-
) // -> Promise<'sha512'>
305+
)
306+
// ->
307+
// Promise<{
308+
// algorithm: 'sha512',
309+
// digest: 'sha512-yzd8ELD1piyANiWnmdnpCL5F52f10UfUdEkHywVZeqTt0ymgrxR63Qz0GB7TKPoeeZQmWCaz7T1'
310+
// }>
306311

307312
ssri.checkStream(
308313
fs.createReadStream('index.js'),
309314
'sha256-l981iLWj8kurw4UbNy8Lpxqdzd7UOxS50Glhv8FwfZ0'
310-
) // -> Promise<'sha256'>
315+
) // -> Promise<IntegrityMetadata>
311316

312317
ssri.checkStream(
313318
fs.createReadStream('index.js'),
314319
'sha1-BaDDigEST'
315-
) // -> Promise<Error<EBADCHECKSUM>>
320+
) // -> Promise<Error<{code: 'EBADCHECKSUM'}>>
316321
```
317322

318323
#### <a name="create-checker-stream"></a> `> createCheckerStream(sri, [opts]) -> CheckerStream`

index.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,9 @@ function checkData (data, sri, opts) {
179179
const algorithm = Object.keys(sri).reduce((acc, algo) => {
180180
return pickAlgorithm(acc, algo) || acc
181181
})
182-
const digests = sri[algorithm].map(m => m.digest)
182+
const digests = sri[algorithm]
183183
const digest = crypto.createHash(algorithm).update(data).digest('base64')
184-
return digests.some(d => d === digest) && algorithm
184+
return digests.find(meta => meta.digest === digest) || false
185185
}
186186

187187
module.exports.checkStream = checkStream
@@ -193,8 +193,8 @@ function checkStream (stream, sri, opts) {
193193
stream.pipe(checker)
194194
stream.on('error', reject)
195195
checker.on('error', reject)
196-
checker.on('verified', algo => {
197-
resolve(algo)
196+
checker.on('verified', meta => {
197+
resolve(meta)
198198
})
199199
})
200200
}
@@ -207,7 +207,7 @@ function createCheckerStream (sri, opts) {
207207
const algorithm = Object.keys(sri).reduce((acc, algo) => {
208208
return pickAlgorithm(acc, algo) || acc
209209
})
210-
const digests = sri[algorithm].map(m => m.digest)
210+
const digests = sri[algorithm]
211211
const hash = crypto.createHash(algorithm)
212212
const stream = new Transform({
213213
transform: function (chunk, enc, cb) {
@@ -216,8 +216,9 @@ function createCheckerStream (sri, opts) {
216216
},
217217
flush: function (cb) {
218218
const digest = hash.digest('base64')
219-
if (digests.some(d => d === digest)) {
220-
stream.emit('verified', algorithm)
219+
const match = digests.find(meta => meta.digest === digest)
220+
if (match) {
221+
stream.emit('verified', match)
221222
return cb()
222223
} else {
223224
const err = new Error(`${algorithm} integrity checksum failed`)

test/check.js

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,43 +22,44 @@ test('checkData', t => {
2222
algorithm: 'sha512',
2323
digest: hash(TEST_DATA, 'sha512')
2424
})
25-
t.equal(
25+
const meta = sri['sha512'][0]
26+
t.deepEqual(
2627
ssri.checkData(TEST_DATA, sri),
27-
'sha512',
28+
meta,
2829
'Buffer data successfully verified'
2930
)
30-
t.equal(
31+
t.deepEqual(
3132
ssri.checkData(TEST_DATA, `sha512-${hash(TEST_DATA, 'sha512')}`),
32-
'sha512',
33+
meta,
3334
'Accepts string SRI'
3435
)
35-
t.equal(
36+
t.deepEqual(
3637
ssri.checkData(TEST_DATA, {
3738
algorithm: 'sha512',
3839
digest: hash(TEST_DATA, 'sha512')
3940
}),
40-
'sha512',
41+
meta,
4142
'Accepts IntegrityMetadata-like SRI'
4243
)
43-
t.equal(
44+
t.deepEqual(
4445
ssri.checkData(TEST_DATA.toString('utf8'), sri),
45-
'sha512',
46+
meta,
4647
'String data successfully verified'
4748
)
48-
t.equal(
49+
t.deepEqual(
4950
ssri.checkData(
5051
TEST_DATA,
5152
`sha512-nope sha512-${hash(TEST_DATA, 'sha512')}`
5253
),
53-
'sha512',
54+
meta,
5455
'succeeds if any of the hashes under the chosen algorithm match'
5556
)
5657
t.equal(
5758
ssri.checkData('nope', sri),
5859
false,
5960
'returns false when verification fails'
6061
)
61-
t.equal(
62+
t.deepEqual(
6263
ssri.checkData(TEST_DATA, [
6364
'sha512-nope',
6465
`sha1-${hash(TEST_DATA, 'sha1')}`,
@@ -68,16 +69,20 @@ test('checkData', t => {
6869
if (a === 'sha1' || b === 'sha1') { return 'sha1' }
6970
}
7071
}),
71-
'sha1',
72+
ssri.parse({
73+
algorithm: 'sha1', digest: hash(TEST_DATA, 'sha1')
74+
})['sha1'][0],
7275
'opts.pickAlgorithm can be used to customize which one is used.'
7376
)
74-
t.equal(
77+
t.deepEqual(
7578
ssri.checkData(TEST_DATA, [
7679
`sha1-${hash(TEST_DATA, 'sha1')}`,
7780
`sha384-${hash(TEST_DATA, 'sha384')}`,
7881
`sha256-${hash(TEST_DATA, 'sha256')}`
7982
].join(' ')),
80-
'sha384',
83+
ssri.parse({
84+
algorithm: 'sha384', digest: hash(TEST_DATA, 'sha384')
85+
})['sha384'][0],
8186
'picks the "strongest" available algorithm, by default'
8287
)
8388
t.done()
@@ -88,31 +93,32 @@ test('checkStream', t => {
8893
algorithm: 'sha512',
8994
digest: hash(TEST_DATA, 'sha512')
9095
})
96+
const meta = sri['sha512'][0]
9197
let streamEnded
9298
const stream = fileStream().on('end', () => { streamEnded = true })
93-
return ssri.checkStream(stream, sri).then(algo => {
94-
t.equal(algo, 'sha512', 'Stream data successfully verified')
99+
return ssri.checkStream(stream, sri).then(res => {
100+
t.deepEqual(res, meta, 'Stream data successfully verified')
95101
t.ok(streamEnded, 'source stream ended')
96102
return ssri.checkStream(
97103
fileStream(),
98104
`sha512-${hash(TEST_DATA, 'sha512')}`
99105
)
100-
}).then(algo => {
101-
t.equal(algo, 'sha512', 'Accepts string SRI')
106+
}).then(res => {
107+
t.deepEqual(res, meta, 'Accepts string SRI')
102108
return ssri.checkStream(fileStream(), {
103109
algorithm: 'sha512',
104110
digest: hash(TEST_DATA, 'sha512')
105111
})
106-
}).then(algo => {
107-
t.equal(algo, 'sha512', 'Accepts IntegrityMetadata-like SRI')
112+
}).then(res => {
113+
t.deepEqual(res, meta, 'Accepts IntegrityMetadata-like SRI')
108114
return ssri.checkStream(
109115
fileStream(),
110116
`sha512-nope sha512-${hash(TEST_DATA, 'sha512')}`
111117
)
112-
}).then(algo => {
113-
t.equal(
114-
algo,
115-
'sha512',
118+
}).then(res => {
119+
t.deepEqual(
120+
res,
121+
meta,
116122
'succeeds if any of the hashes under the chosen algorithm match'
117123
)
118124
return ssri.checkStream(
@@ -133,21 +139,25 @@ test('checkStream', t => {
133139
if (a === 'sha1' || b === 'sha1') { return 'sha1' }
134140
}
135141
})
136-
}).then(algo => {
137-
t.equal(
138-
algo,
139-
'sha1',
142+
}).then(res => {
143+
t.deepEqual(
144+
res,
145+
ssri.parse({
146+
algorithm: 'sha1', digest: hash(TEST_DATA, 'sha1')
147+
})['sha1'][0],
140148
'opts.pickAlgorithm can be used to customize which one is used.'
141149
)
142150
return ssri.checkStream(fileStream(), [
143151
`sha1-${hash(TEST_DATA, 'sha1')}`,
144152
`sha384-${hash(TEST_DATA, 'sha384')}`,
145153
`sha256-${hash(TEST_DATA, 'sha256')}`
146154
].join(' '))
147-
}).then(algo => {
148-
t.equal(
149-
algo,
150-
'sha384',
155+
}).then(res => {
156+
t.deepEqual(
157+
res,
158+
ssri.parse({
159+
algorithm: 'sha384', digest: hash(TEST_DATA, 'sha384')
160+
})['sha384'][0],
151161
'picks the "strongest" available algorithm, by default'
152162
)
153163
})

0 commit comments

Comments
 (0)