diff --git a/README.md b/README.md index 8d2e0e3..b4fc40a 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ ssri.checkData(fs.readFileSync('./my-file'), integrity) // => 'sha512' * Strict standard compliance. * `?foo` metadata option support. * Multiple entries for the same algorithm. -* Object-based integrity metadata manipulation. +* Object-based integrity hash manipulation. * Small footprint: no dependencies, concise implementation. * Full test coverage. * Customizable algorithm picker. @@ -80,9 +80,9 @@ jump in if you'd like to, or even ask us questions if something isn't clear. #### `> ssri.parse(sri, [opts]) -> Integrity` Parses `sri` into an `Integrity` data structure. `sri` can be an integrity -string, an `IntegrityMetadata`-like with `digest` and `algorithm` fields and an -optional `options` field, or an `Integrity`-like object. The resulting object -will be an `Integrity` instance that has this shape: +string, an `Hash`-like with `digest` and `algorithm` fields and an optional +`options` field, or an `Integrity`-like object. The resulting object will be an +`Integrity` instance that has this shape: ```javascript { @@ -94,9 +94,9 @@ will be an `Integrity` instance that has this shape: } ``` -If `opts.single` is truthy, a single `IntegrityMetadata` object will be -returned. That is, a single object that looks like `{algorithm, digest, -options}`, as opposed to a larger object with multiple of these. +If `opts.single` is truthy, a single `Hash` object will be returned. That is, a +single object that looks like `{algorithm, digest, options}`, as opposed to a +larger object with multiple of these. If `opts.strict` is truthy, the resulting object will be filtered such that it strictly follows the Subresource Integrity spec, throwing away any entries @@ -116,7 +116,7 @@ ssri.parse('sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/ This function is identical to [`Integrity#toString()`](#integrity-to-string), except it can be used on _any_ object that [`parse`](#parse) can handle -- that -is, a string, an `IntegrityMetadata`-like, or an `Integrity`-like. +is, a string, an `Hash`-like, or an `Integrity`-like. The `opts.sep` option defines the string to use when joining multiple entries together. To be spec-compliant, this _must_ be whitespace. The default is a @@ -132,7 +132,7 @@ parsing rules. See [`ssri.parse`](#parse). ssri.stringify('\n\rsha512-foo\n\t\tsha384-bar') // -> 'sha512-foo sha384-bar' -// IntegrityMetadata-like: only a single entry. +// Hash-like: only a single entry. ssri.stringify({ algorithm: 'sha512', digest:'9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==', @@ -157,8 +157,8 @@ ssri.stringify({ #### `> Integrity#concat(otherIntegrity, [opts]) -> Integrity` -Concatenates an `Integrity` object with another IntegrityLike, or a string -representing integrity metadata. +Concatenates an `Integrity` object with another IntegrityLike, or an integrity +string. This is functionally equivalent to concatenating the string format of both integrity arguments, and calling [`ssri.parse`](#ssri-parse) on the new string. @@ -183,7 +183,7 @@ desktopIntegrity.concat(mobileIntegrity) #### `> Integrity#toString([opts]) -> String` -Returns the string representation of an `Integrity` object. All metadata entries +Returns the string representation of an `Integrity` object. All hash entries will be concatenated in the string by `opts.sep`, which defaults to `' '`. If you want to serialize an object that didn't from from an `ssri` function, @@ -219,9 +219,9 @@ ssri.parse('sha1-WEakDigEST sha512-yzd8ELD1piyANiWnmdnpCL5F52f10UfUdEkHywVZeqTt0 #### `> Integrity#hexDigest() -> String` -`Integrity` is assumed to be either a single-hash `Integrity` instance, or an -`IntegrityMetadata` instance. Returns its `digest`, converted to a hex -representation of the base64 data. +`Integrity` is assumed to be either a single-hash `Integrity` instance, or a +`Hash` instance. Returns its `digest`, converted to a hex representation of the +base64 data. ##### Example @@ -240,7 +240,7 @@ algorithm + '-' + Buffer.from(hexDigest, 'hex').toString('base64') ``` `opts.options` may optionally be passed in: it must be an array of option -strings that will be added to all generated integrity metadata generated by +strings that will be added to all generated integrity hashes generated by `fromData`. This is a loosely-specified feature of SRIs, and currently has no specified semantics besides being `?`-separated. Use at your own risk, and probably avoid if your integrity strings are meant to be used with browsers. @@ -248,7 +248,7 @@ probably avoid if your integrity strings are meant to be used with browsers. If `opts.strict` is true, the integrity object will be created using strict parsing rules. See [`ssri.parse`](#parse). -If `opts.single` is true, a single `IntegrityMetadata` object will be returned. +If `opts.single` is true, a single `Hash` object will be returned. ##### Example @@ -261,13 +261,13 @@ ssri.fromHex('75e69d6de79f', 'sha1').toString() // 'sha1-deadbeef' Creates an `Integrity` object from either string or `Buffer` data, calculating all the requested hashes and adding any specified options to the object. -`opts.algorithms` determines which algorithms to generate metadata for. All +`opts.algorithms` determines which algorithms to generate hashes for. All results will be included in a single `Integrity` object. The default value for `opts.algorithms` is `['sha512']`. All algorithm strings must be hashes listed in `crypto.getHashes()` for the host Node.js platform. `opts.options` may optionally be passed in: it must be an array of option -strings that will be added to all generated integrity metadata generated by +strings that will be added to all generated integrity hashes generated by `fromData`. This is a loosely-specified feature of SRIs, and currently has no specified semantics besides being `?`-separated. Use at your own risk, and probably avoid if your integrity strings are meant to be used with browsers. @@ -312,7 +312,7 @@ ssri.fromStream(fs.createReadStream('index.js'), { }) // succeeds ``` -#### `> ssri.checkData(data, sri, [opts]) -> IntegrityMetadata|false` +#### `> ssri.checkData(data, sri, [opts]) -> Hash|false` Verifies `data` integrity against an `sri` argument. `data` may be either a `String` or a `Buffer`, and `sri` can be any subresource integrity @@ -334,14 +334,14 @@ ssri.checkData(data, 'sha256-l981iLWj8kurw4UbNy8Lpxqdzd7UOxS50Glhv8FwfZ0') ssri.checkData(data, 'sha1-BaDDigEST') // -> false ``` -#### `> ssri.checkStream(stream, sri, [opts]) -> Promise` +#### `> ssri.checkStream(stream, sri, [opts]) -> Promise` Verifies the contents of `stream` against an `sri` argument. `stream` will be consumed in its entirety by this process. `sri` can be any subresource integrity representation that [`ssri.parse`](#parse) can handle. `checkStream` will return a Promise that either resolves to the -`IntegrityMetadata` that succeeded verification, or, if the verification fails +`Hash` that succeeded verification, or, if the verification fails or an error happens with `stream`, the Promise will be rejected. If the Promise is rejected because verification failed, the returned error will @@ -373,7 +373,7 @@ ssri.checkStream( ssri.checkStream( fs.createReadStream('index.js'), 'sha256-l981iLWj8kurw4UbNy8Lpxqdzd7UOxS50Glhv8FwfZ0' -) // -> Promise +) // -> Promise ssri.checkStream( fs.createReadStream('index.js'), @@ -392,15 +392,13 @@ data, respectively. If `opts.algorithms` is passed in, the listed algorithms will be calculated when generating the final `Integrity` instance. The default is `['sha512']`. -If `opts.single` is passed in, a single `IntegrityMetadata` instance will be -returned. +If `opts.single` is passed in, a single `Hash` instance will be returned. If `opts.integrity` is passed in, it should be an `integrity` value understood by [`parse`](#parse) that the stream will check the data against. If verification succeeds, the integrity stream will emit a `verified` event whose -value is a single `IntegrityMetadata` object that is the one that succeeded -verification. If verification fails, the stream will error with an -`EBADCHECKSUM` error code. +value is a single `Hash` object that is the one that succeeded verification. If +verification fails, the 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 diff --git a/index.js b/index.js index 18fb24b..4123967 100644 --- a/index.js +++ b/index.js @@ -10,12 +10,12 @@ const SRI_REGEX = /^([^-]+)-([^?]+)([?\S*]*)$/ const STRICT_SRI_REGEX = /^([^-]+)-([A-Za-z0-9+/]+(?:=?=?))([?\x21-\x7E]*)$/ const VCHAR_REGEX = /^[\x21-\x7E]+$/ -class IntegrityMetadata { - get isIntegrityMetadata () { return true } - constructor (metadata, opts) { +class Hash { + get isHash () { return true } + constructor (hash, opts) { const strict = !!(opts && opts.strict) - this.source = metadata.trim() - // 3.1. Integrity metadata + this.source = hash.trim() + // 3.1. Integrity metadata (called "Hash" by ssri) // https://w3c.github.io/webappsec-subresource-integrity/#integrity-metadata-description const match = this.source.match( strict @@ -71,8 +71,8 @@ class Integrity { sep = sep.replace(/\S+/g, ' ') } return Object.keys(this).map(k => { - return this[k].map(meta => { - return IntegrityMetadata.prototype.toString.call(meta, opts) + return this[k].map(hash => { + return Hash.prototype.toString.call(hash, opts) }).filter(x => x.length).join(sep) }).filter(x => x.length).join(sep) } @@ -111,14 +111,14 @@ function _parse (integrity, opts) { // 3.4.3. Parse metadata // https://w3c.github.io/webappsec-subresource-integrity/#parse-metadata if (opts.single) { - return new IntegrityMetadata(integrity, opts) + return new Hash(integrity, opts) } return integrity.trim().split(/\s+/).reduce((acc, string) => { - const metadata = new IntegrityMetadata(string, opts) - if (metadata.algorithm && metadata.digest) { - const algo = metadata.algorithm + const hash = new Hash(string, opts) + if (hash.algorithm && hash.digest) { + const algo = hash.algorithm if (!acc[algo]) { acc[algo] = [] } - acc[algo].push(metadata) + acc[algo].push(hash) } return acc }, new Integrity()) @@ -127,7 +127,7 @@ function _parse (integrity, opts) { module.exports.stringify = stringify function stringify (obj, opts) { if (obj.algorithm && obj.digest) { - return IntegrityMetadata.prototype.toString.call(obj, opts) + return Hash.prototype.toString.call(obj, opts) } else if (typeof obj === 'string') { return stringify(parse(obj, opts), opts) } else { @@ -156,14 +156,14 @@ function fromData (data, opts) { : '' return algorithms.reduce((acc, algo) => { const digest = crypto.createHash(algo).update(data).digest('base64') - const meta = new IntegrityMetadata( + const hash = new Hash( `${algo}-${digest}${optString}`, opts ) - if (meta.algorithm && meta.digest) { - const algo = meta.algorithm + if (hash.algorithm && hash.digest) { + const algo = hash.algorithm if (!acc[algo]) { acc[algo] = [] } - acc[algo].push(meta) + acc[algo].push(hash) } return acc }, new Integrity()) @@ -192,7 +192,7 @@ function checkData (data, sri, opts) { const algorithm = sri.pickAlgorithm(opts) const digests = sri[algorithm] const digest = crypto.createHash(algorithm).update(data).digest('base64') - return digests.find(meta => meta.digest === digest) || false + return digests.find(hash => hash.digest === digest) || false } module.exports.checkStream = checkStream @@ -243,9 +243,9 @@ function integrityStream (opts) { const match = ( // Integrity verification mode opts.integrity && - digests.find(meta => { - return newSri[algorithm].find(newmeta => { - return meta.digest === newmeta.digest + digests.find(hash => { + return newSri[algorithm].find(newhash => { + return hash.digest === newhash.digest }) }) ) diff --git a/test/check.js b/test/check.js index 419f521..e38334a 100644 --- a/test/check.js +++ b/test/check.js @@ -39,7 +39,7 @@ test('checkData', t => { digest: hash(TEST_DATA, 'sha512') }), meta, - 'Accepts IntegrityMetadata-like SRI' + 'Accepts Hash-like SRI' ) t.deepEqual( ssri.checkData(TEST_DATA.toString('utf8'), sri), @@ -110,7 +110,7 @@ test('checkStream', t => { digest: hash(TEST_DATA, 'sha512') }) }).then(res => { - t.deepEqual(res, meta, 'Accepts IntegrityMetadata-like SRI') + t.deepEqual(res, meta, 'Accepts Hash-like SRI') return ssri.checkStream( fileStream(), `sha512-nope sha512-${hash(TEST_DATA, 'sha512')}` diff --git a/test/integrity.js b/test/integrity.js index b90e4fb..661a942 100644 --- a/test/integrity.js +++ b/test/integrity.js @@ -34,7 +34,7 @@ test('concat()', t => { t.equal( sri.concat({digest: 'bar', algorithm: 'sha384'}).toString(), 'sha512-foo sha384-bar', - 'concatenates with an IntegrityMetadata-like' + 'concatenates with an Hash-like' ) t.equal( sri.concat({ @@ -97,12 +97,12 @@ test('hexDigest()', t => { t.done() }) -test('isIntegrity and isIntegrityMetadata', t => { +test('isIntegrity and isHash', t => { const sri = ssri.parse('sha512-bar') t.ok(sri.isIntegrity, 'full sri has !!.isIntegrity') t.ok( - sri['sha512'][0].isIntegrityMetadata, - 'sri hash has !!.isIntegrityMetadata' + sri['sha512'][0].isHash, + 'sri hash has !!.isHash' ) t.done() }) diff --git a/test/parse.js b/test/parse.js index 67e953f..cad5a88 100644 --- a/test/parse.js +++ b/test/parse.js @@ -26,7 +26,7 @@ test('parses single-entry integrity string', t => { t.done() }) -test('can parse single-entry string directly into IntegrityMetadata', t => { +test('can parse single-entry string directly into Hash', t => { const sha = hash(TEST_DATA, 'sha512') const integrity = `sha512-${sha}` t.deepEqual(ssri.parse(integrity, {single: true}), { @@ -34,11 +34,11 @@ test('can parse single-entry string directly into IntegrityMetadata', t => { digest: sha, algorithm: 'sha512', options: [] - }, 'single entry parsed into single IntegrityMetadata instance') + }, 'single entry parsed into single Hash instance') t.done() }) -test('accepts IntegrityMetadata-likes as input', t => { +test('accepts Hash-likes as input', t => { const algorithm = 'sha512' const digest = hash(TEST_DATA, 'sha512') const sriLike = { diff --git a/test/stringify.js b/test/stringify.js index 2782692..727efbd 100644 --- a/test/stringify.js +++ b/test/stringify.js @@ -35,7 +35,7 @@ test('serializes Integrity-likes', t => { t.done() }) -test('serializes IntegrityMetadata-likes', t => { +test('serializes Hash-likes', t => { const sriLike = { digest: 'foo', algorithm: 'sha512'