From 2301e749ec5435384460632ea76d9bc5148277ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kat=20March=C3=A1n?= Date: Sat, 1 Apr 2017 22:46:43 -0700 Subject: [PATCH] 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. --- README.md | 13 ++++++--- index.js | 15 ++++++----- test/check.js | 74 +++++++++++++++++++++++++++++---------------------- 3 files changed, 59 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 91e1fdb..7a13804 100644 --- a/README.md +++ b/README.md @@ -249,7 +249,7 @@ ssri.fromStream(fs.createReadStream('index.js'), { }) // succeeds ``` -#### `> ssri.checkData(data, sri, [opts]) -> Algorithm|false` +#### `> 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 @@ -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 ssri.checkStream( fs.createReadStream('index.js'), 'sha1-BaDDigEST' -) // -> Promise> +) // -> Promise> ``` #### `> createCheckerStream(sri, [opts]) -> CheckerStream` diff --git a/index.js b/index.js index 7371494..f90d3fb 100644 --- a/index.js +++ b/index.js @@ -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 @@ -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) }) }) } @@ -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) { @@ -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`) diff --git a/test/check.js b/test/check.js index 1b0f24e..22f951e 100644 --- a/test/check.js +++ b/test/check.js @@ -22,35 +22,36 @@ 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( @@ -58,7 +59,7 @@ test('checkData', t => { false, 'returns false when verification fails' ) - t.equal( + t.deepEqual( ssri.checkData(TEST_DATA, [ 'sha512-nope', `sha1-${hash(TEST_DATA, 'sha1')}`, @@ -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() @@ -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( @@ -133,10 +139,12 @@ 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(), [ @@ -144,10 +152,12 @@ test('checkStream', t => { `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' ) })