ci: harden npm dependency checks #926
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node | |
| # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs | |
| name: Node.js CI | |
| on: | |
| push: | |
| branches: [ "master", "develop" ] | |
| pull_request: | |
| branches: [ "master", "develop" ] | |
| permissions: | |
| contents: read | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| node-version: [20.x, 22.x, 24.x] | |
| # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Use Node.js ${{ matrix.node-version }} | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| cache: 'npm' | |
| - run: npm ci --ignore-scripts | |
| - run: npm run prepare | |
| - run: npm test | |
| # Run CommonJS compatibility test | |
| - run: npm run test:cjs | |
| # Run static analysis on JS files | |
| - run: npm run eslint | |
| # Run static analysis on TS files | |
| - run: npm run tseslint | |
| # Checks code formatting using the version pinned in package-lock.json. | |
| prettier: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Use Node.js 24.x | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version: 24.x | |
| cache: 'npm' | |
| - run: npm ci --ignore-scripts | |
| - run: npm exec -- prettier --check "lib/*.ts" "test/*.js" "examples/**/*.js" | |
| supply-chain: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-node@v7 | |
| with: | |
| node-version: 24.x | |
| cache: 'npm' | |
| - name: Reject known malicious package versions | |
| run: | | |
| node <<'NODE' | |
| const fs = require('node:fs') | |
| const blocked = new Map([ | |
| ['keyv', '6.0.0'], | |
| ['flat-cache', '6.1.24'], | |
| ['file-entry-cache', '11.1.6'] | |
| ]) | |
| const lockfiles = [ | |
| 'package-lock.json', | |
| 'examples/express-sample/package-lock.json', | |
| 'examples/using-domains/package-lock.json' | |
| ] | |
| for (const lockfile of lockfiles) { | |
| const lock = JSON.parse(fs.readFileSync(lockfile, 'utf8')) | |
| for (const [path, metadata] of Object.entries(lock.packages)) { | |
| for (const [name, version] of blocked) { | |
| if ((path === `node_modules/${name}` || path.endsWith(`/node_modules/${name}`)) && metadata.version === version) { | |
| throw new Error(`${lockfile} contains known malicious package ${name}@${version}`) | |
| } | |
| } | |
| } | |
| } | |
| NODE | |
| - run: npm ci --ignore-scripts | |
| - run: npm audit --omit=dev | |
| - run: npm audit signatures | |
| examples: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| example: [express-sample, using-domains] | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-node@v7 | |
| with: | |
| node-version: 24.x | |
| cache: 'npm' | |
| cache-dependency-path: | | |
| package-lock.json | |
| examples/${{ matrix.example }}/package-lock.json | |
| - run: npm ci --ignore-scripts | |
| - run: npm run prepare | |
| - run: npm ci --ignore-scripts | |
| working-directory: examples/${{ matrix.example }} | |
| - run: npm audit --omit=dev | |
| working-directory: examples/${{ matrix.example }} | |
| - run: npm audit signatures | |
| working-directory: examples/${{ matrix.example }} | |
| - name: Smoke test Express example | |
| if: matrix.example == 'express-sample' | |
| working-directory: examples/express-sample | |
| env: | |
| NODE_ENV: production | |
| run: node -e "require('./app')" | |
| - name: Smoke test domains example | |
| if: matrix.example == 'using-domains' | |
| working-directory: examples/using-domains | |
| run: | | |
| node --check app.js | |
| node -e "require('config'); require('raygun')" |