pax_global_header00006660000000000000000000000064142414674550014526gustar00rootroot0000000000000052 comment=ab31e9b13d1b0db630a14cb5f240e7edc0f3447f ssri-9.0.1/000077500000000000000000000000001424146745500125155ustar00rootroot00000000000000ssri-9.0.1/.commitlintrc.js000066400000000000000000000005531424146745500156400ustar00rootroot00000000000000/* This file is automatically added by @npmcli/template-oss. Do not edit. */ module.exports = { extends: ['@commitlint/config-conventional'], rules: { 'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'deps', 'chore']], 'header-max-length': [2, 'always', 80], 'subject-case': [0, 'always', ['lower-case', 'sentence-case', 'start-case']], }, } ssri-9.0.1/.eslintrc.js000066400000000000000000000005451424146745500147600ustar00rootroot00000000000000/* This file is automatically added by @npmcli/template-oss. Do not edit. */ 'use strict' const { readdirSync: readdir } = require('fs') const localConfigs = readdir(__dirname) .filter((file) => file.startsWith('.eslintrc.local.')) .map((file) => `./${file}`) module.exports = { root: true, extends: [ '@npmcli', ...localConfigs, ], } ssri-9.0.1/.github/000077500000000000000000000000001424146745500140555ustar00rootroot00000000000000ssri-9.0.1/.github/CODEOWNERS000066400000000000000000000001321424146745500154440ustar00rootroot00000000000000# This file is automatically added by @npmcli/template-oss. Do not edit. * @npm/cli-team ssri-9.0.1/.github/ISSUE_TEMPLATE/000077500000000000000000000000001424146745500162405ustar00rootroot00000000000000ssri-9.0.1/.github/ISSUE_TEMPLATE/bug.yml000066400000000000000000000026551424146745500175500ustar00rootroot00000000000000# This file is automatically added by @npmcli/template-oss. Do not edit. name: Bug description: File a bug/issue title: "[BUG] " labels: [ Bug, Needs Triage ] body: - type: checkboxes attributes: label: Is there an existing issue for this? description: Please [search here](./issues) to see if an issue already exists for your problem. options: - label: I have searched the existing issues required: true - type: textarea attributes: label: Current Behavior description: A clear & concise description of what you're experiencing. validations: required: false - type: textarea attributes: label: Expected Behavior description: A clear & concise description of what you expected to happen. validations: required: false - type: textarea attributes: label: Steps To Reproduce description: Steps to reproduce the behavior. value: | 1. In this environment... 2. With this config... 3. Run '...' 4. See error... validations: required: false - type: textarea attributes: label: Environment description: | examples: - **npm**: 7.6.3 - **Node**: 13.14.0 - **OS**: Ubuntu 20.04 - **platform**: Macbook Pro value: | - npm: - Node: - OS: - platform: validations: required: false �����������������������������������������������������������������������������������ssri-9.0.1/.github/ISSUE_TEMPLATE/config.yml��������������������������������������������������������0000664�0000000�0000000�00000000145�14241467455�0020230�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This file is automatically added by @npmcli/template-oss. Do not edit. blank_issues_enabled: true ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/.github/dependabot.yml�������������������������������������������������������������������0000664�0000000�0000000�00000000565�14241467455�0016713�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This file is automatically added by @npmcli/template-oss. Do not edit. version: 2 updates: - package-ecosystem: npm directory: "/" schedule: interval: daily allow: - dependency-type: direct versioning-strategy: increase-if-necessary commit-message: prefix: deps prefix-development: chore labels: - "Dependencies" �������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/.github/settings.yml���������������������������������������������������������������������0000664�0000000�0000000�00000000055�14241467455�0016440�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������--- _extends: '.github:npm-cli/settings.yml' �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/.github/workflows/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�14241467455�0016112�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/.github/workflows/audit.yml��������������������������������������������������������������0000664�0000000�0000000�00000001370�14241467455�0017744�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This file is automatically added by @npmcli/template-oss. Do not edit. name: Audit on: workflow_dispatch: schedule: # "At 01:00 on Monday" https://crontab.guru/#0_1_*_*_1 - cron: "0 1 * * 1" jobs: audit: runs-on: ubuntu-latest steps: - 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" - uses: actions/setup-node@v3 with: node-version: 16.x - name: Update npm to latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - run: npm -v - run: npm i --ignore-scripts --no-audit --no-fund --package-lock - run: npm audit ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/.github/workflows/ci.yml�����������������������������������������������������������������0000664�0000000�0000000�00000005114�14241467455�0017231�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This file is automatically added by @npmcli/template-oss. Do not edit. name: CI on: workflow_dispatch: pull_request: branches: - '*' push: branches: - main - latest schedule: # "At 02:00 on Monday" https://crontab.guru/#0_2_*_*_1 - cron: "0 2 * * 1" jobs: lint: runs-on: ubuntu-latest steps: - 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" - uses: actions/setup-node@v3 with: node-version: 16.x - name: Update npm to latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - run: npm -v - run: npm i --ignore-scripts --no-audit --no-fund - run: npm run lint test: strategy: fail-fast: false matrix: node-version: - 12.13.0 - 12.x - 14.15.0 - 14.x - 16.0.0 - 16.x platform: - os: ubuntu-latest shell: bash - os: macos-latest shell: bash - os: windows-latest shell: cmd runs-on: ${{ matrix.platform.os }} defaults: run: shell: ${{ matrix.platform.shell }} steps: - 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" - uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - name: Update to workable npm (windows) # node 12 and 14 ship with npm@6, which is known to fail when updating itself in windows if: matrix.platform.os == 'windows-latest' && (startsWith(matrix.node-version, '12.') || startsWith(matrix.node-version, '14.')) run: | curl -sO https://registry.npmjs.org/npm/-/npm-7.5.4.tgz tar xf npm-7.5.4.tgz cd package node lib/npm.js install --no-fund --no-audit -g ..\npm-7.5.4.tgz cd .. rmdir /s /q package - name: Update npm to 7 # If we do test on npm 10 it needs npm7 if: startsWith(matrix.node-version, '10.') run: npm i --prefer-online --no-fund --no-audit -g npm@7 - name: Update npm to latest if: ${{ !startsWith(matrix.node-version, '10.') }} run: npm i --prefer-online --no-fund --no-audit -g npm@latest - run: npm -v - run: npm i --ignore-scripts --no-audit --no-fund - run: npm test --ignore-scripts ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/.github/workflows/codeql-analysis.yml����������������������������������������������������0000664�0000000�0000000�00000002001�14241467455�0021716�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This file is automatically added by @npmcli/template-oss. Do not edit. name: "CodeQL" on: push: branches: - main - latest pull_request: # The branches below must be a subset of the branches above branches: - main - latest schedule: # "At 03:00 on Monday" https://crontab.guru/#0_3_*_*_1 - cron: "0 3 * * 1" jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ javascript ] steps: - 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" - name: Initialize CodeQL uses: github/codeql-action/init@v1 with: languages: ${{ matrix.language }} - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/.github/workflows/post-dependabot.yml����������������������������������������������������0000664�0000000�0000000�00000002641�14241467455�0021730�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This file is automatically added by @npmcli/template-oss. Do not edit. name: Post Dependabot Actions on: pull_request # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions permissions: contents: write jobs: template-oss-apply: runs-on: ubuntu-latest if: github.actor == 'dependabot[bot]' steps: - 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" - uses: actions/setup-node@v3 with: node-version: 16.x - name: Update npm to latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - run: npm -v - name: Dependabot metadata id: metadata uses: dependabot/fetch-metadata@v1.1.1 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: npm install and commit if: contains(steps.metadata.outputs.dependency-names, '@npmcli/template-oss') env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | gh pr checkout ${{ github.event.pull_request.number }} npm install --ignore-scripts --no-audit --no-fund npm run template-oss-apply git add . git commit -am "chore: postinstall for dependabot template-oss PR" git push npm run lint �����������������������������������������������������������������������������������������������ssri-9.0.1/.github/workflows/pull-request.yml�������������������������������������������������������0000664�0000000�0000000�00000002143�14241467455�0021277�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This file is automatically added by @npmcli/template-oss. Do not edit. name: Pull Request Linting on: pull_request: types: - opened - reopened - edited - synchronize jobs: check: name: Check PR Title or Commits runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: fetch-depth: 0 - name: Setup git user run: | git config --global user.email "npm-cli+bot@github.com" git config --global user.name "npm CLI robot" - uses: actions/setup-node@v3 with: node-version: 16.x - name: Update npm to latest run: npm i --prefer-online --no-fund --no-audit -g npm@latest - run: npm -v - name: Install deps run: npm i -D @commitlint/cli @commitlint/config-conventional - name: Check commits OR PR title env: PR_TITLE: ${{ github.event.pull_request.title }} run: | npx --offline commitlint -V --from origin/main --to ${{ github.event.pull_request.head.sha }} \ || echo $PR_TITLE | npx --offline commitlint -V �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/.github/workflows/release-please.yml�����������������������������������������������������0000664�0000000�0000000�00000001314�14241467455�0021523�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This file is automatically added by @npmcli/template-oss. Do not edit. name: Release Please on: push: branches: - main - latest jobs: release-please: runs-on: ubuntu-latest steps: - uses: google-github-actions/release-please-action@v3 id: release with: release-type: node changelog-types: > [ {"type":"feat","section":"Features","hidden":false}, {"type":"fix","section":"Bug Fixes","hidden":false}, {"type":"docs","section":"Documentation","hidden":false}, {"type":"deps","section":"Dependencies","hidden":false}, {"type":"chore","hidden":true} ] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/.gitignore�������������������������������������������������������������������������������0000664�0000000�0000000�00000000570�14241467455�0014507�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This file is automatically added by @npmcli/template-oss. Do not edit. # ignore everything in the root /* # keep these !/.eslintrc.local.* !**/.gitignore !/docs/ !/tap-snapshots/ !/test/ !/map.js !/scripts/ !/README* !/LICENSE* !/CHANGELOG* !/.commitlintrc.js !/.eslintrc.js !/.github/ !/.gitignore !/.npmrc !/CODE_OF_CONDUCT.md !/SECURITY.md !/bin/ !/lib/ !/package.json ����������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/.npmrc�����������������������������������������������������������������������������������0000664�0000000�0000000�00000000135�14241467455�0013634�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������; This file is automatically added by @npmcli/template-oss. Do not edit. package-lock=false �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/CHANGELOG.md�����������������������������������������������������������������������������0000664�0000000�0000000�00000026110�14241467455�0014326�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Changelog ### [9.0.1](https://github.com/npm/ssri/compare/v9.0.0...v9.0.1) (2022-05-19) ### Bug Fixes * store emitted events and re-emit them for late listeners ([#39](https://github.com/npm/ssri/issues/39)) ([c5421f1](https://github.com/npm/ssri/commit/c5421f1fb4f684b941e4ea1abff83b7048a48d61)) ## [9.0.0](https://github.com/npm/ssri/compare/v8.0.1...v9.0.0) (2022-04-05) ### ⚠ BREAKING CHANGES * this drops support for node 8, node 10, and non-LTS versions of node 12 and node 14 ### Dependencies * @npmcli/template-oss@3.2.2 ([#31](https://github.com/npm/ssri/issues/31)) ([a14b37a](https://github.com/npm/ssri/commit/a14b37aaec562339e836960478d6f506f63243e0)) ### [8.0.1](https://github.com/npm/ssri/compare/v8.0.0...v8.0.1) (2021-01-27) ### Bug Fixes * simplify regex for strict mode, add tests ([76e2233](https://github.com/npm/ssri/commit/76e223317d971f19e4db8191865bdad5edee40d2)) ## [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) ### Bug Fixes * Do not blow up if the opts object is mutated ([806e8c8](https://github.com/npm/ssri/commit/806e8c8)) ### Features * Add Integrity#merge method ([0572c1d](https://github.com/npm/ssri/commit/0572c1d)), closes [#4](https://github.com/npm/ssri/issues/4) ### [7.0.1](https://github.com/npm/ssri/compare/v7.0.0...v7.0.1) (2019-09-30) ## [7.0.0](https://github.com/npm/ssri/compare/v6.0.1...v7.0.0) (2019-09-18) ### ⚠ BREAKING CHANGES * ssri no longer accepts a Promise option, and does not use, return, or rely on Bluebird promises. * drop support for Node.js v6. We knew this was coming, and the Stream changes are breaking anyway. May as well do this now. * **streams:** this replaces the Node.js stream with a Minipass stream. See http://npm.im/minipass for documentation. ### Bug Fixes * return super.write() return value ([55b055d](https://github.com/npm/ssri/commit/55b055d)) * Use native promises only ([6d13165](https://github.com/npm/ssri/commit/6d13165)) * update tap, standard, standard-version, travis ([2e54956](https://github.com/npm/ssri/commit/2e54956)) * **streams:** replace transform streams with minipass ([363995e](https://github.com/npm/ssri/commit/363995e)) <a name="6.0.1"></a> ## [6.0.1](https://github.com/npm/ssri/compare/v6.0.0...v6.0.1) (2018-08-27) ### Bug Fixes * **opts:** use figgy-pudding to specify consumed opts ([cf86553](https://github.com/npm/ssri/commit/cf86553)) <a name="6.0.0"></a> # [6.0.0](https://github.com/npm/ssri/compare/v5.3.0...v6.0.0) (2018-04-09) ### Bug Fixes * **docs:** minor typo ([b71ef17](https://github.com/npm/ssri/commit/b71ef17)) ### meta * drop support for node@4 ([d9bf359](https://github.com/npm/ssri/commit/d9bf359)) ### BREAKING CHANGES * node@4 is no longer supported <a name="5.3.0"></a> # [5.3.0](https://github.com/npm/ssri/compare/v5.2.4...v5.3.0) (2018-03-13) ### Features * **checkData:** optionally throw when checkData fails ([bf26b84](https://github.com/npm/ssri/commit/bf26b84)) <a name="5.2.4"></a> ## [5.2.4](https://github.com/npm/ssri/compare/v5.2.3...v5.2.4) (2018-02-16) <a name="5.2.3"></a> ## [5.2.3](https://github.com/npm/ssri/compare/v5.2.2...v5.2.3) (2018-02-16) ### Bug Fixes * **hashes:** filter hash priority list by available hashes ([2fa30b8](https://github.com/npm/ssri/commit/2fa30b8)) * **integrityStream:** dedupe algorithms to generate ([d56c654](https://github.com/npm/ssri/commit/d56c654)) <a name="5.2.2"></a> ## [5.2.2](https://github.com/npm/ssri/compare/v5.2.1...v5.2.2) (2018-02-14) ### Bug Fixes * **security:** tweak strict SRI regex ([#10](https://github.com/npm/ssri/issues/10)) ([d0ebcdc](https://github.com/npm/ssri/commit/d0ebcdc)) <a name="5.2.1"></a> ## [5.2.1](https://github.com/npm/ssri/compare/v5.2.0...v5.2.1) (2018-02-06) <a name="5.2.0"></a> # [5.2.0](https://github.com/npm/ssri/compare/v5.1.0...v5.2.0) (2018-02-06) ### Features * **match:** add integrity.match() ([3c49cc4](https://github.com/npm/ssri/commit/3c49cc4)) <a name="5.1.0"></a> # [5.1.0](https://github.com/npm/ssri/compare/v5.0.0...v5.1.0) (2018-01-18) ### Bug Fixes * **checkStream:** integrityStream now takes opts.integrity algos into account ([d262910](https://github.com/npm/ssri/commit/d262910)) ### Features * **sha3:** do some guesswork about upcoming sha3 ([7fdd9df](https://github.com/npm/ssri/commit/7fdd9df)) <a name="5.0.0"></a> # [5.0.0](https://github.com/npm/ssri/compare/v4.1.6...v5.0.0) (2017-10-23) ### Features * **license:** relicense to ISC (#9) ([c82983a](https://github.com/npm/ssri/commit/c82983a)) ### BREAKING CHANGES * **license:** the license has been changed from CC0-1.0 to ISC. <a name="4.1.6"></a> ## [4.1.6](https://github.com/npm/ssri/compare/v4.1.5...v4.1.6) (2017-06-07) ### Bug Fixes * **checkStream:** make sure to pass all opts through ([0b1bcbe](https://github.com/npm/ssri/commit/0b1bcbe)) <a name="4.1.5"></a> ## [4.1.5](https://github.com/npm/ssri/compare/v4.1.4...v4.1.5) (2017-06-05) ### Bug Fixes * **integrityStream:** stop crashing if opts.algorithms and opts.integrity have an algo mismatch ([fb1293e](https://github.com/npm/ssri/commit/fb1293e)) <a name="4.1.4"></a> ## [4.1.4](https://github.com/npm/ssri/compare/v4.1.3...v4.1.4) (2017-05-31) ### Bug Fixes * **node:** older versions of node[@4](https://github.com/4) do not support base64buffer string parsing ([513df4e](https://github.com/npm/ssri/commit/513df4e)) <a name="4.1.3"></a> ## [4.1.3](https://github.com/npm/ssri/compare/v4.1.2...v4.1.3) (2017-05-24) ### Bug Fixes * **check:** handle various bad hash corner cases better ([c2c262b](https://github.com/npm/ssri/commit/c2c262b)) <a name="4.1.2"></a> ## [4.1.2](https://github.com/npm/ssri/compare/v4.1.1...v4.1.2) (2017-04-18) ### Bug Fixes * **stream:** _flush can be called multiple times. use on("end") ([b1c4805](https://github.com/npm/ssri/commit/b1c4805)) <a name="4.1.1"></a> ## [4.1.1](https://github.com/npm/ssri/compare/v4.1.0...v4.1.1) (2017-04-12) ### Bug Fixes * **pickAlgorithm:** error if pickAlgorithm() is used in an empty Integrity ([fab470e](https://github.com/npm/ssri/commit/fab470e)) <a name="4.1.0"></a> # [4.1.0](https://github.com/npm/ssri/compare/v4.0.0...v4.1.0) (2017-04-07) ### Features * adding ssri.create for a crypto style interface (#2) ([96f52ad](https://github.com/npm/ssri/commit/96f52ad)) <a name="4.0.0"></a> # [4.0.0](https://github.com/npm/ssri/compare/v3.0.2...v4.0.0) (2017-04-03) ### Bug Fixes * **integrity:** should have changed the error code before. oops ([8381afa](https://github.com/npm/ssri/commit/8381afa)) ### BREAKING CHANGES * **integrity:** EBADCHECKSUM -> EINTEGRITY for verification errors <a name="3.0.2"></a> ## [3.0.2](https://github.com/npm/ssri/compare/v3.0.1...v3.0.2) (2017-04-03) <a name="3.0.1"></a> ## [3.0.1](https://github.com/npm/ssri/compare/v3.0.0...v3.0.1) (2017-04-03) ### Bug Fixes * **package.json:** really should have these in the keywords because search ([a6ac6d0](https://github.com/npm/ssri/commit/a6ac6d0)) <a name="3.0.0"></a> # [3.0.0](https://github.com/npm/ssri/compare/v2.0.0...v3.0.0) (2017-04-03) ### Bug Fixes * **hashes:** IntegrityMetadata -> Hash ([d04aa1f](https://github.com/npm/ssri/commit/d04aa1f)) ### Features * **check:** return IntegrityMetadata on check success ([2301e74](https://github.com/npm/ssri/commit/2301e74)) * **fromHex:** ssri.fromHex to make it easier to generate them from hex valus ([049b89e](https://github.com/npm/ssri/commit/049b89e)) * **hex:** utility function for getting hex version of digest ([a9f021c](https://github.com/npm/ssri/commit/a9f021c)) * **hexDigest:** added hexDigest method to Integrity objects too ([85208ba](https://github.com/npm/ssri/commit/85208ba)) * **integrity:** add .isIntegrity and .isIntegrityMetadata ([1b29e6f](https://github.com/npm/ssri/commit/1b29e6f)) * **integrityStream:** new stream that can both generate and check streamed data ([fd23e1b](https://github.com/npm/ssri/commit/fd23e1b)) * **parse:** allow parsing straight into a single IntegrityMetadata object ([c8ddf48](https://github.com/npm/ssri/commit/c8ddf48)) * **pickAlgorithm:** Intergrity#pickAlgorithm() added ([b97a796](https://github.com/npm/ssri/commit/b97a796)) * **size:** calculate and update stream sizes ([02ed1ad](https://github.com/npm/ssri/commit/02ed1ad)) ### BREAKING CHANGES * **hashes:** `.isIntegrityMetadata` is now `.isHash`. Also, any references to `IntegrityMetadata` now refer to `Hash`. * **integrityStream:** createCheckerStream has been removed and replaced with a general-purpose integrityStream. To convert existing createCheckerStream code, move the `sri` argument into `opts.integrity` in integrityStream. All other options should be the same. * **check:** `checkData`, `checkStream`, and `createCheckerStream` now yield a whole IntegrityMetadata instance representing the first successful hash match. <a name="2.0.0"></a> # [2.0.0](https://github.com/npm/ssri/compare/v1.0.0...v2.0.0) (2017-03-24) ### Bug Fixes * **strict-mode:** make regexes more rigid ([122a32c](https://github.com/npm/ssri/commit/122a32c)) ### Features * **api:** added serialize alias for unparse ([999b421](https://github.com/npm/ssri/commit/999b421)) * **concat:** add Integrity#concat() ([cae12c7](https://github.com/npm/ssri/commit/cae12c7)) * **pickAlgo:** pick the strongest algorithm provided, by default ([58c18f7](https://github.com/npm/ssri/commit/58c18f7)) * **strict-mode:** strict SRI support ([3f0b64c](https://github.com/npm/ssri/commit/3f0b64c)) * **stringify:** replaced unparse/serialize with stringify ([4acad30](https://github.com/npm/ssri/commit/4acad30)) * **verification:** add opts.pickAlgorithm ([f72e658](https://github.com/npm/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> # 1.0.0 (2017-03-23) ### Features * **api:** implemented initial api ([4fbb16b](https://github.com/npm/ssri/commit/4fbb16b)) ### BREAKING CHANGES * **api:** Initial API established. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/CODE_OF_CONDUCT.md�����������������������������������������������������������������������0000664�0000000�0000000�00000000507�14241467455�0015316�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!-- This file is automatically added by @npmcli/template-oss. Do not edit. --> All interactions in this repo are covered by the [npm Code of Conduct](https://docs.npmjs.com/policies/conduct) The npm cli team may, at its own discretion, moderate, remove, or edit any interactions such as pull requests, issues, and comments. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/LICENSE.md�������������������������������������������������������������������������������0000664�0000000�0000000�00000001370�14241467455�0014122�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ISC License Copyright 2021 (c) npm, Inc. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE COPYRIGHT HOLDER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/README.md��������������������������������������������������������������������������������0000664�0000000�0000000�00000047665�14241467455�0014016�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# 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/npm/ssri.svg)](https://travis-ci.org/npm/ssri) [![AppVeyor](https://ci.appveyor.com/api/projects/status/github/npm/ssri?svg=true)](https://ci.appveyor.com/project/npm/ssri) [![Coverage Status](https://coveralls.io/repos/github/npm/ssri/badge.svg?branch=latest)](https://coveralls.io/github/npm/ssri?branch=latest) [`ssri`](https://github.com/npm/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. ## Install `$ npm install --save ssri` ## Table of Contents * [Example](#example) * [Features](#features) * [Contributing](#contributing) * [API](#api) * Parsing & Serializing * [`parse`](#parse) * [`stringify`](#stringify) * [`Integrity#concat`](#integrity-concat) * [`Integrity#merge`](#integrity-merge) * [`Integrity#toString`](#integrity-to-string) * [`Integrity#toJSON`](#integrity-to-json) * [`Integrity#match`](#integrity-match) * [`Integrity#pickAlgorithm`](#integrity-pick-algorithm) * [`Integrity#hexDigest`](#integrity-hex-digest) * Integrity Generation * [`fromHex`](#from-hex) * [`fromData`](#from-data) * [`fromStream`](#from-stream) * [`create`](#create) * Integrity Verification * [`checkData`](#check-data) * [`checkStream`](#check-stream) * [`integrityStream`](#integrity-stream) ### Example ```javascript const ssri = require('ssri') const integrity = 'sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==?foo' // Parsing and serializing const parsed = ssri.parse(integrity) ssri.stringify(parsed) // === integrity (works on non-Integrity objects) parsed.toString() // === integrity // Async stream functions ssri.checkStream(fs.createReadStream('./my-file'), integrity).then(...) ssri.fromStream(fs.createReadStream('./my-file')).then(sri => { sri.toString() === integrity }) fs.createReadStream('./my-file').pipe(ssri.createCheckerStream(sri)) // Sync data functions ssri.fromData(fs.readFileSync('./my-file')) // === parsed ssri.checkData(fs.readFileSync('./my-file'), integrity) // => 'sha512' ``` ### Features * 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 hash 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. ### API #### <a name="parse"></a> `> ssri.parse(sri, [opts]) -> Integrity` Parses `sri` into an `Integrity` data structure. `sri` can be an integrity 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 { 'sha1': [{algorithm: 'sha1', digest: 'deadbeef', options: []}], 'sha512': [ {algorithm: 'sha512', digest: 'c0ffee', options: []}, {algorithm: 'sha512', digest: 'bad1dea', options: ['foo']} ], } ``` 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 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 `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 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' // Hash-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 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. 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-merge"></a> `> Integrity#merge(otherIntegrity, [opts])` Safely merges another IntegrityLike or integrity string into an `Integrity` object. If the other integrity value has any algorithms in common with the current object, then the hash digests must match, or an error is thrown. Any new hashes will be added to the current object's set. This is useful when an integrity value may be upgraded with a stronger algorithm, you wish to prevent accidentally suppressing integrity errors by overwriting the expected integrity value. ##### Example ```javascript const data = fs.readFileSync('data.txt') // integrity.txt contains 'sha1-X1UT+IIv2+UUWvM7ZNjZcNz5XG4=' // because we were young, and didn't realize sha1 would not last const expectedIntegrity = ssri.parse(fs.readFileSync('integrity.txt', 'utf8')) const match = ssri.checkData(data, expectedIntegrity, { algorithms: ['sha512', 'sha1'] }) if (!match) { throw new Error('data corrupted or something!') } // get a stronger algo! if (match && match.algorithm !== 'sha512') { const updatedIntegrity = ssri.fromData(data, { algorithms: ['sha512'] }) expectedIntegrity.merge(updatedIntegrity) fs.writeFileSync('integrity.txt', expectedIntegrity.toString()) // file now contains // 'sha1-X1UT+IIv2+UUWvM7ZNjZcNz5XG4= sha512-yzd8ELD1piyANiWnmdnpCL5F52f10UfUdEkHywVZeqTt0ymgrxR63Qz0GB7TKPoeeZQmWCaz7T1+9vBnypkYWg==' } ``` #### <a name="integrity-to-string"></a> `> Integrity#toString([opts]) -> String` 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 come 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="integrity-to-json"></a> `> Integrity#toJSON() -> String` Returns the string representation of an `Integrity` object. All hash entries will be concatenated in the string by `' '`. This is a convenience method so you can pass an `Integrity` object directly to `JSON.stringify`. For more info check out [toJSON() behavior on mdn](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON%28%29_behavior). ##### Example ```javascript const integrity = '"sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==?foo"' JSON.stringify(ssri.parse(integrity)) === integrity ``` #### <a name="integrity-match"></a> `> Integrity#match(sri, [opts]) -> Hash | false` Returns the matching (truthy) hash if `Integrity` matches the argument passed as `sri`, which can be anything that [`parse`](#parse) will accept. `opts` will be passed through to `parse` and [`pickAlgorithm()`](#integrity-pick-algorithm). ##### Example ```javascript const integrity = 'sha512-9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==' ssri.parse(integrity).match(integrity) // Hash { // digest: '9KhgCRIx/AmzC8xqYJTZRrnO8OW2Pxyl2DIMZSBOr0oDvtEFyht3xpp71j/r/pAe1DM+JI/A+line3jUBgzQ7A==' // algorithm: 'sha512' // } ssri.parse(integrity).match('sha1-deadbeef') // false ``` #### <a name="integrity-pick-algorithm"></a> `> Integrity#pickAlgorithm([opts]) -> String` Returns the "best" algorithm from those available in the integrity object. 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 ssri.parse('sha1-WEakDigEST sha512-yzd8ELD1piyANiWnmdnpCL5F52f10UfUdEkHywVZeqTt0ymgrxR63Qz0GB7TKPoeeZQmWCaz7T1').pickAlgorithm() // sha512 ``` #### <a name="integrity-hex-digest"></a> `> Integrity#hexDigest() -> String` `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 ```javascript ssri.parse('sha1-deadbeef').hexDigest() // '75e69d6de79f' ``` #### <a name="from-hex"></a> `> ssri.fromHex(hexDigest, algorithm, [opts]) -> Integrity` Creates an `Integrity` object with a single entry, based on a hex-formatted hash. This is a utility function to help convert existing shasums to the Integrity format, and is roughly equivalent to something like: ```javascript 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 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. 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 `Hash` object will be returned. ##### Example ```javascript ssri.fromHex('75e69d6de79f', 'sha1').toString() // 'sha1-deadbeef' ``` #### <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 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 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. 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="create"></a> `> ssri.create([opts]) -> <Hash>` Returns a Hash object with `update(<Buffer or string>[,enc])` and `digest()` methods. The Hash object provides the same methods as [crypto class Hash](https://nodejs.org/dist/latest-v6.x/docs/api/crypto.html#crypto_class_hash). `digest()` accepts no arguments and returns an Integrity object calculated by reading data from calls to update. It accepts both `opts.algorithms` and `opts.options`, which are documented as part of [`ssri.fromData`](#from-data). If `opts.strict` is true, the integrity object will be created using strict parsing rules. See [`ssri.parse`](#parse). ##### Example ```javascript const integrity = ssri.create().update('foobarbaz').digest() integrity.toString() // -> // sha512-yzd8ELD1piyANiWnmdnpCL5F52f10UfUdEkHywVZeqTt0ymgrxR63Qz0GB7TKPoeeZQmWCaz7T1+9vBnypkYWg== ``` #### <a name="check-data"></a> `> 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 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 used by [`Integrity#pickAlgorithm`](#integrity-pick-algorithm) when deciding which of the available digests to match against. If `opts.error` is true, and verification fails, `checkData` will throw either an `EBADSIZE` or an `EINTEGRITY` error, instead of just returning false. ##### 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 ssri.checkData(data, 'sha1-BaDDigEST', {error: true}) // -> Error! EINTEGRITY ``` #### <a name="check-stream"></a> `> ssri.checkStream(stream, sri, [opts]) -> Promise<Hash>` 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 `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 have `err.code` as `EINTEGRITY`. 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. ##### Example ```javascript const integrity = ssri.fromData(fs.readFileSync('index.js')) ssri.checkStream( fs.createReadStream('index.js'), integrity ) // -> // Promise<{ // algorithm: 'sha512', // digest: 'sha512-yzd8ELD1piyANiWnmdnpCL5F52f10UfUdEkHywVZeqTt0ymgrxR63Qz0GB7TKPoeeZQmWCaz7T1' // }> ssri.checkStream( fs.createReadStream('index.js'), 'sha256-l981iLWj8kurw4UbNy8Lpxqdzd7UOxS50Glhv8FwfZ0' ) // -> Promise<Hash> ssri.checkStream( fs.createReadStream('index.js'), 'sha1-BaDDigEST' ) // -> Promise<Error<{code: 'EINTEGRITY'}>> ``` #### <a name="integrity-stream"></a> `> integrityStream([opts]) -> IntegrityStream` Returns a `Transform` stream that data can be piped through in order to generate and optionally check data integrity for piped data. When the stream completes successfully, it emits `size` and `integrity` events, containing the total number of bytes processed and a calculated `Integrity` instance based on stream 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 `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 `Hash` object that is the one that succeeded verification. If verification fails, the stream will error with an `EINTEGRITY` 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 **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.integrityStream({integrity})) ``` ���������������������������������������������������������������������������ssri-9.0.1/SECURITY.md������������������������������������������������������������������������������0000664�0000000�0000000�00000000246�14241467455�0014310�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!-- This file is automatically added by @npmcli/template-oss. Do not edit. --> Please send vulnerability reports through [hackerone](https://hackerone.com/github). ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/lib/�������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14241467455�0013263�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/lib/index.js�����������������������������������������������������������������������������0000664�0000000�0000000�00000034756�14241467455�0014747�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict' const crypto = require('crypto') const MiniPass = require('minipass') const SPEC_ALGORITHMS = ['sha256', 'sha384', 'sha512'] // 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 = /^([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') class IntegrityStream extends MiniPass { constructor (opts) { super() this.size = 0 this.opts = opts // may be overridden later, but set now for class consistency this[_getOptions]() // options used for calculating stream. can't be changed. const { algorithms = defaultOpts.algorithms } = opts 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 } // For verification 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(this.opts) : null this.digests = this.goodSri ? this.sri[this.algorithm] : null this.optString = getOptString(options) } on (ev, handler) { if (ev === 'size' && this[_emittedSize]) { return handler(this[_emittedSize]) } if (ev === 'integrity' && this[_emittedIntegrity]) { return handler(this[_emittedIntegrity]) } if (ev === 'verified' && this[_emittedVerified]) { return handler(this[_emittedVerified]) } return super.on(ev, handler) } emit (ev, data) { if (ev === 'end') { this[_onEnd]() } return super.emit(ev, data) } write (data) { this.size += data.length this.hashes.forEach(h => h.update(data)) return super.write(data) } [_onEnd] () { if (!this.goodSri) { this[_getOptions]() } const newSri = parse(this.hashes.map((h, i) => { return `${this.algorithms[i]}-${h.digest('base64')}${this.optString}` }).join(' '), this.opts) // Integrity verification mode const match = this.goodSri && newSri.match(this.sri, this.opts) if (typeof this.expectedSize === 'number' && this.size !== this.expectedSize) { /* eslint-disable-next-line max-len */ const err = new Error(`stream size mismatch when checking ${this.sri}.\n Wanted: ${this.expectedSize}\n Found: ${this.size}`) err.code = 'EBADSIZE' err.found = this.size err.expected = this.expectedSize err.sri = this.sri this.emit('error', err) } else if (this.sri && !match) { /* eslint-disable-next-line max-len */ const err = new Error(`${this.sri} integrity checksum failed when using ${this.algorithm}: wanted ${this.digests} but got ${newSri}. (${this.size} bytes)`) err.code = 'EINTEGRITY' err.found = newSri err.expected = this.digests err.algorithm = this.algorithm err.sri = this.sri this.emit('error', err) } else { this[_emittedSize] = this.size this.emit('size', this.size) this[_emittedIntegrity] = newSri this.emit('integrity', newSri) if (match) { this[_emittedVerified] = match this.emit('verified', match) } } } } class Hash { get isHash () { return true } constructor (hash, opts) { opts = ssriOpts(opts) const strict = !!opts.strict this.source = hash.trim() // set default values so that we make V8 happy to // always see a familiar object template. this.digest = '' this.algorithm = '' this.options = [] // 3.1. Integrity metadata (called "Hash" by ssri) // https://w3c.github.io/webappsec-subresource-integrity/#integrity-metadata-description 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] this.digest = match[2] const rawOpts = match[3] if (rawOpts) { this.options = rawOpts.slice(1).split('?') } } hexDigest () { return this.digest && Buffer.from(this.digest, 'base64').toString('hex') } toJSON () { return this.toString() } toString (opts) { opts = ssriOpts(opts) 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) && // 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}${options}` } } class Integrity { get isIntegrity () { return true } toJSON () { return this.toString() } isEmpty () { return Object.keys(this).length === 0 } toString (opts) { opts = ssriOpts(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(hash => { return Hash.prototype.toString.call(hash, opts) }).filter(x => x.length).join(sep) }).filter(x => x.length).join(sep) } concat (integrity, opts) { opts = ssriOpts(opts) const other = typeof integrity === 'string' ? integrity : stringify(integrity, opts) return parse(`${this.toString(opts)} ${other}`, opts) } hexDigest () { return parse(this, { single: true }).hexDigest() } // 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]) { if (!this[algo].find(hash => other[algo].find(otherhash => hash.digest === otherhash.digest))) { throw new Error('hashes do not match, cannot update integrity') } } else { this[algo] = other[algo] } } } match (integrity, opts) { opts = ssriOpts(opts) const other = parse(integrity, opts) const algo = other.pickAlgorithm(opts) return ( this[algo] && other[algo] && this[algo].find(hash => other[algo].find(otherhash => hash.digest === otherhash.digest ) ) ) || false } pickAlgorithm (opts) { opts = ssriOpts(opts) const pickAlgorithm = opts.pickAlgorithm const keys = Object.keys(this) return keys.reduce((acc, algo) => { return pickAlgorithm(acc, algo) || acc }) } } module.exports.parse = parse 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) { 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 if (opts.single) { return new Hash(integrity, opts) } const hashes = integrity.trim().split(/\s+/).reduce((acc, string) => { const hash = new Hash(string, opts) if (hash.algorithm && hash.digest) { const algo = hash.algorithm if (!acc[algo]) { acc[algo] = [] } acc[algo].push(hash) } return acc }, new Integrity()) return hashes.isEmpty() ? null : hashes } 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') { return stringify(parse(obj, opts), opts) } else { return Integrity.prototype.toString.call(obj, opts) } } module.exports.fromHex = fromHex function fromHex (hexDigest, algorithm, opts) { opts = ssriOpts(opts) const optString = getOptString(opts.options) return parse( `${algorithm}-${ Buffer.from(hexDigest, 'hex').toString('base64') }${optString}`, opts ) } module.exports.fromData = fromData function fromData (data, opts) { opts = ssriOpts(opts) const algorithms = opts.algorithms const optString = getOptString(opts.options) return algorithms.reduce((acc, algo) => { const digest = crypto.createHash(algo).update(data).digest('base64') const hash = new Hash( `${algo}-${digest}${optString}`, opts ) /* istanbul ignore else - it would be VERY strange if the string we * just calculated with an algo did not have an algo or digest. */ if (hash.algorithm && hash.digest) { const hashAlgo = hash.algorithm if (!acc[hashAlgo]) { acc[hashAlgo] = [] } acc[hashAlgo].push(hash) } return acc }, new Integrity()) } module.exports.fromStream = fromStream function fromStream (stream, opts) { opts = ssriOpts(opts) const istream = integrityStream(opts) return new Promise((resolve, reject) => { stream.pipe(istream) stream.on('error', reject) istream.on('error', reject) let sri istream.on('integrity', s => { sri = s }) istream.on('end', () => resolve(sri)) istream.on('data', () => {}) }) } 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) { throw Object.assign( new Error('No valid integrity hashes to check against'), { code: 'EINTEGRITY', } ) } else { return false } } const algorithm = sri.pickAlgorithm(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) { return match } else if (typeof opts.size === 'number' && (data.length !== opts.size)) { /* eslint-disable-next-line max-len */ const err = new Error(`data size mismatch when checking ${sri}.\n Wanted: ${opts.size}\n Found: ${data.length}`) err.code = 'EBADSIZE' err.found = data.length err.expected = opts.size err.sri = sri throw err } else { /* eslint-disable-next-line max-len */ const err = new Error(`Integrity checksum failed when using ${algorithm}: Wanted ${sri}, but got ${newSri}. (${data.length} bytes)`) err.code = 'EINTEGRITY' err.found = newSri err.expected = sri err.algorithm = algorithm err.sri = sri throw err } } module.exports.checkStream = checkStream function checkStream (stream, sri, opts) { 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) => { stream.pipe(checker) stream.on('error', reject) checker.on('error', reject) let verified checker.on('verified', s => { verified = s }) checker.on('end', () => resolve(verified)) checker.on('data', () => {}) }) } module.exports.integrityStream = integrityStream function integrityStream (opts = {}) { return new IntegrityStream(opts) } module.exports.create = createIntegrity function createIntegrity (opts) { opts = ssriOpts(opts) const algorithms = opts.algorithms const optString = getOptString(opts.options) const hashes = algorithms.map(crypto.createHash) return { update: function (chunk, enc) { hashes.forEach(h => h.update(chunk, enc)) return this }, digest: function (enc) { const integrity = algorithms.reduce((acc, algo) => { const digest = hashes.shift().digest('base64') const hash = new Hash( `${algo}-${digest}${optString}`, opts ) /* istanbul ignore else - it would be VERY strange if the hash we * just calculated with an algo did not have an algo or digest. */ if (hash.algorithm && hash.digest) { const hashAlgo = hash.algorithm if (!acc[hashAlgo]) { acc[hashAlgo] = [] } acc[hashAlgo].push(hash) } return acc }, new Integrity()) return integrity }, } } const NODE_HASHES = new Set(crypto.getHashes()) // This is a Best Effort™ at a reasonable priority for hash algos const DEFAULT_PRIORITY = [ 'md5', 'whirlpool', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', // TODO - it's unclear _which_ of these Node will actually use as its name // for the algorithm, so we guesswork it based on the OpenSSL names. 'sha3', 'sha3-256', 'sha3-384', 'sha3-512', 'sha3_256', 'sha3_384', 'sha3_512', ].filter(algo => NODE_HASHES.has(algo)) function getPrioritizedHash (algo1, algo2) { /* eslint-disable-next-line max-len */ return DEFAULT_PRIORITY.indexOf(algo1.toLowerCase()) >= DEFAULT_PRIORITY.indexOf(algo2.toLowerCase()) ? algo1 : algo2 } ������������������ssri-9.0.1/package.json�����������������������������������������������������������������������������0000664�0000000�0000000�00000002754�14241467455�0015013�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "name": "ssri", "version": "9.0.1", "description": "Standard Subresource Integrity library -- parses, serializes, generates, and verifies integrity metadata according to the SRI spec.", "main": "lib/index.js", "files": [ "bin/", "lib/" ], "scripts": { "prerelease": "npm t", "postrelease": "npm publish", "prepublishOnly": "git push origin --follow-tags", "posttest": "npm run lint", "test": "tap", "coverage": "tap", "lint": "eslint \"**/*.js\"", "postlint": "template-oss-check", "template-oss-apply": "template-oss-apply --force", "lintfix": "npm run lint -- --fix", "preversion": "npm test", "postversion": "npm publish", "snap": "tap" }, "tap": { "check-coverage": true }, "repository": { "type": "git", "url": "https://github.com/npm/ssri.git" }, "keywords": [ "w3c", "web", "security", "integrity", "checksum", "hashing", "subresource integrity", "sri", "sri hash", "sri string", "sri generator", "html" ], "author": "GitHub Inc.", "license": "ISC", "dependencies": { "minipass": "^3.1.1" }, "devDependencies": { "@npmcli/eslint-config": "^3.0.1", "@npmcli/template-oss": "3.5.0", "tap": "^16.0.1" }, "engines": { "node": "^12.13.0 || ^14.15.0 || >=16.0.0" }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "version": "3.5.0" } } ��������������������ssri-9.0.1/test/������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14241467455�0013474�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/test/check.js����������������������������������������������������������������������������0000664�0000000�0000000�00000016522�14241467455�0015115�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict' const crypto = require('crypto') const fs = require('fs') const path = require('path') const test = require('tap').test const ssri = require('..') const TEST_DATA = fs.readFileSync(__filename) function hash (data, algorithm) { return crypto.createHash(algorithm).update(data).digest('base64') } function fileStream () { return fs.createReadStream(__filename) } test('checkData', t => { const sri = ssri.parse({ algorithm: 'sha512', digest: hash(TEST_DATA, 'sha512'), }) const meta = sri.sha512[0] t.same( ssri.checkData(TEST_DATA, sri), meta, 'Buffer data successfully verified' ) t.doesNotThrow(() => { ssri.checkData(TEST_DATA, sri, { error: true }) }, 'error not thrown when error: true and data verifies') t.same( ssri.checkData(TEST_DATA, `sha512-${hash(TEST_DATA, 'sha512')}`), meta, 'Accepts string SRI' ) t.same( ssri.checkData(TEST_DATA, { algorithm: 'sha512', digest: hash(TEST_DATA, 'sha512'), }), meta, 'Accepts Hash-like SRI' ) t.same( ssri.checkData(TEST_DATA.toString('utf8'), sri), meta, 'String data successfully verified' ) t.same( ssri.checkData( TEST_DATA, `sha512-nope sha512-${hash(TEST_DATA, 'sha512')}` ), meta, 'succeeds if any of the hashes under the chosen algorithm match' ) t.equal( ssri.checkData('nope', sri), false, 'returns false when verification fails' ) t.throws(() => { ssri.checkData('nope', sri, { error: true }) }, /Integrity checksum failed/, 'integrity error thrown when error: true with bad data') t.throws(() => { ssri.checkData('nope', sri, { error: true, size: 3 }) }, /data size mismatch/, 'size error thrown when error: true with bad size') t.equal( ssri.checkData('nope', 'sha512-nope'), false, 'returns false on invalid sri hash' ) t.equal( ssri.checkData('nope', 'garbage'), false, 'returns false on garbage sri input' ) t.equal( ssri.checkData('nope', ''), false, 'returns false on empty sri input' ) t.throws(() => { ssri.checkData('nope', '', { error: true }) }, /No valid integrity hashes/, 'errors on empty sri input if error: true') t.same( ssri.checkData(TEST_DATA, [ 'sha512-nope', `sha1-${hash(TEST_DATA, 'sha1')}`, `sha512-${hash(TEST_DATA, 'sha512')}`, ].join(' '), { pickAlgorithm: (a, b) => { if (a === 'sha1' || b === 'sha1') { return 'sha1' } }, }), ssri.parse({ algorithm: 'sha1', digest: hash(TEST_DATA, 'sha1'), }).sha1[0], 'opts.pickAlgorithm can be used to customize which one is used.' ) t.same( ssri.checkData(TEST_DATA, [ `sha256-${hash(TEST_DATA, 'sha256')}`, `sha1-${hash(TEST_DATA, 'sha1')}`, `sha512-${hash(TEST_DATA, 'sha512')}`, ].join(' '), { pickAlgorithm: (a, b) => { return false }, }), ssri.parse({ algorithm: 'sha256', digest: hash(TEST_DATA, 'sha256'), }).sha256[0], 'opts.pickAlgorithm can return false to keep the first option' ) t.same( ssri.checkData(TEST_DATA, [ `sha1-${hash(TEST_DATA, 'sha1')}`, `sha384-${hash(TEST_DATA, 'sha384')}`, `sha256-${hash(TEST_DATA, 'sha256')}`, ].join(' ')), ssri.parse({ algorithm: 'sha384', digest: hash(TEST_DATA, 'sha384'), }).sha384[0], 'picks the "strongest" available algorithm, by default' ) t.end() }) test('checkStream', t => { const sri = ssri.parse({ algorithm: 'sha512', digest: hash(TEST_DATA, 'sha512'), }) const meta = sri.sha512[0] let streamEnded const stream = fileStream().on('end', () => { streamEnded = true }) return ssri.checkStream(stream, sri).then(res => { t.same(res, meta, 'Stream data successfully verified') t.ok(streamEnded, 'source stream ended') return ssri.checkStream( fileStream(), `sha512-${hash(TEST_DATA, 'sha512')}` ) }).then(res => { t.same(res, meta, 'Accepts string SRI') return ssri.checkStream(fileStream(), { algorithm: 'sha512', digest: hash(TEST_DATA, 'sha512'), }) }).then(res => { t.same(res, meta, 'Accepts Hash-like SRI') return ssri.checkStream( fileStream(), `sha512-nope sha512-${hash(TEST_DATA, 'sha512')}` ) }).then(res => { t.same( res, meta, 'succeeds if any of the hashes under the chosen algorithm match' ) return ssri.checkStream( fs.createReadStream(path.join(__dirname, '..', 'package.json')), sri ).then(() => { throw new Error('unexpected success') }, err => { t.equal(err.code, 'EINTEGRITY', 'checksum failure rejects the promise') }) }).then(() => { return ssri.checkStream( fs.createReadStream(path.join(__dirname, '..', 'package.json')), 'garbage' ).then(() => { throw new Error('unexpected success') }, err => { t.equal(err.code, 'EINTEGRITY', 'checksum failure if sri is garbage') }) }).then(() => { return ssri.checkStream( fs.createReadStream(path.join(__dirname, '..', 'package.json')), 'sha512-nope' ).then(() => { throw new Error('unexpected success') }, err => { t.equal(err.code, 'EINTEGRITY', 'checksum failure if sri has bad hash') }) }).then(() => { return ssri.checkStream(fileStream(), [ 'sha512-nope', `sha1-${hash(TEST_DATA, 'sha1')}`, `sha512-${hash(TEST_DATA, 'sha512')}`, ].join(' '), { pickAlgorithm: (a, b) => { if (a === 'sha1' || b === 'sha1') { return 'sha1' } }, }) }).then(res => { t.same( res, ssri.parse({ algorithm: 'sha1', digest: hash(TEST_DATA, 'sha1'), }).sha1[0], 'opts.pickAlgorithm can be used to customize which one is used.' ) return ssri.checkStream(fileStream(), [ `sha1-${hash(TEST_DATA, 'sha1')}`, `sha384-${hash(TEST_DATA, 'sha384')}`, `sha256-${hash(TEST_DATA, 'sha256')}`, ].join(' ')) }).then(res => { t.same( res, ssri.parse({ algorithm: 'sha384', digest: hash(TEST_DATA, 'sha384'), }).sha384[0], 'picks the "strongest" available algorithm, by default' ) return ssri.checkStream(fileStream(), [ `sha1-${hash(TEST_DATA, 'sha1')}`, `sha384-${hash(TEST_DATA, 'sha384')}`, `sha256-${hash(TEST_DATA, 'sha256')}`, ].join(' '), { algorithms: ['sha256'], }) }).then(res => { t.same( res, ssri.parse({ algorithm: 'sha384', digest: hash(TEST_DATA, 'sha384'), }).sha384[0], 'opts.algorithm still takes into account algo to check against' ) return ssri.checkStream(fileStream(), [ `sha1-${hash(TEST_DATA, 'sha1')}`, `sha384-${hash(TEST_DATA, 'sha384')}`, `sha256-${hash(TEST_DATA, 'sha256')}`, ].join(' '), { algorithms: ['sha512'], }) }).then(res => { t.same( res, ssri.parse({ algorithm: 'sha384', digest: hash(TEST_DATA, 'sha384'), }).sha384[0], '...even if opts.algorithms includes a hash that is not present' ) 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') }) }) }) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/test/create.js���������������������������������������������������������������������������0000664�0000000�0000000�00000005115�14241467455�0015277�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict' const test = require('tap').test const ssri = require('..') test('works just like from', function (t) { const integrity = ssri.fromData('hi') const integrityCreate = ssri.create().update('hi').digest() t.ok(integrityCreate instanceof integrity.constructor, 'should be same Integrity that fromData returns') t.equal(integrity + '', integrityCreate + '', 'should be the sam as fromData') t.end() }) test('pass in an algo multiple times', t => { t.match(ssri.fromData('hi', { algorithms: ['sha512', 'sha512'], }), { sha512: [ { /* eslint-disable-next-line max-len */ source: 'sha512-FQoU7VvqbMcxz4bEFWasQnqNtI7xuf1iZmSzv7uZBx+kySLzPd44cZuMg1Tit6udd+Dmf8EoQ5IKcS5z1Vjhlw==', /* eslint-disable-next-line max-len */ digest: 'FQoU7VvqbMcxz4bEFWasQnqNtI7xuf1iZmSzv7uZBx+kySLzPd44cZuMg1Tit6udd+Dmf8EoQ5IKcS5z1Vjhlw==', algorithm: 'sha512', options: [], }, { /* eslint-disable-next-line max-len */ source: 'sha512-FQoU7VvqbMcxz4bEFWasQnqNtI7xuf1iZmSzv7uZBx+kySLzPd44cZuMg1Tit6udd+Dmf8EoQ5IKcS5z1Vjhlw==', /* eslint-disable-next-line max-len */ digest: 'FQoU7VvqbMcxz4bEFWasQnqNtI7xuf1iZmSzv7uZBx+kySLzPd44cZuMg1Tit6udd+Dmf8EoQ5IKcS5z1Vjhlw==', algorithm: 'sha512', options: [], }, ], }) t.match(ssri.create({ options: ['foo=bar', 'baz=quux'], algorithms: ['sha512', 'sha512'], }).update('hi').digest(), { sha512: [ { /* eslint-disable-next-line max-len */ source: 'sha512-FQoU7VvqbMcxz4bEFWasQnqNtI7xuf1iZmSzv7uZBx+kySLzPd44cZuMg1Tit6udd+Dmf8EoQ5IKcS5z1Vjhlw==', /* eslint-disable-next-line max-len */ digest: 'FQoU7VvqbMcxz4bEFWasQnqNtI7xuf1iZmSzv7uZBx+kySLzPd44cZuMg1Tit6udd+Dmf8EoQ5IKcS5z1Vjhlw==', algorithm: 'sha512', options: [], }, { /* eslint-disable-next-line max-len */ source: 'sha512-FQoU7VvqbMcxz4bEFWasQnqNtI7xuf1iZmSzv7uZBx+kySLzPd44cZuMg1Tit6udd+Dmf8EoQ5IKcS5z1Vjhlw==', /* eslint-disable-next-line max-len */ digest: 'FQoU7VvqbMcxz4bEFWasQnqNtI7xuf1iZmSzv7uZBx+kySLzPd44cZuMg1Tit6udd+Dmf8EoQ5IKcS5z1Vjhlw==', algorithm: 'sha512', options: [], }, ], }) t.end() }) test('can pass options', function (t) { const integrity = ssri.create({ algorithms: ['sha256', 'sha384'] }).update('hi').digest() t.equal( integrity + '', 'sha256-j0NDRmSPa5bfid2pAcUXaxCm2Dlh3TwayItZstwyeqQ= ' + 'sha384-B5EAbfgShHckT1PQ/c4hDbgfVXV1EOJqzuNcGKa86qKNzbv9bcBBubTcextU439S', 'should be expected value' ) t.end() }) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/test/from.js�����������������������������������������������������������������������������0000664�0000000�0000000�00000005151�14241467455�0014777�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict' const crypto = require('crypto') const fs = require('fs') const test = require('tap').test const ssri = require('..') const TEST_DATA = fs.readFileSync(__filename) function hash (data, algorithm) { return crypto.createHash(algorithm).update(data).digest('base64') } function fileStream () { return fs.createReadStream(__filename) } test('fromHex', t => { t.equal( ssri.fromHex('deadbeef', 'sha1').toString(), 'sha1-3q2+7w==', 'created an Integrity object from a given hex + sha' ) t.equal( ssri.fromHex('deadbeef', 'sha512', { options: ['a', 'b', 'c'] }).toString(), 'sha512-3q2+7w==?a?b?c', 'options added to entry' ) t.end() }) test('fromData', t => { t.equal( ssri.fromData(TEST_DATA).toString(), `sha512-${hash(TEST_DATA, 'sha512')}`, 'generates sha512 integrity object from Buffer data' ) t.equal( ssri.fromData(TEST_DATA.toString('utf8')).toString(), `sha512-${hash(TEST_DATA, 'sha512')}`, 'generates sha512 integrity object from String data' ) t.equal( ssri.fromData(TEST_DATA, { algorithms: ['sha256', 'sha384'] }).toString(), `sha256-${hash(TEST_DATA, 'sha256')} sha384-${hash(TEST_DATA, 'sha384')}`, 'can generate multiple metadata entries with opts.algorithms' ) t.equal( ssri.fromData(TEST_DATA, { algorithms: ['sha256', 'sha384'], options: ['foo', 'bar'], }).toString(), [ `sha256-${hash(TEST_DATA, 'sha256')}?foo?bar`, `sha384-${hash(TEST_DATA, 'sha384')}?foo?bar`, ].join(' '), 'can add opts.options to each entry' ) t.end() }) test('fromStream', t => { let streamEnded const stream = fileStream().on('end', () => { streamEnded = true }) return ssri.fromStream(stream).then(integrity => { t.equal( integrity.toString(), `sha512-${hash(TEST_DATA, 'sha512')}`, 'generates sha512 from a stream' ) t.ok(streamEnded, 'source stream ended') return ssri.fromStream(fileStream(), { algorithms: ['sha256', 'sha384'], }) }).then(integrity => { t.equal( integrity.toString(), [ `sha256-${hash(TEST_DATA, 'sha256')}`, `sha384-${hash(TEST_DATA, 'sha384')}`, ].join(' '), 'can generate multiple metadata entries with opts.algorithms' ) return ssri.fromStream(fileStream(), { algorithms: ['sha256', 'sha384'], options: ['foo', 'bar'], }) }).then(integrity => { t.equal( integrity.toString(), [ `sha256-${hash(TEST_DATA, 'sha256')}?foo?bar`, `sha384-${hash(TEST_DATA, 'sha384')}?foo?bar`, ].join(' '), 'can add opts.options to each entry' ) }) }) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/test/integrity-stream.js�����������������������������������������������������������������0000664�0000000�0000000�00000006334�14241467455�0017347�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict' const test = require('tap').test const ssri = require('..') test('works with no options', t => { const TARGET = ssri.fromData('foo') const stream = ssri.integrityStream() stream.write('foo') let integrity stream.on('integrity', i => { integrity = i }) stream.on('end', () => { t.same(integrity, TARGET, 'matching integrity emitted') t.end() }) stream.resume() stream.end() }) test('generates integrity', t => { const TARGET = ssri.fromData('foo') const stream = ssri.integrityStream({ integrity: TARGET }) stream.write('foo') let collected = '' stream.on('data', d => { collected += d.toString() }) let integrity stream.on('integrity', i => { integrity = i }) let size stream.on('size', s => { size = s }) let verified stream.on('verified', v => { verified = v }) stream.on('end', () => { t.equal(collected, 'foo', 'stream output is complete') t.equal(size, 3, 'size emitted') t.same(integrity, TARGET, 'matching integrity emitted') t.same(verified, TARGET.sha512[0], 'verified emitted') t.end() }) stream.end() }) test('re-emits for late listeners', t => { const TARGET = ssri.fromData('foo') const stream = ssri.integrityStream({ integrity: TARGET }) stream.write('foo') let collected = '' stream.on('data', d => { collected += d.toString() }) stream.on('end', () => { // we add the listeners _after_ the end event this time to ensure that the events // get emitted again for late listeners let integrity stream.on('integrity', i => { integrity = i }) let size stream.on('size', s => { size = s }) let verified stream.on('verified', v => { verified = v }) t.equal(collected, 'foo', 'stream output is complete') t.equal(size, 3, 'size emitted') t.same(integrity, TARGET, 'matching integrity emitted') t.same(verified, TARGET.sha512[0], 'verified emitted') t.end() }) stream.end() }) test('optional algorithms option', t => { const TARGET = ssri.fromData('foo', { algorithms: ['sha1', 'sha256'] }) const stream = ssri.integrityStream({ algorithms: ['sha1', 'sha256'] }) stream.write('foo') stream.on('data', () => {}) let integrity stream.on('integrity', i => { integrity = i }) stream.on('end', () => { t.same(integrity, TARGET, 'matching integrity emitted') t.end() }) stream.end() }) test('verification for correct data succeeds', t => { const TARGET = ssri.fromData('foo') const stream = ssri.integrityStream({ integrity: TARGET, }) stream.write('foo') let collected = '' stream.on('data', d => { collected += d.toString() }) let integrity stream.on('integrity', i => { integrity = i }) stream.on('end', () => { t.equal(collected, 'foo', 'stream output is complete') t.same(integrity, TARGET, 'matching integrity emitted') t.end() }) stream.end() }) test('verification for wrong data fails', t => { const stream = ssri.integrityStream({ integrity: ssri.fromData('bar'), }) stream.write('foo') stream.on('data', () => {}) stream.on('error', err => { t.equal(err.code, 'EINTEGRITY', 'errors with EINTEGRITY on mismatch') t.end() }) stream.end() }) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/test/integrity.js������������������������������������������������������������������������0000664�0000000�0000000�00000010547�14241467455�0016057�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict' const Buffer = require('safe-buffer').Buffer const test = require('tap').test const ssri = require('..') test('toString()', t => { /* eslint-disable-next-line max-len */ const sri = ssri.parse('sha1-eUN/Xt2hP5wGabl43XqQZt0gWfE= sha256-Qhx213Vjr6GRSEawEL0WTzlb00whAuXpngy5zxc8HYc=') t.equal( sri.toString(), 'sha1-eUN/Xt2hP5wGabl43XqQZt0gWfE= sha256-Qhx213Vjr6GRSEawEL0WTzlb00whAuXpngy5zxc8HYc=', 'integrity objects from ssri.parse() can use toString()' ) t.equal( sri.toString({ strict: true }), 'sha256-Qhx213Vjr6GRSEawEL0WTzlb00whAuXpngy5zxc8HYc=', 'accepts strict mode option' ) t.equal( sri.toString({ sep: '\n' }), 'sha1-eUN/Xt2hP5wGabl43XqQZt0gWfE=\nsha256-Qhx213Vjr6GRSEawEL0WTzlb00whAuXpngy5zxc8HYc=', 'accepts separator option' ) t.end() }) test('toJSON()', t => { const sri = ssri.parse('sha512-foo sha256-bar!') t.equal( sri.toJSON(), 'sha512-foo sha256-bar!', 'integrity objects from ssri.parse() can use toJSON()' ) t.equal( sri.sha512[0].toJSON(), 'sha512-foo', 'hash objects should toJSON also' ) t.end() }) test('concat()', t => { const sri = ssri.parse('sha512-foo') t.equal( sri.concat('sha512-bar').toString(), 'sha512-foo sha512-bar', 'concatenates with a string' ) t.equal( sri.concat({ digest: 'bar', algorithm: 'sha384' }).toString(), 'sha512-foo sha384-bar', 'concatenates with an Hash-like' ) t.equal( sri.concat({ sha384: [{ digest: 'bar', algorithm: 'sha384' }], sha1: [{ digest: 'baz', algorithm: 'sha1' }], }).toString(), 'sha512-foo sha384-bar sha1-baz', 'concatenates with an Integrity-like' ) t.equal( sri.concat( { digest: 'bar', algorithm: 'sha1' } ).concat( 'sha1-baz' ).concat( 'sha512-quux' ).toString(), 'sha512-foo sha512-quux sha1-bar sha1-baz', 'preserves relative order for algorithms between different concatenations' ) /* eslint-disable-next-line max-len */ const strictSri = ssri.parse('sha512-WrLorGiX4iEWOOOaJSiCrmDIamA47exH+Bz7tVwIPb4sCU8w4iNqGCqYuspMMeU5pgz/sU7koP5u8W3RCUojGw==') t.equal( strictSri.concat('sha1-eUN/Xt2hP5wGabl43XqQZt0gWfE=', { strict: true, }).toString(), /* eslint-disable-next-line max-len */ 'sha512-WrLorGiX4iEWOOOaJSiCrmDIamA47exH+Bz7tVwIPb4sCU8w4iNqGCqYuspMMeU5pgz/sU7koP5u8W3RCUojGw==', 'accepts strict mode option' ) t.end() }) test('match()', t => { const sri = ssri.parse('sha1-foo sha512-bar') t.match(sri.match('sha1-foo'), { algorithm: 'sha1', digest: 'foo', }, 'returns the matching hash') t.match(sri.match(ssri.parse('sha1-foo')), { algorithm: 'sha1', digest: 'foo', }, 'accepts other Integrity objects') t.match(sri.match(ssri.parse('sha1-foo')), { algorithm: 'sha1', digest: 'foo', }, 'accepts other Hash objects') t.match(sri.match({ digest: 'foo', algorithm: 'sha1' }), { algorithm: 'sha1', digest: 'foo', }, 'accepts Hash-like objects') t.match(sri.match('sha1-bar sha512-bar'), { algorithm: 'sha512', digest: 'bar', }, 'returns the strongest match') t.notOk(sri.match('sha512-foo'), 'falsy when match fails') t.notOk(sri.match('sha384-foo'), 'falsy when match fails') t.end() }) test('pickAlgorithm()', t => { const sri = ssri.parse('sha1-foo sha512-bar sha384-baz') t.equal(sri.pickAlgorithm(), 'sha512', 'picked best algorithm') t.equal( ssri.parse('unknown-deadbeef uncertain-bada55').pickAlgorithm(), 'unknown', 'unrecognized algorithm returned if none others known' ) t.equal( sri.pickAlgorithm({ pickAlgorithm: (a, b) => 'sha384', }), 'sha384', 'custom pickAlgorithm function accepted' ) t.end() }) test('hexDigest()', t => { t.equal( ssri.parse('sha512-foo').hexDigest(), Buffer.from('foo', 'base64').toString('hex'), 'returned hex version of base64 digest') t.equal( ssri.parse('sha512-bar', { single: true }).hexDigest(), Buffer.from('bar', 'base64').toString('hex'), 'returned hex version of base64 digest') t.end() }) test('isIntegrity and isHash', t => { const sri = ssri.parse('sha512-bar') t.ok(sri.isIntegrity, 'full sri has !!.isIntegrity') t.ok( sri.sha512[0].isHash, 'sri hash has !!.isHash' ) t.end() }) test('semi-private', t => { t.equal(ssri.Integrity, undefined, 'Integrity class is module-private.') t.end() }) ���������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/test/mutable-opts-resilience.js����������������������������������������������������������0000664�0000000�0000000�00000003165�14241467455�0020573�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������const ssri = require('../') const t = require('tap') const data = 'hello world' const expectIntegrity = ssri.fromData(data, { algorithms: ['sha512'] }) const expectSize = data.length t.test('support adding bad integrity later', t => { const opts = {} const stream = ssri.integrityStream(opts) opts.integrity = ssri.parse('sha512-deepbeets') return t.rejects(stream.end(data).collect(), { code: 'EINTEGRITY', }) }) t.test('support adding bad integrity string later', t => { const opts = {} const stream = ssri.integrityStream(opts) opts.integrity = 'sha512-deepbeets' return t.rejects(stream.end(data).collect(), { code: 'EINTEGRITY', }) }) t.test('support adding bad size later', t => { const opts = {} const stream = ssri.integrityStream(opts) opts.size = 2 return t.rejects(stream.end(data).collect(), { code: 'EBADSIZE', }) }) t.test('support adding good integrity later', t => { const opts = {} const stream = ssri.integrityStream(opts) opts.integrity = expectIntegrity return stream.end(data).on('verified', match => { t.same(match, expectIntegrity.sha512[0]) }).collect() }) t.test('support adding good integrity string later', t => { const opts = {} const stream = ssri.integrityStream(opts) opts.integrity = String(expectIntegrity) return stream.end(data).on('verified', match => { t.same(match, expectIntegrity.sha512[0]) }).collect() }) t.test('support adding good size later', t => { const opts = {} const stream = ssri.integrityStream(opts) opts.size = expectSize return stream.end(data).on('size', size => { t.same(size, expectSize) }).collect() }) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/test/parse.js����������������������������������������������������������������������������0000664�0000000�0000000�00000014602�14241467455�0015147�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict' const crypto = require('crypto') const fs = require('fs') const test = require('tap').test const ssri = require('..') const TEST_DATA = fs.readFileSync(__filename) function hash (data, algorithm) { return crypto.createHash(algorithm).update(data).digest('base64') } test('parses single-entry integrity string', t => { const sha = hash(TEST_DATA, 'sha512') const integrity = `sha512-${sha}` t.same(ssri.parse(integrity), { sha512: [{ source: integrity, digest: sha, algorithm: 'sha512', options: [], }], }, 'single entry parsed into full Integrity instance') t.end() }) test('parses options from integrity string', t => { const sha = hash(TEST_DATA, 'sha512') const integrity = `sha512-${sha}?one?two?three` t.same(ssri.parse(integrity), { sha512: [{ source: integrity, digest: sha, algorithm: 'sha512', options: ['one', 'two', 'three'], }], }, 'single entry parsed into full Integrity instance') t.end() }) test('parses options from integrity string in strict mode', t => { const sha = hash(TEST_DATA, 'sha512') const integrity = `sha512-${sha}?one?two?three` t.same(ssri.parse(integrity, { strict: true }), { sha512: [{ source: integrity, digest: sha, algorithm: 'sha512', options: ['one', 'two', 'three'], }], }, 'single entry parsed into full Integrity instance') t.end() }) test('can parse single-entry string directly into Hash', t => { const sha = hash(TEST_DATA, 'sha512') const integrity = `sha512-${sha}` t.same(ssri.parse(integrity, { single: true }), { source: integrity, digest: sha, algorithm: 'sha512', options: [], }, 'single entry parsed into single Hash instance') t.end() }) test('accepts Hash-likes as input', t => { const algorithm = 'sha512' const digest = hash(TEST_DATA, 'sha512') const sriLike = { algorithm, digest, options: ['foo'], } const parsed = ssri.parse(sriLike) t.same(parsed, { sha512: [{ source: `sha512-${digest}?foo`, algorithm, digest, options: ['foo'], }], }, 'Metadata-like returned as full Integrity instance') t.end() }) test('omits unsupported algos in strict mode only', t => { const xxx = new Array(50).join('x') t.match(ssri.parse(`foo-${xxx}`, { strict: true, single: true, }), { source: `foo-${xxx}`, algorithm: '', digest: '', options: [], }) t.match(ssri.parse(`foo-${xxx}`, { strict: false, single: true, }), { source: `foo-${xxx}`, algorithm: 'foo', digest: xxx, options: [], }) t.match(ssri.parse(`sha512-${xxx}`, { strict: true, single: true, }), { source: `sha512-${xxx}`, algorithm: 'sha512', digest: xxx, options: [], }) t.end() }) test('use " " as sep when opts.sep is falsey', t => { const parsed = ssri.parse('yum-somehash foo-barbaz') t.equal(parsed.toString({ sep: false }), 'yum-somehash foo-barbaz') t.equal(parsed.toString({ sep: '\t' }), 'yum-somehash\tfoo-barbaz') t.end() }) test('accepts Integrity-like as input', t => { const algorithm = 'sha512' const digest = hash(TEST_DATA, 'sha512') const sriLike = { sha512: [{ algorithm, digest, options: ['foo'], }], } const parsed = ssri.parse(sriLike) t.same(parsed, { sha512: [{ source: `sha512-${digest}?foo`, algorithm, digest, options: ['foo'], }], }, 'Integrity-like returned as full Integrity instance') t.not(parsed, sriLike, 'Objects are separate instances.') t.end() }) test('parses and groups multiple-entry strings', t => { const hashes = [ `sha1-${hash(TEST_DATA, 'sha1')}`, `sha256-${hash(TEST_DATA, 'sha256')}`, 'sha1-OthERhaSh', 'unknown-WoWoWoWoW', ] t.same(ssri.parse(hashes.join(' ')), { sha1: [{ source: hashes[0], digest: hashes[0].split('-')[1], algorithm: 'sha1', options: [], }, { source: hashes[2], digest: hashes[2].split('-')[1], algorithm: 'sha1', options: [], }], sha256: [{ source: hashes[1], digest: hashes[1].split('-')[1], algorithm: 'sha256', options: [], }], unknown: [{ source: hashes[3], digest: hashes[3].split('-')[1], algorithm: 'unknown', options: [], }], }) t.end() }) test('parses any whitespace as entry separators', t => { const integrity = '\tsha512-foobarbaz \n\rsha384-bazbarfoo\n \t \t\t sha256-foo' t.same(ssri.parse(integrity), { sha512: [{ source: 'sha512-foobarbaz', algorithm: 'sha512', digest: 'foobarbaz', options: [], }], sha384: [{ source: 'sha384-bazbarfoo', algorithm: 'sha384', digest: 'bazbarfoo', options: [], }], sha256: [{ source: 'sha256-foo', algorithm: 'sha256', digest: 'foo', options: [], }], }, 'whitespace around metadata skipped and trimmed') t.end() }) test('discards invalid format entries', t => { const missingDash = 'thisisbad' const missingAlgorithm = '-deadbeef' const missingDigest = 'sha512-' const valid = `sha512-${hash(TEST_DATA, 'sha512')}` t.equal(ssri.parse([ missingDash, missingAlgorithm, missingDigest, valid, ].join(' ')).toString(), valid, 'invalid entries thrown out') t.end() }) test('trims whitespace from either end', t => { const integrity = ` sha512-${hash(TEST_DATA, 'sha512')} ` t.same(ssri.parse(integrity), { sha512: [{ source: integrity.trim(), algorithm: 'sha512', digest: hash(TEST_DATA, 'sha512'), options: [], }], }, 'whitespace is trimmed from source before parsing') t.end() }) test('supports strict spec parsing', t => { const valid = `sha512-${hash(TEST_DATA, 'sha512')}` const badAlgorithm = `sha1-${hash(TEST_DATA, 'sha1')}` const badBase64 = 'sha512-@#$@%#$' const badOpts = `${valid}?\x01\x02` t.same(ssri.parse([ badAlgorithm, badBase64, badOpts, valid, ].join(' '), { strict: true, }).toString(), valid, 'entries that fail strict check rejected') t.end() }) test('does not allow weird stuff in sri', t => { const badInt = 'mdc2\u0000/../../../hello_what_am_I_doing_here-Juwtg9UFssfrRfwsXu+n/Q==' const bad = ssri.parse(badInt) const badStrict = ssri.parse(badInt, { strict: true }) const expect = ssri.parse('') t.strictSame(bad, expect) t.strictSame(badStrict, expect) t.end() }) ������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/test/stringify.js������������������������������������������������������������������������0000664�0000000�0000000�00000005553�14241467455�0016060�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������'use strict' const crypto = require('crypto') const fs = require('fs') const test = require('tap').test const ssri = require('..') const TEST_DATA = fs.readFileSync(__filename) function hash (data, algorithm) { return crypto.createHash(algorithm).update(data).digest('base64') } test('serializes Integrity-likes', t => { const sriLike = { sha512: [{ digest: 'foo', algorithm: 'sha512', options: ['ayy', 'woo'], }, { digest: 'bar', algorithm: 'sha512', }], whirlpool: [{ digest: 'wut', algorithm: 'whirlpool', }], } t.equal( ssri.stringify(sriLike), 'sha512-foo?ayy?woo sha512-bar whirlpool-wut', 'stringification contains correct data for all entries' ) t.end() }) test('serializes Hash-likes', t => { const sriLike = { digest: 'foo', algorithm: 'sha512', } t.equal( ssri.stringify(sriLike), 'sha512-foo', 'serialization has correct data' ) t.end() }) test('serialized plain strings into a valid parsed version', t => { const sri = ' \tsha512-foo?bar \n\n\nsha1-nope\r' t.equal( ssri.stringify(sri), 'sha512-foo?bar sha1-nope', 'cleaned-up string with identical contents generated' ) t.end() }) test('accepts a separator opt', t => { const sriLike = { sha512: [{ algorithm: 'sha512', digest: 'foo', }, { algorithm: 'sha512', digest: 'bar', }], } t.equal( ssri.stringify(sriLike, { sep: '\n' }), 'sha512-foo\nsha512-bar' ) t.equal( ssri.stringify(sriLike, { sep: ' | ' }), 'sha512-foo | sha512-bar' ) t.end() }) test('support strict serialization', t => { const sriLike = { // only sha256, sha384, and sha512 are allowed by the spec sha1: [{ algorithm: 'sha1', digest: 'feh', }], sha256: [{ algorithm: 'sha256', // Must be valid base64 digest: 'wut!!!??!!??!', }, { algorithm: 'sha256', digest: hash(TEST_DATA, 'sha256'), options: ['foo'], }], sha512: [{ algorithm: 'sha512', digest: hash(TEST_DATA, 'sha512'), // Options must use VCHAR options: ['\x01'], }], } t.equal( ssri.stringify(sriLike, { strict: true }), `sha256-${hash(TEST_DATA, 'sha256')}?foo`, 'entries that do not conform to strict spec interpretation removed' ) t.equal( /* eslint-disable-next-line max-len */ ssri.stringify('sha512-WrLorGiX4iEWOOOaJSiCrmDIamA47exH+Bz7tVwIPb4sCU8w4iNqGCqYuspMMeU5pgz/sU7koP5u8W3RCUojGw== sha256-Qhx213Vjr6GRSEawEL0WTzlb00whAuXpngy5zxc8HYc=', { sep: ' \r|\n\t', strict: true }), /* eslint-disable-next-line max-len */ 'sha512-WrLorGiX4iEWOOOaJSiCrmDIamA47exH+Bz7tVwIPb4sCU8w4iNqGCqYuspMMeU5pgz/sU7koP5u8W3RCUojGw== \r \n\tsha256-Qhx213Vjr6GRSEawEL0WTzlb00whAuXpngy5zxc8HYc=', 'strict mode replaces non-whitespace characters in separator with space' ) t.end() }) �����������������������������������������������������������������������������������������������������������������������������������������������������ssri-9.0.1/test/update.js���������������������������������������������������������������������������0000664�0000000�0000000�00000000644�14241467455�0015320�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������const ssri = require('../') const t = require('tap') const i = ssri.parse('sha1-foo') const o = ssri.parse('sha512-bar') i.merge(o) t.equal(i.toString(), 'sha1-foo sha512-bar', 'added second algo') t.throws(() => i.merge(ssri.parse('sha1-baz')), { message: 'hashes do not match, cannot update integrity', }) i.merge(o) i.merge(ssri.parse('sha1-foo')) t.equal(i.toString(), 'sha1-foo sha512-bar', 'did not duplicate') ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������