diff --git a/.github/workflows/post-dependabot.yml b/.github/workflows/post-dependabot.yml index 19902bd..ce38340 100644 --- a/.github/workflows/post-dependabot.yml +++ b/.github/workflows/post-dependabot.yml @@ -48,11 +48,11 @@ jobs: run: | dependabot_dir="${{ steps.metadata.outputs.directory }}" if [[ "$dependabot_dir" == "/" ]]; then - echo "::set-output name=workspace::-iwr" + echo "workspace=-iwr" >> $GITHUB_OUTPUT else # strip leading slash from directory so it works as a # a path to the workspace flag - echo "::set-output name=workspace::-w ${dependabot_dir#/}" + echo "workspace=-w ${dependabot_dir#/}" >> $GITHUB_OUTPUT fi - name: Apply Changes @@ -61,7 +61,7 @@ jobs: run: | npm run template-oss-apply ${{ steps.flags.outputs.workspace }} if [[ `git status --porcelain` ]]; then - echo "::set-output name=changes::true" + echo "changes=true" >> $GITHUB_OUTPUT fi # This only sets the conventional commit prefix. This workflow can't reliably determine # what the breaking change is though. If a BREAKING CHANGE message is required then @@ -71,7 +71,7 @@ jobs: else prefix='chore' fi - echo "::set-output name=message::$prefix: postinstall for dependabot template-oss PR" + echo "message=$prefix: postinstall for dependabot template-oss PR" >> $GITHUB_OUTPUT # This step will fail if template-oss has made any workflow updates. It is impossible # for a workflow to update other workflows. In the case it does fail, we continue diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 1a1d1ee..99877da 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -41,8 +41,8 @@ jobs: id: commit continue-on-error: true run: | - npx --offline commitlint -V --from origin/${{ github.base_ref }} --to ${{ github.event.pull_request.head.sha }} + npx --offline commitlint -V --from 'origin/${{ github.base_ref }}' --to ${{ github.event.pull_request.head.sha }} - name: Run Commitlint on PR Title if: steps.commit.outcome == 'failure' run: | - echo ${{ github.event.pull_request.title }} | npx --offline commitlint -V + echo '${{ github.event.pull_request.title }}' | npx --offline commitlint -V diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 264cf3d..0eb163d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,10 @@ name: Release on: workflow_dispatch: + inputs: + release-pr: + description: a release PR number to rerun release jobs on + type: string push: branches: - main @@ -19,8 +23,8 @@ jobs: release: outputs: pr: ${{ steps.release.outputs.pr }} + release: ${{ steps.release.outputs.release }} releases: ${{ steps.release.outputs.releases }} - release-flags: ${{ steps.release.outputs.release-flags }} branch: ${{ steps.release.outputs.pr-branch }} pr-number: ${{ steps.release.outputs.pr-number }} comment-id: ${{ steps.pr-comment.outputs.result }} @@ -53,7 +57,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - npx --offline template-oss-release-please ${{ github.ref_name }} ${{ github.event_name }} + npx --offline template-oss-release-please "${{ github.ref_name }}" "${{ inputs.release-pr }}" - name: Post Pull Request Comment if: steps.release.outputs.pr-number uses: actions/github-script@v6 @@ -63,26 +67,25 @@ jobs: REF_NAME: ${{ github.ref_name }} with: script: | - const { REF_NAME, PR_NUMBER } = process.env - const repo = { owner: context.repo.owner, repo: context.repo.repo } - const issue = { ...repo, issue_number: PR_NUMBER } + const { REF_NAME, PR_NUMBER: issue_number } = process.env + const { runId, repo: { owner, repo } } = context - const { data: workflow } = await github.rest.actions.getWorkflowRun({ ...repo, run_id: context.runId }) + const { data: workflow } = await github.rest.actions.getWorkflowRun({ owner, repo, run_id: runId }) let body = '## Release Manager\n\n' - const comments = await github.paginate(github.rest.issues.listComments, issue) - let commentId = comments?.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith(body))?.id + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number }) + let commentId = comments.find(c => c.user.login === 'github-actions[bot]' && c.body.startsWith(body))?.id body += `Release workflow run: ${workflow.html_url}\n\n#### Force CI to Update This Release\n\n` body += `This PR will be updated and CI will run for every non-\`chore:\` commit that is pushed to \`main\`. ` body += `To force CI to update this PR, run this command:\n\n` - body += `\`\`\`\ngh workflow run release.yml -r ${REF_NAME}\n\`\`\`` + body += `\`\`\`\ngh workflow run release.yml -r ${REF_NAME} -R ${owner}/${repo} -f release-pr=${issue_number}\n\`\`\`` if (commentId) { - await github.rest.issues.updateComment({ ...repo, comment_id: commentId, body }) + await github.rest.issues.updateComment({ owner, repo, comment_id: commentId, body }) } else { - const { data: comment } = await github.rest.issues.createComment({ ...issue, body }) + const { data: comment } = await github.rest.issues.createComment({ owner, repo, issue_number, body }) commentId = comment?.id } @@ -168,7 +171,7 @@ jobs: RELEASE_COMMENT_ID: ${{ needs.release.outputs.comment-id }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - npm exec --offline -- template-oss-release-manager --lockfile=false + npm exec --offline -- template-oss-release-manager --lockfile=false --publish=true npm run rp-pull-request --ignore-scripts --if-present - name: Commit id: commit @@ -177,7 +180,7 @@ jobs: run: | git commit --all --amend --no-edit || true git push --force-with-lease - echo "::set-output name=sha::$(git rev-parse HEAD)" + echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT - name: Get Workflow Job uses: actions/github-script@v6 if: steps.commit.outputs.sha @@ -258,7 +261,7 @@ jobs: else result="success" fi - echo "::set-output name=result::$result" + echo "result=$result" >> $GITHUB_OUTPUT - name: Conclude Check uses: LouisBrunner/checks-action@v1.3.1 if: needs.update.outputs.check-id && always() @@ -275,25 +278,121 @@ jobs: defaults: run: shell: bash + steps: + - name: Create Release PR Comment + uses: actions/github-script@v6 + env: + RELEASES: ${{ needs.release.outputs.releases }} + with: + script: | + const releases = JSON.parse(process.env.RELEASES) + const { runId, repo: { owner, repo } } = context + const issue_number = releases[0].prNumber + + let body = '## Release Workflow\n\n' + for (const { pkgName, version, url } of releases) { + body += `- \`${pkgName}@${version}\` ${url}\n` + } + + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number }) + .then(cs => cs.map(c => ({ id: c.id, login: c.user.login, body: c.body }))) + console.log(`Found comments: ${JSON.stringify(comments, null, 2)}`) + const releaseComments = comments.filter(c => c.login === 'github-actions[bot]' && c.body.includes('Release is at')) + + for (const comment of releaseComments) { + console.log(`Release comment: ${JSON.stringify(comment, null, 2)}`) + await github.rest.issues.deleteComment({ owner, repo, comment_id: comment.id }) + } + + const runUrl = `https://github.com/${owner}/${repo}/actions/runs/${runId}` + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body: `${body}- Workflow run: :arrows_counterclockwise: ${runUrl}`, + }) + + release-integration: + needs: release + name: Release Integration + if: needs.release.outputs.release + runs-on: ubuntu-latest + defaults: + run: + shell: bash + permissions: + deployments: write steps: - name: Checkout uses: actions/checkout@v3 - - name: Setup Git User - run: | - git config --global user.email "npm-cli+bot@github.com" - git config --global user.name "npm CLI robot" + with: + ref: ${{ fromJSON(needs.release.outputs.release).tagName }} - name: Setup Node uses: actions/setup-node@v3 with: node-version: 18.x - name: Install npm@latest - run: npm i --prefer-online --no-fund --no-audit -g npm@latest - - name: npm Version - run: npm -v - - name: Install Dependencies - run: npm i --ignore-scripts --no-audit --no-fund - - name: Run Post Release Actions + run: | + npm i --prefer-online --no-fund --no-audit -g npm@latest + npm config set '//registry.npmjs.org/:_authToken'=\${PUBLISH_TOKEN} + - name: Publish env: - RELEASES: ${{ needs.release.outputs.releases }} + PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} + run: npm publish + + post-release-integration: + needs: [ release, release-integration ] + name: Post Release Integration - Release + if: github.repository_owner == 'npm' && needs.release.outputs.release && always() + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Get Needs Result + id: needs-result run: | - npm run rp-release --ignore-scripts --if-present ${{ join(fromJSON(needs.release.outputs.release-flags), ' ') }} + if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then + result="x" + elif [[ "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then + result="heavy_multiplication_x" + else + result="white_check_mark" + fi + echo "result=$result" >> $GITHUB_OUTPUT + - name: Update Release PR Comment + uses: actions/github-script@v6 + env: + PR_NUMBER: ${{ fromJSON(needs.release.outputs.release).prNumber }} + RESULT: ${{ steps.needs-result.outputs.result }} + with: + script: | + const { PR_NUMBER: issue_number, RESULT } = process.env + const { runId, repo: { owner, repo } } = context + + const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number }) + const updateComment = comments.find(c => + c.user.login === 'github-actions[bot]' && + c.body.startsWith('## Release Workflow\n\n') && + c.body.includes(runId) + ) + + if (updateComment) { + console.log('Found comment to update:', JSON.stringify(updateComment, null, 2)) + let body = updateComment.body.replace(/Workflow run: :[a-z_]+:/, `Workflow run: :${RESULT}:`) + const tagCodeowner = RESULT !== 'white_check_mark' + if (tagCodeowner) { + body += `\n\n:rotating_light:` + body += ` @npm/cli-team: The post-release workflow failed for this release.` + body += ` Manual steps may need to be taken after examining the workflow output` + body += ` from the above workflow run. :rotating_light:` + } + await github.rest.issues.updateComment({ + owner, + repo, + body, + comment_id: updateComment.id, + }) + } else { + console.log('No matching comments found:', JSON.stringify(comments, null, 2)) + } diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 932a782..952c140 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "10.0.1" + ".": "10.0.2" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 159a92f..e55a91a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [10.0.2](https://github.com/npm/ssri/compare/v10.0.1...v10.0.2) (2023-04-03) + +### Bug Fixes + +* [`8e80eca`](https://github.com/npm/ssri/commit/8e80eca497bd7f6a714498df6ad64f3faef3bc5e) [#74](https://github.com/npm/ssri/pull/74) move from symbols to private methods (#74) (@wraithgar) +* [`a316b12`](https://github.com/npm/ssri/commit/a316b12e0c177757528c643ac28ce8b2425fcb3f) [#75](https://github.com/npm/ssri/pull/75) faster toString for integrity (#75) (@H4ad) +* [`6e6877d`](https://github.com/npm/ssri/commit/6e6877d55cf608ea9b80b19a389f7e1775421e24) [#72](https://github.com/npm/ssri/pull/72) remove spread of defaultOpts (#72) (@H4ad) + ## [10.0.1](https://github.com/npm/ssri/compare/v10.0.0...v10.0.1) (2022-12-07) ### Dependencies diff --git a/SECURITY.md b/SECURITY.md index 4e7c26c..9cd2dea 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,11 +4,10 @@ GitHub takes the security of our software products and services seriously, inclu If you believe you have found a security vulnerability in this GitHub-owned open source repository, you can report it to us in one of two ways. -If the vulnerability you have found is *not* [in scope for the GitHub Bug Bounty Program](https://bounty.github.com/#scope) or if you do not wish to be considered for a bounty reward, please report the issue to us directly using [private vulnerability reporting](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability). +If the vulnerability you have found is *not* [in scope for the GitHub Bug Bounty Program](https://bounty.github.com/#scope) or if you do not wish to be considered for a bounty reward, please report the issue to us directly through [opensource-security@github.com](mailto:opensource-security@github.com). If the vulnerability you have found is [in scope for the GitHub Bug Bounty Program](https://bounty.github.com/#scope) and you would like for your finding to be considered for a bounty reward, please submit the vulnerability to us through [HackerOne](https://hackerone.com/github) in order to be eligible to receive a bounty award. **Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.** Thanks for helping make GitHub safe for everyone. - diff --git a/lib/index.js b/lib/index.js index 222861a..e142431 100644 --- a/lib/index.js +++ b/lib/index.js @@ -3,7 +3,8 @@ const crypto = require('crypto') const MiniPass = require('minipass') -const SPEC_ALGORITHMS = ['sha256', 'sha384', 'sha512'] +const SPEC_ALGORITHMS = ['sha512', 'sha384', 'sha256'] +const DEFAULT_ALGORITHMS = ['sha512'] // TODO: this should really be a hardcoded list of algorithms we support, // rather than [a-z0-9]. @@ -12,72 +13,50 @@ 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 defaultOpts = { - algorithms: ['sha512'], - error: false, - options: [], - pickAlgorithm: getPrioritizedHash, - sep: ' ', - single: false, - strict: false, -} - -const ssriOpts = (opts = {}) => ({ ...defaultOpts, ...opts }) - -const getOptString = options => !options || !options.length - ? '' - : `?${options.join('?')}` - -const _onEnd = Symbol('_onEnd') -const _getOptions = Symbol('_getOptions') -const _emittedSize = Symbol('_emittedSize') -const _emittedIntegrity = Symbol('_emittedIntegrity') -const _emittedVerified = Symbol('_emittedVerified') +const getOptString = options => options?.length ? `?${options.join('?')}` : '' class IntegrityStream extends MiniPass { + #emittedIntegrity + #emittedSize + #emittedVerified + constructor (opts) { super() this.size = 0 this.opts = opts // may be overridden later, but set now for class consistency - this[_getOptions]() + this.#getOptions() // options used for calculating stream. can't be changed. - const { algorithms = defaultOpts.algorithms } = opts + const algorithms = opts?.algorithms || DEFAULT_ALGORITHMS this.algorithms = Array.from( new Set(algorithms.concat(this.algorithm ? [this.algorithm] : [])) ) this.hashes = this.algorithms.map(crypto.createHash) } - [_getOptions] () { - const { - integrity, - size, - options, - } = { ...defaultOpts, ...this.opts } - + #getOptions () { // For verification - this.sri = integrity ? parse(integrity, this.opts) : null - this.expectedSize = size + this.sri = this.opts?.integrity ? parse(this.opts?.integrity, this.opts) : null + this.expectedSize = this.opts?.size this.goodSri = this.sri ? !!Object.keys(this.sri).length : false this.algorithm = this.goodSri ? this.sri.pickAlgorithm(this.opts) : null this.digests = this.goodSri ? this.sri[this.algorithm] : null - this.optString = getOptString(options) + this.optString = getOptString(this.opts?.options) } on (ev, handler) { - if (ev === 'size' && this[_emittedSize]) { - return handler(this[_emittedSize]) + if (ev === 'size' && this.#emittedSize) { + return handler(this.#emittedSize) } - if (ev === 'integrity' && this[_emittedIntegrity]) { - return handler(this[_emittedIntegrity]) + if (ev === 'integrity' && this.#emittedIntegrity) { + return handler(this.#emittedIntegrity) } - if (ev === 'verified' && this[_emittedVerified]) { - return handler(this[_emittedVerified]) + if (ev === 'verified' && this.#emittedVerified) { + return handler(this.#emittedVerified) } return super.on(ev, handler) @@ -85,7 +64,7 @@ class IntegrityStream extends MiniPass { emit (ev, data) { if (ev === 'end') { - this[_onEnd]() + this.#onEnd() } return super.emit(ev, data) } @@ -96,9 +75,9 @@ class IntegrityStream extends MiniPass { return super.write(data) } - [_onEnd] () { + #onEnd () { if (!this.goodSri) { - this[_getOptions]() + this.#getOptions() } const newSri = parse(this.hashes.map((h, i) => { return `${this.algorithms[i]}-${h.digest('base64')}${this.optString}` @@ -123,12 +102,12 @@ class IntegrityStream extends MiniPass { err.sri = this.sri this.emit('error', err) } else { - this[_emittedSize] = this.size + this.#emittedSize = this.size this.emit('size', this.size) - this[_emittedIntegrity] = newSri + this.#emittedIntegrity = newSri this.emit('integrity', newSri) if (match) { - this[_emittedVerified] = match + this.#emittedVerified = match this.emit('verified', match) } } @@ -141,8 +120,7 @@ class Hash { } constructor (hash, opts) { - opts = ssriOpts(opts) - const strict = !!opts.strict + const strict = opts?.strict this.source = hash.trim() // set default values so that we make V8 happy to @@ -161,7 +139,7 @@ class Hash { if (!match) { return } - if (strict && !SPEC_ALGORITHMS.some(a => a === match[1])) { + if (strict && !SPEC_ALGORITHMS.includes(match[1])) { return } this.algorithm = match[1] @@ -182,14 +160,13 @@ class Hash { } toString (opts) { - opts = ssriOpts(opts) - if (opts.strict) { + if (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) && + SPEC_ALGORITHMS.includes(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. @@ -203,11 +180,41 @@ class Hash { return '' } } - const options = this.options && this.options.length - ? `?${this.options.join('?')}` - : '' - return `${this.algorithm}-${this.digest}${options}` + return `${this.algorithm}-${this.digest}${getOptString(this.options)}` + } +} + +function integrityHashToString (toString, sep, opts, hashes) { + const toStringIsNotEmpty = toString !== '' + + let shouldAddFirstSep = false + let complement = '' + + const lastIndex = hashes.length - 1 + + for (let i = 0; i < lastIndex; i++) { + const hashString = Hash.prototype.toString.call(hashes[i], opts) + + if (hashString) { + shouldAddFirstSep = true + + complement += hashString + complement += sep + } + } + + const finalHashString = Hash.prototype.toString.call(hashes[lastIndex], opts) + + if (finalHashString) { + shouldAddFirstSep = true + complement += finalHashString } + + if (toStringIsNotEmpty && shouldAddFirstSep) { + return toString + sep + complement + } + + return toString + complement } class Integrity { @@ -224,21 +231,28 @@ class Integrity { } toString (opts) { - opts = ssriOpts(opts) - let sep = opts.sep || ' ' - if (opts.strict) { + let sep = opts?.sep || ' ' + let toString = '' + + if (opts?.strict) { // Entries must be separated by whitespace, according to spec. sep = sep.replace(/\S+/g, ' ') + + for (const hash of SPEC_ALGORITHMS) { + if (this[hash]) { + toString = integrityHashToString(toString, sep, opts, this[hash]) + } + } + } else { + for (const hash of Object.keys(this)) { + toString = integrityHashToString(toString, sep, opts, this[hash]) + } } - return Object.keys(this).map(k => { - return this[k].map(hash => { - return Hash.prototype.toString.call(hash, opts) - }).filter(x => x.length).join(sep) - }).filter(x => x.length).join(sep) + + return toString } concat (integrity, opts) { - opts = ssriOpts(opts) const other = typeof integrity === 'string' ? integrity : stringify(integrity, opts) @@ -252,7 +266,6 @@ class Integrity { // add additional hashes to an integrity value, but prevent // *changing* an existing integrity hash. merge (integrity, opts) { - opts = ssriOpts(opts) const other = parse(integrity, opts) for (const algo in other) { if (this[algo]) { @@ -268,7 +281,6 @@ class Integrity { } match (integrity, opts) { - opts = ssriOpts(opts) const other = parse(integrity, opts) if (!other) { return false @@ -286,8 +298,7 @@ class Integrity { } pickAlgorithm (opts) { - opts = ssriOpts(opts) - const pickAlgorithm = opts.pickAlgorithm + const pickAlgorithm = opts?.pickAlgorithm || getPrioritizedHash const keys = Object.keys(this) return keys.reduce((acc, algo) => { return pickAlgorithm(acc, algo) || acc @@ -300,7 +311,6 @@ function parse (sri, opts) { if (!sri) { return null } - opts = ssriOpts(opts) if (typeof sri === 'string') { return _parse(sri, opts) } else if (sri.algorithm && sri.digest) { @@ -315,7 +325,7 @@ function parse (sri, opts) { function _parse (integrity, opts) { // 3.4.3. Parse metadata // https://w3c.github.io/webappsec-subresource-integrity/#parse-metadata - if (opts.single) { + if (opts?.single) { return new Hash(integrity, opts) } const hashes = integrity.trim().split(/\s+/).reduce((acc, string) => { @@ -334,7 +344,6 @@ function _parse (integrity, opts) { module.exports.stringify = stringify function stringify (obj, opts) { - opts = ssriOpts(opts) if (obj.algorithm && obj.digest) { return Hash.prototype.toString.call(obj, opts) } else if (typeof obj === 'string') { @@ -346,8 +355,7 @@ function stringify (obj, opts) { module.exports.fromHex = fromHex function fromHex (hexDigest, algorithm, opts) { - opts = ssriOpts(opts) - const optString = getOptString(opts.options) + const optString = getOptString(opts?.options) return parse( `${algorithm}-${ Buffer.from(hexDigest, 'hex').toString('base64') @@ -357,9 +365,8 @@ function fromHex (hexDigest, algorithm, opts) { module.exports.fromData = fromData function fromData (data, opts) { - opts = ssriOpts(opts) - const algorithms = opts.algorithms - const optString = getOptString(opts.options) + const algorithms = opts?.algorithms || DEFAULT_ALGORITHMS + const optString = getOptString(opts?.options) return algorithms.reduce((acc, algo) => { const digest = crypto.createHash(algo).update(data).digest('base64') const hash = new Hash( @@ -382,7 +389,6 @@ function fromData (data, opts) { module.exports.fromStream = fromStream function fromStream (stream, opts) { - opts = ssriOpts(opts) const istream = integrityStream(opts) return new Promise((resolve, reject) => { stream.pipe(istream) @@ -399,10 +405,9 @@ function fromStream (stream, opts) { module.exports.checkData = checkData function checkData (data, sri, opts) { - opts = ssriOpts(opts) sri = parse(sri, opts) if (!sri || !Object.keys(sri).length) { - if (opts.error) { + if (opts?.error) { throw Object.assign( new Error('No valid integrity hashes to check against'), { code: 'EINTEGRITY', @@ -416,7 +421,8 @@ function checkData (data, sri, opts) { const digest = crypto.createHash(algorithm).update(data).digest('base64') const newSri = parse({ algorithm, digest }) const match = newSri.match(sri, opts) - if (match || !opts.error) { + opts = opts || {} + if (match || !(opts.error)) { return match } else if (typeof opts.size === 'number' && (data.length !== opts.size)) { /* eslint-disable-next-line max-len */ @@ -440,7 +446,7 @@ function checkData (data, sri, opts) { module.exports.checkStream = checkStream function checkStream (stream, sri, opts) { - opts = ssriOpts(opts) + opts = opts || Object.create(null) opts.integrity = sri sri = parse(sri, opts) if (!sri || !Object.keys(sri).length) { @@ -465,15 +471,14 @@ function checkStream (stream, sri, opts) { } module.exports.integrityStream = integrityStream -function integrityStream (opts = {}) { +function integrityStream (opts = Object.create(null)) { return new IntegrityStream(opts) } module.exports.create = createIntegrity function createIntegrity (opts) { - opts = ssriOpts(opts) - const algorithms = opts.algorithms - const optString = getOptString(opts.options) + const algorithms = opts?.algorithms || DEFAULT_ALGORITHMS + const optString = getOptString(opts?.options) const hashes = algorithms.map(crypto.createHash) diff --git a/package.json b/package.json index 65b14bc..4d5963e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ssri", - "version": "10.0.1", + "version": "10.0.2", "description": "Standard Subresource Integrity library -- parses, serializes, generates, and verifies integrity metadata according to the SRI spec.", "main": "lib/index.js", "files": [ @@ -51,7 +51,7 @@ }, "devDependencies": { "@npmcli/eslint-config": "^4.0.0", - "@npmcli/template-oss": "4.10.0", + "@npmcli/template-oss": "4.13.0", "tap": "^16.0.1" }, "engines": { @@ -59,6 +59,7 @@ }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", - "version": "4.10.0" + "version": "4.13.0", + "publish": "true" } }