Skip to content

Commit

Permalink
feat(check): return IntegrityMetadata on check success
Browse files Browse the repository at this point in the history
BREAKING CHANGE: `checkData`, `checkStream`, and `createCheckerStream` now yield a whole IntegrityMetadata instance representing the first successful hash match.
  • Loading branch information
zkat committed Apr 2, 2017
1 parent a06455f commit 2301e74
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 43 deletions.
13 changes: 9 additions & 4 deletions README.md
Expand Up @@ -249,7 +249,7 @@ ssri.fromStream(fs.createReadStream('index.js'), {
}) // succeeds
```

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

Verifies `data` integrity against an `sri` argument. `data` may be either a
`String` or a `Buffer`, and `sri` can be any subresource integrity
Expand Down Expand Up @@ -302,17 +302,22 @@ const integrity = ssri.fromData(fs.readFileSync('index.js'))
ssri.checkStream(
fs.createReadStream('index.js'),
integrity
) // -> Promise<'sha512'>
)
// ->
// Promise<{
// algorithm: 'sha512',
// digest: 'sha512-yzd8ELD1piyANiWnmdnpCL5F52f10UfUdEkHywVZeqTt0ymgrxR63Qz0GB7TKPoeeZQmWCaz7T1'
// }>

ssri.checkStream(
fs.createReadStream('index.js'),
'sha256-l981iLWj8kurw4UbNy8Lpxqdzd7UOxS50Glhv8FwfZ0'
) // -> Promise<'sha256'>
) // -> Promise<IntegrityMetadata>

ssri.checkStream(
fs.createReadStream('index.js'),
'sha1-BaDDigEST'
) // -> Promise<Error<EBADCHECKSUM>>
) // -> Promise<Error<{code: 'EBADCHECKSUM'}>>
```

#### <a name="create-checker-stream"></a> `> createCheckerStream(sri, [opts]) -> CheckerStream`
Expand Down
15 changes: 8 additions & 7 deletions index.js
Expand Up @@ -179,9 +179,9 @@ function checkData (data, sri, opts) {
const algorithm = Object.keys(sri).reduce((acc, algo) => {
return pickAlgorithm(acc, algo) || acc
})
const digests = sri[algorithm].map(m => m.digest)
const digests = sri[algorithm]
const digest = crypto.createHash(algorithm).update(data).digest('base64')
return digests.some(d => d === digest) && algorithm
return digests.find(meta => meta.digest === digest) || false
}

