From d6cf181061f5ffbc27061808fcb5241e6807fd08 Mon Sep 17 00:00:00 2001 From: Kai Koenig Date: Thu, 3 Sep 2026 15:01:27 +1200 Subject: [PATCH] ci: harden npm dependency checks --- .github/workflows/node.js.yml | 84 ++++++++++++++++++++++++++++++++++- package.json | 5 --- 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 89b07e2..6f14c3f 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -9,6 +9,9 @@ on: pull_request: branches: [ "master", "develop" ] +permissions: + contents: read + jobs: build: @@ -26,7 +29,8 @@ jobs: with: node-version: ${{ matrix.node-version }} cache: 'npm' - - run: npm ci + - run: npm ci --ignore-scripts + - run: npm run prepare - run: npm test # Run CommonJS compatibility test - run: npm run test:cjs @@ -47,5 +51,81 @@ jobs: with: node-version: 24.x cache: 'npm' - - run: npm ci + - 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')" diff --git a/package.json b/package.json index bc3fa3e..720491f 100644 --- a/package.json +++ b/package.json @@ -69,11 +69,6 @@ "debug": "^4.3.4", "stack-trace": "0.0.10" }, - "overrides": { - "keyv": "4.5.4", - "flat-cache": "4.0.1", - "file-entry-cache": "8.0.0" - }, "keywords": [ "APM", "crash",