Comparing version 1.0.0 to 2.0.0
@@ -5,2 +5,29 @@ # Change Log | ||
<a name="2.0.0"></a> | ||
# [2.0.0](https://github.com/zkat/ssri/compare/v1.0.0...v2.0.0) (2017-03-24) | ||
### Bug Fixes | ||
* **strict-mode:** make regexes more rigid ([122a32c](https://github.com/zkat/ssri/commit/122a32c)) | ||
### Features | ||
* **api:** added serialize alias for unparse ([999b421](https://github.com/zkat/ssri/commit/999b421)) | ||
* **concat:** add Integrity#concat() ([cae12c7](https://github.com/zkat/ssri/commit/cae12c7)) | ||
* **pickAlgo:** pick the strongest algorithm provided, by default ([58c18f7](https://github.com/zkat/ssri/commit/58c18f7)) | ||
* **strict-mode:** strict SRI support ([3f0b64c](https://github.com/zkat/ssri/commit/3f0b64c)) | ||
* **stringify:** replaced unparse/serialize with stringify ([4acad30](https://github.com/zkat/ssri/commit/4acad30)) | ||
* **verification:** add opts.pickAlgorithm ([f72e658](https://github.com/zkat/ssri/commit/f72e658)) | ||
### BREAKING CHANGES | ||
* **pickAlgo:** ssri will prioritize specific hashes now | ||
* **stringify:** serialize and unparse have been removed. Use ssri.stringify instead. | ||
* **strict-mode:** functions that accepted an optional `sep` argument now expect `opts.sep`. | ||
<a name="1.0.0"></a> | ||
@@ -7,0 +34,0 @@ # 1.0.0 (2017-03-23) |
137
index.js
@@ -6,11 +6,22 @@ 'use strict' | ||
const SRI_REGEX = /([^-]+)-([^?]+)([?\S*]*)/ | ||
const SPEC_ALGORITHMS = ['sha256', 'sha384', 'sha512'] | ||
const BASE64_REGEX = /^[a-z0-9+/]+(?:=?=?)$/i | ||
const SRI_REGEX = /^([^-]+)-([^?]+)([?\S*]*)$/ | ||
const STRICT_SRI_REGEX = /^([^-]+)-([A-Za-z0-9+/]+(?:=?=?))([?\x21-\x7E]*)$/ | ||
const VCHAR_REGEX = /^[\x21-\x7E]+$/ | ||
class IntegrityMetadata { | ||
constructor (metadata) { | ||
this.source = metadata | ||
constructor (metadata, opts) { | ||
const strict = !!(opts && opts.strict) | ||
this.source = metadata.trim() | ||
// 3.1. Integrity metadata | ||
// https://w3c.github.io/webappsec-subresource-integrity/#integrity-metadata-description | ||
const match = metadata.match(SRI_REGEX) | ||
const match = this.source.match( | ||
strict | ||
? STRICT_SRI_REGEX | ||
: SRI_REGEX | ||
) | ||
if (!match) { return } | ||
if (strict && !SPEC_ALGORITHMS.some(a => a === match[1])) { return } | ||
this.algorithm = match[1] | ||
@@ -22,7 +33,27 @@ this.digest = match[2] | ||
} | ||
toString () { | ||
const opts = this.options && this.options.length | ||
toString (opts) { | ||
if (opts && opts.strict) { | ||
// Strict mode enforces the standard as close to the foot of the | ||
// letter as it can. | ||
if (!( | ||
// The spec has very restricted productions for algorithms. | ||
// https://www.w3.org/TR/CSP2/#source-list-syntax | ||
SPEC_ALGORITHMS.some(x => x === this.algorithm) && | ||
// Usually, if someone insists on using a "different" base64, we | ||
// leave it as-is, since there's multiple standards, and the | ||
// specified is not a URL-safe variant. | ||
// https://www.w3.org/TR/CSP2/#base64_value | ||
this.digest.match(BASE64_REGEX) && | ||
// Option syntax is strictly visual chars. | ||
// https://w3c.github.io/webappsec-subresource-integrity/#grammardef-option-expression | ||
// https://tools.ietf.org/html/rfc5234#appendix-B.1 | ||
(this.options || []).every(opt => opt.match(VCHAR_REGEX)) | ||
)) { | ||
return '' | ||
} | ||
} | ||
const options = this.options && this.options.length | ||
? `?${this.options.join('?')}` | ||
: '' | ||
return `${this.algorithm}-${this.digest}${opts}` | ||
return `${this.algorithm}-${this.digest}${options}` | ||
} | ||
@@ -32,18 +63,42 @@ } | ||
class Integrity { | ||
toString (sep) { | ||
sep = sep || ' ' | ||
toString (opts) { | ||
opts = opts || {} | ||
let sep = opts.sep || ' ' | ||
if (opts.strict) { | ||
// Entries must be separated by whitespace, according to spec. | ||
sep = sep.replace(/\S+/g, ' ') | ||
} | ||
return Object.keys(this).map(k => { | ||
return this[k].map(meta => { | ||
return IntegrityMetadata.prototype.toString.call(meta) | ||
}) | ||
}).join(sep) | ||
return IntegrityMetadata.prototype.toString.call(meta, opts) | ||
}).filter(x => x.length).join(sep) | ||
}).filter(x => x.length).join(sep) | ||
} | ||
concat (integrity, opts) { | ||
const other = typeof integrity === 'string' | ||
? integrity | ||
: stringify(integrity, opts) | ||
return parse(`${this.toString(opts)} ${other}`, opts) | ||
} | ||
} | ||
module.exports.parse = parse | ||
function parse (integrity) { | ||
function parse (sri, opts) { | ||
opts = opts || {} | ||
if (typeof sri === 'string') { | ||
return _parse(sri, opts) | ||
} else if (sri.algorithm && sri.digest) { | ||
const fullSri = new Integrity() | ||
fullSri[sri.algorithm] = [sri] | ||
return _parse(stringify(fullSri, opts), opts) | ||
} else { | ||
return _parse(stringify(sri, opts), opts) | ||
} | ||
} | ||
function _parse (integrity, opts) { | ||
// 3.4.3. Parse metadata | ||
// https://w3c.github.io/webappsec-subresource-integrity/#parse-metadata | ||
return integrity.trim().split(/\s+/).reduce((acc, string) => { | ||
const metadata = new IntegrityMetadata(string) | ||
const metadata = new IntegrityMetadata(string, opts) | ||
if (metadata.algorithm && metadata.digest) { | ||
@@ -58,8 +113,10 @@ const algo = metadata.algorithm | ||
module.exports.unparse = unparse | ||
function unparse (obj, sep) { | ||
module.exports.stringify = stringify | ||
function stringify (obj, opts) { | ||
if (obj.algorithm && obj.digest) { | ||
return IntegrityMetadata.prototype.toString.call(obj) | ||
return IntegrityMetadata.prototype.toString.call(obj, opts) | ||
} else if (typeof obj === 'string') { | ||
return stringify(parse(obj, opts), opts) | ||
} else { | ||
return Integrity.prototype.toString.call(obj, sep) | ||
return Integrity.prototype.toString.call(obj, opts) | ||
} | ||
@@ -77,3 +134,6 @@ } | ||
const digest = crypto.createHash(algo).update(data).digest('base64') | ||
const meta = new IntegrityMetadata(`${algo}-${digest}${optString}`) | ||
const meta = new IntegrityMetadata( | ||
`${algo}-${digest}${optString}`, | ||
opts | ||
) | ||
if (meta.algorithm && meta.digest) { | ||
@@ -104,3 +164,6 @@ const algo = meta.algorithm | ||
const digest = hash.digest('base64') | ||
const meta = new IntegrityMetadata(`${algo}-${digest}${optString}`) | ||
const meta = new IntegrityMetadata( | ||
`${algo}-${digest}${optString}`, | ||
opts | ||
) | ||
if (meta.algorithm && meta.digest) { | ||
@@ -120,15 +183,10 @@ const algo = meta.algorithm | ||
opts = opts || {} | ||
if (typeof sri === 'string') { | ||
sri = parse(sri) | ||
} else if (sri.algorithm && sri.digest) { | ||
const fullSri = new Integrity() | ||
fullSri[sri.algorithm] = [sri] | ||
sri = fullSri | ||
} | ||
sri = parse(sri, opts) | ||
const pickAlgorithm = opts.pickAlgorithm || getPrioritizedHash | ||
const algorithm = Object.keys(sri).reduce((acc, algo) => { | ||
return getPrioritizedHashFunction(acc, algo) || acc | ||
return pickAlgorithm(acc, algo) || acc | ||
}) | ||
const digests = sri[algorithm].map(m => m.digest) | ||
const digest = crypto.createHash(algorithm).update(data).digest('base64') | ||
return digests.some(d => d === digest) | ||
return digests.some(d => d === digest) && algorithm | ||
} | ||
@@ -154,11 +212,6 @@ | ||
opts = opts || {} | ||
if (typeof sri === 'string') { | ||
sri = parse(sri) | ||
} else if (sri.algorithm && sri.digest) { | ||
const fullSri = new Integrity() | ||
fullSri[sri.algorithm] = [sri] | ||
sri = fullSri | ||
} | ||
sri = parse(sri, opts) | ||
const pickAlgorithm = opts.pickAlgorithm || getPrioritizedHash | ||
const algorithm = Object.keys(sri).reduce((acc, algo) => { | ||
return getPrioritizedHashFunction(acc, algo) || acc | ||
return pickAlgorithm(acc, algo) || acc | ||
}) | ||
@@ -190,4 +243,10 @@ const digests = sri[algorithm].map(m => m.digest) | ||
function getPrioritizedHashFunction (algo1, algo2) { | ||
// Default implementaion is empty | ||
// This is a Best Effort™ at a reasonable priority for hash algos | ||
const DEFAULT_PRIORITY = [ | ||
'md5', 'whirlpool', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512' | ||
] | ||
function getPrioritizedHash (algo1, algo2) { | ||
return DEFAULT_PRIORITY.indexOf(algo1.toLowerCase()) >= DEFAULT_PRIORITY.indexOf(algo2.toLowerCase()) | ||
? algo1 | ||
: algo2 | ||
} |
{ | ||
"name": "ssri", | ||
"version": "1.0.0", | ||
"description": "Simple Subresource Integrity library -- generates, parses, and unparses integrity strings.", | ||
"version": "2.0.0", | ||
"description": "Standard Subresource Integrity library -- parses, serializes, generates, and verifies integrity metadata according to the SRI spec.", | ||
"main": "index.js", | ||
"files": [ | ||
"*.js", | ||
"lib" | ||
"*.js" | ||
], | ||
@@ -13,5 +12,5 @@ "scripts": { | ||
"postrelease": "npm publish && git push --follow-tags", | ||
"pretest": "standard lib test *.js", | ||
"pretest": "standard", | ||
"release": "standard-version -s", | ||
"test": "nyc --all -- tap -J test/*.js", | ||
"test": "tap -J --coverage test/*.js", | ||
"update-coc": "weallbehave -o . && git add CODE_OF_CONDUCT.md && git commit -m 'docs(coc): updated CODE_OF_CONDUCT.md'", | ||
@@ -23,2 +22,4 @@ "update-contrib": "weallcontribute -o . && git add CONTRIBUTING.md && git commit -m 'docs(contributing): updated CONTRIBUTING.md'" | ||
"w3c", | ||
"web", | ||
"security", | ||
"integrity", | ||
@@ -25,0 +26,0 @@ "checksum", |
299
README.md
# ssri [![npm version](https://img.shields.io/npm/v/ssri.svg)](https://npm.im/ssri) [![license](https://img.shields.io/npm/l/ssri.svg)](https://npm.im/ssri) [![Travis](https://img.shields.io/travis/zkat/ssri.svg)](https://travis-ci.org/zkat/ssri) [![AppVeyor](https://ci.appveyor.com/api/projects/status/github/zkat/ssri?svg=true)](https://ci.appveyor.com/project/zkat/ssri) [![Coverage Status](https://coveralls.io/repos/github/zkat/ssri/badge.svg?branch=latest)](https://coveralls.io/github/zkat/ssri?branch=latest) | ||
[`ssri`](https://github.com/zkat/ssri), short for Simple Subresource Integrity, | ||
is a Node.js utility for parsing, unparsing, and generating [Subresource | ||
[`ssri`](https://github.com/zkat/ssri), short for Standard Subresource | ||
Integrity, is a Node.js utility for parsing, manipulating, serializing, | ||
generating, and verifying [Subresource | ||
Integrity](https://w3c.github.io/webappsec/specs/subresourceintegrity/) hashes. | ||
@@ -19,3 +20,5 @@ | ||
* [`parse`](#parse) | ||
* [`unparse`](#unparse) | ||
* [`stringify`](#stringify) | ||
* [`Integrity#concat`](#integrity-concat) | ||
* [`Integrity#toString`](#integrity-to-string) | ||
* Integrity Generation | ||
@@ -38,9 +41,9 @@ * [`fromData`](#from-data) | ||
const parsed = ssri.parse(integrity) | ||
ssri.stringify(parsed) // === integrity (works on non-Integrity objects) | ||
parsed.toString() // === integrity | ||
ssri.unparse(parsed) // === integrity (works on non-Integrity objects) | ||
// Async stream functions | ||
ssri.checkStream(fs.createReadStream('./my-file'), parsed).then(...) | ||
ssri.checkStream(fs.createReadStream('./my-file'), integrity).then(...) | ||
ssri.fromStream(fs.createReadStream('./my-file')).then(sri => { | ||
sri.toString() === parsed.toString() | ||
sri.toString() === integrity | ||
}) | ||
@@ -51,3 +54,3 @@ fs.createReadStream('./my-file').pipe(ssri.createCheckerStream(sri)) | ||
ssri.fromData(fs.readFileSync('./my-file')) // === parsed | ||
ssri.checkData(fs.readFileSync('./my-file'), parsed) // => true | ||
ssri.checkData(fs.readFileSync('./my-file'), integrity) // => 'sha512' | ||
``` | ||
@@ -57,11 +60,285 @@ | ||
* Parses and unparses SRI strings. | ||
* Generates SRI strings from direct data or Streams. | ||
* Optional use of reserved option expression syntax. | ||
* Parses and stringifies SRI strings. | ||
* Generates SRI strings from raw data or Streams. | ||
* Strict standard compliance. | ||
* `?foo` metadata option support. | ||
* Multiple entries for the same algorithm. | ||
* Object-based integrity metadata manipulation. | ||
* Small footprint: no dependencies, concise implementation. | ||
* Full test coverage. | ||
* Customizable algorithm picker. | ||
### Contributing | ||
The ssri team enthusiastically welcomes contributions and project participation! There's a bunch of things you can do if you want to contribute! The [Contributor Guide](CONTRIBUTING.md) has all the information you need for everything from reporting bugs to contributing entire new features. Please don't hesitate to jump in if you'd like to, or even ask us questions if something isn't clear. | ||
The ssri team enthusiastically welcomes contributions and project participation! | ||
There's a bunch of things you can do if you want to contribute! The [Contributor | ||
Guide](CONTRIBUTING.md) has all the information you need for everything from | ||
reporting bugs to contributing entire new features. Please don't hesitate to | ||
jump in if you'd like to, or even ask us questions if something isn't clear. | ||
### API | ||
#### <a name="parse"></a> `> 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: | ||
```javascript | ||
{ | ||
'sha1': [{algorithm: 'sha1', digest: 'deadbeef', options: []}], | ||
'sha512': [ | ||
{algorithm: 'sha512', digest: 'c0ffee', options: []}, | ||
{algorithm: 'sha512', digest: 'bad1dea', options: ['foo']} | ||
], | ||
} | ||
``` | ||
If `opts.strict` is truthy, the resulting object will be filtered such that | ||
it strictly follows the Subresource Integrity spec, throwing away any entries | ||
with any invalid components. This also means a restricted set of algorithms | ||
will be used -- the spec limits them to `sha256`, `sha384`, and `sha512`. | ||
Strict mode is recommended if the integrity strings are intended for use in | ||
browsers, or in other situations where strict adherence to the spec is needed. | ||
##### Example | ||
```javascript | ||
ssri.parse('sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==?foo') // -> Integrity object | ||
``` | ||
#### <a name="stringify"></a> `> ssri.stringify(sri, [opts]) -> String` | ||
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. | ||
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 | ||
single space (`' '`). | ||
If `opts.strict` is true, the integrity string will be created using strict | ||
parsing rules. See [`ssri.parse`](#parse). | ||
##### Example | ||
```javascript | ||
// Useful for cleaning up input SRI strings: | ||
ssri.stringify('\n\rsha512-foo\n\t\tsha384-bar') | ||
// -> 'sha512-foo sha384-bar' | ||
// IntegrityMetadata-like: only a single entry. | ||
ssri.stringify({ | ||
algorithm: 'sha512', | ||
digest:'9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==', | ||
options: ['foo'] | ||
}) | ||
// -> | ||
// 'sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==?foo' | ||
// Integrity-like: full multi-entry syntax. Similar to output of `ssri.parse` | ||
ssri.stringify({ | ||
'sha512': [ | ||
{ | ||
algorithm: 'sha512', | ||
digest:'9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==', | ||
options: ['foo'] | ||
} | ||
] | ||
}) | ||
// -> | ||
// 'sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==?foo' | ||
``` | ||
#### <a name="integrity-concat"></a> `> Integrity#concat(otherIntegrity, [opts]) -> Integrity` | ||
Concatenates an `Integrity` object with another IntegrityLike, or a string | ||
representing integrity metadata. | ||
This is functionally equivalent to concatenating the string format of both | ||
integrity arguments, and calling [`ssri.parse`](#ssri-parse) on the new string. | ||
If `opts.strict` is true, the new `Integrity` will be created using strict | ||
parsing rules. See [`ssri.parse`](#parse). | ||
##### Example | ||
```javascript | ||
// This will combine the integrity checks for two different versions of | ||
// your index.js file so you can use a single integrity string and serve | ||
// either of these to clients, from a single `<script>` tag. | ||
const desktopIntegrity = ssri.fromData(fs.readFileSync('./index.desktop.js')) | ||
const mobileIntegrity = ssri.fromData(fs.readFileSync('./index.mobile.js')) | ||
// Note that browsers (and ssri) will succeed as long as ONE of the entries | ||
// for the *prioritized* algorithm succeeds. That is, in order for this fallback | ||
// to work, both desktop and mobile *must* use the same `algorithm` values. | ||
desktopIntegrity.concat(mobileIntegrity) | ||
``` | ||
#### <a name="integrity-to-string"></a> `> Integrity#toString([opts]) -> String` | ||
Returns the string representation of an `Integrity` object. All metadata 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, | ||
use [`ssri.stringify()`](#stringify). | ||
If `opts.strict` is true, the integrity string will be created using strict | ||
parsing rules. See [`ssri.parse`](#parse). | ||
##### Example | ||
```javascript | ||
const integrity = 'sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==?foo' | ||
ssri.parse(integrity).toString() === integrity | ||
``` | ||
#### <a name="from-data"></a> `> ssri.fromData(data, [opts]) -> Integrity` | ||
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 | ||
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 | ||
`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. | ||
If `opts.strict` is true, the integrity object will be created using strict | ||
parsing rules. See [`ssri.parse`](#parse). | ||
##### Example | ||
```javascript | ||
const integrityObj = ssri.fromData('foobarbaz', { | ||
algorithms: ['sha256', 'sha384', 'sha512'] | ||
}) | ||
integrity.toString('\n') | ||
// -> | ||
// sha256-l981iLWj8kurw4UbNy8Lpxqdzd7UOxS50Glhv8FwfZ0= | ||
// sha384-irnCxQ0CfQhYGlVAUdwTPC9bF3+YWLxlaDGM4xbYminxpbXEq+D+2GCEBTxcjES9 | ||
// sha512-yzd8ELD1piyANiWnmdnpCL5F52f10UfUdEkHywVZeqTt0ymgrxR63Qz0GB7TKPoeeZQmWCaz7T1+9vBnypkYWg== | ||
``` | ||
#### <a name="from-stream"></a> `> ssri.fromStream(stream, [opts]) -> Promise<Integrity>` | ||
Returns a Promise of an Integrity object calculated by reading data from | ||
a given `stream`. | ||
It accepts both `opts.algorithms` and `opts.options`, which are documented as | ||
part of [`ssri.fromData`](#from-data). | ||
Additionally, `opts.Promise` may be passed in to inject a Promise library of | ||
choice. By default, ssri will use Node's built-in Promises. | ||
If `opts.strict` is true, the integrity object will be created using strict | ||
parsing rules. See [`ssri.parse`](#parse). | ||
##### Example | ||
```javascript | ||
ssri.fromStream(fs.createReadStream('index.js'), { | ||
algorithms: ['sha1', 'sha512'] | ||
}).then(integrity => { | ||
return ssri.checkStream(fs.createReadStream('index.js'), integrity) | ||
}) // succeeds | ||
``` | ||
#### <a name="check-data"></a> `> ssri.checkData(data, sri, [opts]) -> Algorithm|false` | ||
Verifies `data` integrity against an `sri` argument. `data` may be either a | ||
`String` or a `Buffer`, and `sri` can be any subresource integrity | ||
representation that [`ssri.parse`](#parse) can handle. | ||
If verification succeeds, `checkData` will return the name of the algorithm that | ||
was used for verification (a truthy value). Otherwise, it will return `false`. | ||
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 | ||
**must** return one of the two algorithms provided. By default, ssri will make | ||
a best-effort to pick the strongest/most reliable of the given algorithms. It | ||
may intentionally deprioritize algorithms with known vulnerabilities. | ||
##### Example | ||
```javascript | ||
const data = fs.readFileSync('index.js') | ||
ssri.checkData(data, ssri.fromData(data)) // -> 'sha512' | ||
ssri.checkData(data, 'sha256-l981iLWj8kurw4UbNy8Lpxqdzd7UOxS50Glhv8FwfZ0') | ||
ssri.checkData(data, 'sha1-BaDDigEST') // -> false | ||
``` | ||
#### <a name="check-stream"></a> `> ssri.checkStream(stream, sri, [opts]) -> Promise<Algorithm>` | ||
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 string name of | ||
the algorithm that verification was done with, 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 | ||
have `err.code` as `EBADCHECKSUM`. | ||
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 | ||
**must** return one of the two algorithms provided. By default, ssri will make | ||
a best-effort to pick the strongest/most reliable of the given algorithms. It | ||
may intentionally deprioritize algorithms with known vulnerabilities. | ||
##### Example | ||
```javascript | ||
const integrity = ssri.fromData(fs.readFileSync('index.js')) | ||
ssri.checkStream( | ||
fs.createReadStream('index.js'), | ||
integrity | ||
) // -> Promise<'sha512'> | ||
ssri.checkStream( | ||
fs.createReadStream('index.js'), | ||
'sha256-l981iLWj8kurw4UbNy8Lpxqdzd7UOxS50Glhv8FwfZ0' | ||
) // -> Promise<'sha256'> | ||
ssri.checkStream( | ||
fs.createReadStream('index.js'), | ||
'sha1-BaDDigEST' | ||
) // -> Promise<Error<EBADCHECKSUM>> | ||
``` | ||
#### <a name="create-checker-stream"></a> `> createCheckerStream(sri, [opts]) -> CheckerStream` | ||
Returns a `Through` stream that data can be piped through in order to check it | ||
against `sri`. `sri` can be any subresource integrity representation that | ||
[`ssri.parse`](#parse) can handle. | ||
If verification fails, the returned stream will error with an `EBADCHECKSUM` | ||
error code. | ||
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 | ||
**must** return one of the two algorithms provided. By default, ssri will make | ||
a best-effort to pick the strongest/most reliable of the given algorithms. It | ||
may intentionally deprioritize algorithms with known vulnerabilities. | ||
##### Example | ||
```javascript | ||
const integrity = ssri.fromData(fs.readFileSync('index.js')) | ||
fs.createReadStream('index.js') | ||
.pipe(ssri.checkStream(integrity)) | ||
``` |
24182
228
341