module.exports.checkStream = checkStream
Expand All @@ -193,8 +193,8 @@ function checkStream (stream, sri, opts) {
stream.pipe(checker)
stream.on('error', reject)
checker.on('error', reject)
checker.on('verified', algo => {
resolve(algo)
checker.on('verified', meta => {
resolve(meta)
})
})
}
Expand All @@ -207,7 +207,7 @@ function createCheckerStream (sri, opts) {
const algorithm = Object.keys(sri).reduce((acc, algo) => {
return pickAlgorithm(acc, algo) || acc
})
const digests = sri[algorithm].map(m => m.digest)
const digests = sri[algorithm]
const hash = crypto.createHash(algorithm)
const stream = new Transform({
transform: function (chunk, enc, cb) {
Expand All @@ -216,8 +216,9 @@ function createCheckerStream (sri, opts) {
},
flush: function (cb) {
const digest = hash.digest('base64')
if (digests.some(d => d === digest)) {
stream.emit('verified', algorithm)
const match = digests.find(meta => meta.digest === digest)
if (match) {
stream.emit('verified', match)
return cb()
} else {
const err = new Error(`${algorithm} integrity checksum failed`)
Expand Down
74 changes: 42 additions & 32 deletions test/check.js
Expand Up @@ -22,43 +22,44 @@ test('checkData', t => {
algorithm: 'sha512',
digest: hash(TEST_DATA, 'sha512')
})
t.equal(
const meta = sri['sha512'][0]
t.deepEqual(
ssri.checkData(TEST_DATA, sri),
'sha512',
meta,
'Buffer data successfully verified'
)
t.equal(
t.deepEqual(
ssri.checkData(TEST_DATA, `sha512-${hash(TEST_DATA, 'sha512')}`),
'sha512',
meta,
'Accepts string SRI'
)
t.equal(
t.deepEqual(
ssri.checkData(TEST_DATA, {
algorithm: 'sha512',
digest: hash(TEST_DATA, 'sha512')
}),
'sha512',
meta,
'Accepts IntegrityMetadata-like SRI'
)
t.equal(
t.deepEqual(
ssri.checkData(TEST_DATA.toString('utf8'), sri),
'sha512',
meta,
'String data successfully verified'
)
t.equal(
t.deepEqual(
ssri.checkData(
TEST_DATA,
`sha512-nope sha512-${hash(TEST_DATA, 'sha512')}`
),
'sha512',
meta,
'succeeds if any of the hashes under the chosen algorithm match'
)
t.equal(
ssri.checkData('nope', sri),
false,
'returns false when verification fails'
)
t.equal(
t.deepEqual(
ssri.checkData(TEST_DATA, [
'sha512-nope',
`sha1-${hash(TEST_DATA, 'sha1')}`,
Expand All @@ -68,16 +69,20 @@ test('checkData', t => {
if (a === 'sha1' || b === 'sha1') { return 'sha1' }
}
}),
'sha1',
ssri.parse({
algorithm: 'sha1', digest: hash(TEST_DATA, 'sha1')
})['sha1'][0],
'opts.pickAlgorithm can be used to customize which one is used.'
)
t.equal(
t.deepEqual(
ssri.checkData(TEST_DATA, [
`sha1-${hash(TEST_DATA, 'sha1')}`,
`sha384-${hash(TEST_DATA, 'sha384')}`,
`sha256-${hash(TEST_DATA, 'sha256')}`
].join(' ')),
'sha384',
ssri.parse({
algorithm: 'sha384', digest: hash(TEST_DATA, 'sha384')
})['sha384'][0],
'picks the "strongest" available algorithm, by default'
)
t.done()
Expand All @@ -88,31 +93,32 @@ test('checkStream', t => {
algorithm: 'sha512',
digest: hash(TEST_DATA, 'sha512')
})
const meta = sri['sha512'][0]
let streamEnded
const stream = fileStream().on('end', () => { streamEnded = true })
return ssri.checkStream(stream, sri).then(algo => {
t.equal(algo, 'sha512', 'Stream data successfully verified')
return ssri.checkStream(stream, sri).then(res => {
t.deepEqual(res, meta, 'Stream data successfully verified')
t.ok(streamEnded, 'source stream ended')
return ssri.checkStream(
fileStream(),
`sha512-${hash(TEST_DATA, 'sha512')}`
)
}).then(algo => {
t.equal(algo, 'sha512', 'Accepts string SRI')
}).then(res => {
t.deepEqual(res, meta, 'Accepts string SRI')
return ssri.checkStream(fileStream(), {
algorithm: 'sha512',
digest: hash(TEST_DATA, 'sha512')
})
}).then(algo => {
t.equal(algo, 'sha512', 'Accepts IntegrityMetadata-like SRI')
}).then(res => {
t.deepEqual(res, meta, 'Accepts IntegrityMetadata-like SRI')
return ssri.checkStream(
fileStream(),
`sha512-nope sha512-${hash(TEST_DATA, 'sha512')}`
)
}).then(algo => {
t.equal(
algo,
'sha512',
}).then(res => {
t.deepEqual(
res,
meta,
'succeeds if any of the hashes under the chosen algorithm match'
)
return ssri.checkStream(
Expand All @@ -133,21 +139,25 @@ test('checkStream', t => {
if (a === 'sha1' || b === 'sha1') { return 'sha1' }
}
})
}).then(algo => {
t.equal(
algo,
'sha1',
}).then(res => {
t.deepEqual(
res,
ssri.parse({
algorithm: 'sha1', digest: hash(TEST_DATA, 'sha1')
})['sha1'][0],
'opts.pickAlgorithm can be used to customize which one is used.'
)
return ssri.checkStream(fileStream(), [
`sha1-${hash(TEST_DATA, 'sha1')}`,
`sha384-${hash(TEST_DATA, 'sha384')}`,
`sha256-${hash(TEST_DATA, 'sha256')}`
].join(' '))
}).then(algo => {
t.equal(
algo,
'sha384',
}).then(res => {
t.deepEqual(
res,
ssri.parse({
algorithm: 'sha384', digest: hash(TEST_DATA, 'sha384')
})['sha384'][0],
'picks the "strongest" available algorithm, by default'
)
})
Expand Down

0 comments on commit 2301e74

Please sign in to comment.