diff --git a/README.md b/README.md index d5dcbbd..3b6fba0 100644 --- a/README.md +++ b/README.md @@ -346,6 +346,10 @@ or an error happens with `stream`, the Promise will be rejected. If the Promise is rejected because verification failed, the returned error will have `err.code` as `EBADCHECKSUM`. +If `opts.size` is given, it will be matched against the stream size. An error +with `err.code` `EBADSIZE` will be returned by a rejection if the expected size +and actual size fail to match. + If `opts.pickAlgorithm` is provided, it will be used by [`Integrity#pickAlgorithm`](#integrity-pick-algorithm) when deciding which of the available digests to match against. @@ -385,6 +389,10 @@ against `sri`. `sri` can be any subresource integrity representation that If verification fails, the returned stream will error with an `EBADCHECKSUM` error code. +If `opts.size` is given, it will be matched against the stream size. An error +with `err.code` `EBADSIZE` will be emitted by the stream if the expected size +and actual size fail to match. + If `opts.pickAlgorithm` is provided, it will be passed two algorithms as arguments. ssri will prioritize whichever of the two algorithms is returned by this function. Note that the function may be called multiple times, and it diff --git a/index.js b/index.js index 038b639..8141612 100644 --- a/index.js +++ b/index.js @@ -229,23 +229,34 @@ function createCheckerStream (sri, opts) { const algorithm = sri.pickAlgorithm(opts) const digests = sri[algorithm] const hash = crypto.createHash(algorithm) + let streamSize = 0 const stream = new Transform({ transform: function (chunk, enc, cb) { + streamSize += chunk.length hash.update(chunk, enc) cb(null, chunk, enc) }, flush: function (cb) { const digest = hash.digest('base64') const match = digests.find(meta => meta.digest === digest) - if (match) { + if (typeof opts.size === 'number' && streamSize !== opts.size) { + const err = new Error(`stream size mismatch when checking ${sri}.\n Wanted: ${opts.size}\n Found: ${streamSize}`) + err.code = 'EBADSIZE' + err.found = streamSize + err.expected = opts.size + err.sri = sri + return cb(err) + } else if (match) { + stream.emit('size', streamSize) stream.emit('verified', match) return cb() } else { - const err = new Error(`${algorithm} integrity checksum failed`) + const err = new Error(`${sri} integrity checksum failed when using ${algorithm}`) err.code = 'EBADCHECKSUM' err.found = digest err.expected = digests err.algorithm = algorithm + err.sri = sri return cb(err) } } diff --git a/test/check.js b/test/check.js index 22f951e..419f521 100644 --- a/test/check.js +++ b/test/check.js @@ -160,5 +160,14 @@ test('checkStream', t => { })['sha384'][0], 'picks the "strongest" available algorithm, by default' ) + return ssri.checkStream( + fileStream(), `sha256-${hash(TEST_DATA, 'sha256')}`, { + size: TEST_DATA.length - 1 + } + ).then(() => { + throw new Error('unexpected success') + }, err => { + t.equal(err.code, 'EBADSIZE', 'size check failure rejects the promise') + }) }) })