Comparing version 7.1.0 to 8.0.0
@@ -5,2 +5,27 @@ # Changelog | ||
## [8.0.0](https://github.com/npm/ssri/compare/v7.1.0...v8.0.0) (2020-02-18) | ||
### ⚠ BREAKING CHANGES | ||
* SRI values with `../` in the algorithm name now throw | ||
as invalid (which they always probably should have!) | ||
* adds a new error that will be thrown. Empty SRIs are | ||
no longer considered valid for checking, only when using integrityStream | ||
to calculate the SRI value. | ||
PR-URL: https://github.com/npm/ssri/pull/12 | ||
Credit: @claudiahdz | ||
### Features | ||
* remove figgy-pudding ([0e78fd7](https://github.com/npm/ssri/commit/0e78fd7b754e2d098875eb4c57238709d96d7c27)) | ||
### Bug Fixes | ||
* harden SRI parsing against ../ funny business ([4062735](https://github.com/npm/ssri/commit/4062735d1281941fd32ac4320b9f9965fcec278b)) | ||
* IntegrityStream responds to mutating opts object mid-stream ([4a963e5](https://github.com/npm/ssri/commit/4a963e5982478c6b07f86848cdb72d142c765195)) | ||
* throw null when sri is empty or bad ([a6811cb](https://github.com/npm/ssri/commit/a6811cba71e20ea1fdefa6e50c9ea3c67efc2500)), closes [#12](https://github.com/npm/ssri/issues/12) | ||
## [7.1.0](https://github.com/npm/ssri/compare/v7.0.1...v7.1.0) (2019-10-24) | ||
@@ -7,0 +32,0 @@ |
109
index.js
'use strict' | ||
const crypto = require('crypto') | ||
const figgyPudding = require('figgy-pudding') | ||
const MiniPass = require('minipass') | ||
@@ -9,19 +8,21 @@ | ||
// TODO: this should really be a hardcoded list of algorithms we support, | ||
// rather than [a-z0-9]. | ||
const BASE64_REGEX = /^[a-z0-9+/]+(?:=?=?)$/i | ||
const SRI_REGEX = /^([^-]+)-([^?]+)([?\S*]*)$/ | ||
const STRICT_SRI_REGEX = /^([^-]+)-([A-Za-z0-9+/=]{44,88})(\?[\x21-\x7E]*)*$/ | ||
const SRI_REGEX = /^([a-z0-9]+)-([^?]+)([?\S*]*)$/ | ||
const STRICT_SRI_REGEX = /^([a-z0-9]+)-([A-Za-z0-9+/=]{44,88})(\?[\x21-\x7E]*)*$/ | ||
const VCHAR_REGEX = /^[\x21-\x7E]+$/ | ||
const SsriOpts = figgyPudding({ | ||
algorithms: { default: ['sha512'] }, | ||
error: { default: false }, | ||
integrity: {}, | ||
options: { default: [] }, | ||
pickAlgorithm: { default: () => getPrioritizedHash }, | ||
sep: { default: ' ' }, | ||
single: { default: false }, | ||
size: {}, | ||
strict: { default: false } | ||
}) | ||
const defaultOpts = { | ||
algorithms: ['sha512'], | ||
error: false, | ||
options: [], | ||
pickAlgorithm: getPrioritizedHash, | ||
sep: ' ', | ||
single: false, | ||
strict: false | ||
} | ||
const ssriOpts = (opts = {}) => ({ ...defaultOpts, ...opts }) | ||
const getOptString = options => !options || !options.length ? '' | ||
@@ -42,4 +43,5 @@ : `?${options.join('?')}` | ||
// options used for calculating stream. can't be changed. | ||
const { algorithms = defaultOpts.algorithms } = opts | ||
this.algorithms = Array.from( | ||
new Set(opts.algorithms.concat(this.algorithm ? [this.algorithm] : [])) | ||
new Set(algorithms.concat(this.algorithm ? [this.algorithm] : [])) | ||
) | ||
@@ -50,10 +52,15 @@ this.hashes = this.algorithms.map(crypto.createHash) | ||
[_getOptions] () { | ||
const opts = this.opts | ||
const { | ||
integrity, | ||
size, | ||
options | ||
} = { ...defaultOpts, ...this.opts } | ||
// For verification | ||
this.sri = opts.integrity ? parse(opts.integrity, opts) : null | ||
this.expectedSize = opts.size | ||
this.sri = integrity ? parse(integrity, this.opts) : null | ||
this.expectedSize = size | ||
this.goodSri = this.sri ? !!Object.keys(this.sri).length : false | ||
this.algorithm = this.goodSri ? this.sri.pickAlgorithm(opts) : null | ||
this.algorithm = this.goodSri ? this.sri.pickAlgorithm(this.opts) : null | ||
this.digests = this.goodSri ? this.sri[this.algorithm] : null | ||
this.optString = getOptString(opts.options) | ||
this.optString = getOptString(options) | ||
} | ||
@@ -107,3 +114,3 @@ | ||
constructor (hash, opts) { | ||
opts = SsriOpts(opts) | ||
opts = ssriOpts(opts) | ||
const strict = !!opts.strict | ||
@@ -145,3 +152,3 @@ this.source = hash.trim() | ||
toString (opts) { | ||
opts = SsriOpts(opts) | ||
opts = ssriOpts(opts) | ||
if (opts.strict) { | ||
@@ -180,4 +187,8 @@ // Strict mode enforces the standard as close to the foot of the | ||
isEmpty () { | ||
return Object.keys(this).length === 0 | ||
} | ||
toString (opts) { | ||
opts = SsriOpts(opts) | ||
opts = ssriOpts(opts) | ||
let sep = opts.sep || ' ' | ||
@@ -196,3 +207,3 @@ if (opts.strict) { | ||
concat (integrity, opts) { | ||
opts = SsriOpts(opts) | ||
opts = ssriOpts(opts) | ||
const other = typeof integrity === 'string' | ||
@@ -211,3 +222,3 @@ ? integrity | ||
merge (integrity, opts) { | ||
opts = SsriOpts(opts) | ||
opts = ssriOpts(opts) | ||
const other = parse(integrity, opts) | ||
@@ -228,3 +239,3 @@ for (const algo in other) { | ||
match (integrity, opts) { | ||
opts = SsriOpts(opts) | ||
opts = ssriOpts(opts) | ||
const other = parse(integrity, opts) | ||
@@ -244,10 +255,5 @@ const algo = other.pickAlgorithm(opts) | ||
pickAlgorithm (opts) { | ||
opts = SsriOpts(opts) | ||
opts = ssriOpts(opts) | ||
const pickAlgorithm = opts.pickAlgorithm | ||
const keys = Object.keys(this) | ||
if (!keys.length) { | ||
throw new Error(`No algorithms available for ${ | ||
JSON.stringify(this.toString()) | ||
}`) | ||
} | ||
return keys.reduce((acc, algo) => { | ||
@@ -261,3 +267,4 @@ return pickAlgorithm(acc, algo) || acc | ||
function parse (sri, opts) { | ||
opts = SsriOpts(opts) | ||
if (!sri) return null | ||
opts = ssriOpts(opts) | ||
if (typeof sri === 'string') { | ||
@@ -280,3 +287,3 @@ return _parse(sri, opts) | ||
} | ||
return integrity.trim().split(/\s+/).reduce((acc, string) => { | ||
const hashes = integrity.trim().split(/\s+/).reduce((acc, string) => { | ||
const hash = new Hash(string, opts) | ||
@@ -290,2 +297,3 @@ if (hash.algorithm && hash.digest) { | ||
}, new Integrity()) | ||
return hashes.isEmpty() ? null : hashes | ||
} | ||
@@ -295,3 +303,3 @@ | ||
function stringify (obj, opts) { | ||
opts = SsriOpts(opts) | ||
opts = ssriOpts(opts) | ||
if (obj.algorithm && obj.digest) { | ||
@@ -308,3 +316,3 @@ return Hash.prototype.toString.call(obj, opts) | ||
function fromHex (hexDigest, algorithm, opts) { | ||
opts = SsriOpts(opts) | ||
opts = ssriOpts(opts) | ||
const optString = getOptString(opts.options) | ||
@@ -320,3 +328,3 @@ return parse( | ||
function fromData (data, opts) { | ||
opts = SsriOpts(opts) | ||
opts = ssriOpts(opts) | ||
const algorithms = opts.algorithms | ||
@@ -344,3 +352,3 @@ const optString = getOptString(opts.options) | ||
function fromStream (stream, opts) { | ||
opts = SsriOpts(opts) | ||
opts = ssriOpts(opts) | ||
const istream = integrityStream(opts) | ||
@@ -360,5 +368,5 @@ return new Promise((resolve, reject) => { | ||
function checkData (data, sri, opts) { | ||
opts = SsriOpts(opts) | ||
opts = ssriOpts(opts) | ||
sri = parse(sri, opts) | ||
if (!Object.keys(sri).length) { | ||
if (!sri || !Object.keys(sri).length) { | ||
if (opts.error) { | ||
@@ -400,6 +408,13 @@ throw Object.assign( | ||
function checkStream (stream, sri, opts) { | ||
opts = SsriOpts(opts) | ||
const checker = integrityStream(opts.concat({ | ||
integrity: sri | ||
})) | ||
opts = ssriOpts(opts) | ||
opts.integrity = sri | ||
sri = parse(sri, opts) | ||
if (!sri || !Object.keys(sri).length) { | ||
return Promise.reject(Object.assign( | ||
new Error('No valid integrity hashes to check against'), { | ||
code: 'EINTEGRITY' | ||
} | ||
)) | ||
} | ||
const checker = integrityStream(opts) | ||
return new Promise((resolve, reject) => { | ||
@@ -417,4 +432,4 @@ stream.pipe(checker) | ||
module.exports.integrityStream = integrityStream | ||
function integrityStream (opts) { | ||
return new IntegrityStream(SsriOpts(opts)) | ||
function integrityStream (opts = {}) { | ||
return new IntegrityStream(opts) | ||
} | ||
@@ -424,3 +439,3 @@ | ||
function createIntegrity (opts) { | ||
opts = SsriOpts(opts) | ||
opts = ssriOpts(opts) | ||
const algorithms = opts.algorithms | ||
@@ -427,0 +442,0 @@ const optString = getOptString(opts.options) |
{ | ||
"name": "ssri", | ||
"version": "7.1.0", | ||
"version": "8.0.0", | ||
"description": "Standard Subresource Integrity library -- parses, serializes, generates, and verifies integrity metadata according to the SRI spec.", | ||
"main": "index.js", | ||
"files": [ | ||
"*.js" | ||
], | ||
"files": [], | ||
"scripts": { | ||
"prerelease": "npm t", | ||
"postrelease": "npm publish && git push --follow-tags", | ||
"pretest": "standard", | ||
"postrelease": "npm publish", | ||
"prepublishOnly": "git push --follow-tags", | ||
"posttest": "npm run lint", | ||
"release": "standard-version -s", | ||
"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'", | ||
"update-contrib": "weallcontribute -o . && git add CONTRIBUTING.md && git commit -m 'docs(contributing): updated CONTRIBUTING.md'" | ||
"test": "tap", | ||
"coverage": "tap", | ||
"lint": "standard" | ||
}, | ||
"tap": { | ||
"check-coverage": true | ||
}, | ||
"repository": "https://github.com/npm/ssri", | ||
@@ -40,15 +42,9 @@ "keywords": [ | ||
"dependencies": { | ||
"figgy-pudding": "^3.5.1", | ||
"minipass": "^3.1.1" | ||
}, | ||
"devDependencies": { | ||
"standard": "^14.3.0", | ||
"standard-version": "^7.0.0", | ||
"tap": "^14.8.2", | ||
"weallbehave": "^1.2.0", | ||
"weallcontribute": "^1.0.8" | ||
"standard": "^14.3.1", | ||
"standard-version": "^7.1.0", | ||
"tap": "^14.10.6" | ||
}, | ||
"tap": { | ||
"check-coverage": true | ||
}, | ||
"engines": { | ||
@@ -55,0 +51,0 @@ "node": ">= 8" |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
46677
1
3
424
- Removedfiggy-pudding@^3.5.1
- Removedfiggy-pudding@3.5.2(transitive)