From f4d7ba535839d0d1da46eaec0482fba794deb8c7 Mon Sep 17 00:00:00 2001 From: baixiangcpp Date: Thu, 16 Jul 2026 14:12:32 -0600 Subject: [PATCH] Remediate Next bundled PostCSS advisory (#7) --- .github/pull_request_template.md | 2 +- .github/workflows/ci.yml | 2 +- .../security/next-postcss-advisory-runbook.md | 63 +++++----- package-lock.json | 116 +++++++++--------- package.json | 12 +- .../next-postcss-advisory-runbook.test.ts | 35 +++++- tests/guards/supply-chain-audit-guard.test.ts | 19 ++- 7 files changed, 146 insertions(+), 103 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 251f1b65..525ac5bf 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -21,7 +21,7 @@ - [ ] No secrets, private payloads, local reports, or process files committed ## Commands Run -- [ ] `npm run check:audit:prod-high` +- [ ] `npm run check:audit:prod` - [ ] `npm run generate:tool-index` - [ ] `npm run check:tool-index` - [ ] `npm run generate:client-tool-lookup` diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b3d5aed3..d3e5d3d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: run: npm ci - name: Run Production Dependency Audit - run: npm run check:audit:prod-high + run: npm run check:audit:prod - name: Run ESLint run: npm run lint diff --git a/docs/security/next-postcss-advisory-runbook.md b/docs/security/next-postcss-advisory-runbook.md index f212636a..511c27dd 100644 --- a/docs/security/next-postcss-advisory-runbook.md +++ b/docs/security/next-postcss-advisory-runbook.md @@ -1,47 +1,51 @@ # Next Bundled PostCSS Advisory Runbook -This runbook tracks issue #7: GHSA-qx2v-qp2m-jg93 through Next.js bundled `postcss`. +This runbook records the remediation for issue #7: GHSA-qx2v-qp2m-jg93 through +Next.js bundled `postcss`. -The issue must stay open until a stable Next.js release bundles `postcss >= 8.5.10`, or an upstream non-applicable determination is documented. +## Stable Remediation -## Current Stable Evidence +Checked on 2026-07-16: -Checked on 2026-06-27: +- `npm view next version`: `16.2.10` +- `npm view @next/bundle-analyzer version`: `16.2.10` +- Stable Next still declares vulnerable `postcss@8.4.31`. +- The project keeps Next, `@next/bundle-analyzer`, and `eslint-config-next` aligned at + stable `16.2.10`. +- A controlled npm override replaces only Next's internal PostCSS copy with + `postcss@8.5.10`, the first patched version already used by Next 16.3 canaries. -- `npm view next version`: `16.2.9` -- `npm view @next/bundle-analyzer version`: `16.2.9` -- `npm view next@latest dependencies --json`: bundled `postcss` is `8.4.31` -- Project dependency: `next@16.2.9` -- Project dev dependency: `@next/bundle-analyzer@16.2.9` +This avoids the unsafe framework downgrade proposed by `npm audit fix --force`, does +not suppress the advisory, and keeps the application on a stable Next release. -This does not meet the closing requirement because `8.4.31 < 8.5.10`. - -## Stable Remediation Check - -Run on a dedicated branch: - -```bash -git checkout main -git pull --ff-only origin main -git checkout -b chore/next-postcss-stable-check -npm view next version -npm view @next/bundle-analyzer version -npm view next@latest dependencies --json -``` - -If stable Next bundles `postcss >= 8.5.10`, update Next and bundle analyzer together: +After `npm ci`, verify the effective dependency and production audit: ```bash -npm install next@ @next/bundle-analyzer@ --package-lock-only -npm ci node -p "require('./node_modules/next/package.json').version" node -p "require('./node_modules/next/node_modules/postcss/package.json').version" npm audit --omit=dev --json ``` -Required validation before closing #7: +Expected results: + +- Next reports `16.2.10`. +- Next's effective PostCSS reports `8.5.10`, satisfying `postcss >= 8.5.10`. +- The production audit reports 0 moderate/high/critical vulnerabilities and no + `next -> postcss` advisory path. + +## Upstream Evidence + +- Advisory: https://github.com/advisories/GHSA-qx2v-qp2m-jg93 +- Next PostCSS 8.5.10 bump: https://github.com/vercel/next.js/pull/93288 +- Next applicability discussion: https://github.com/vercel/next.js/issues/93234 + +## Regression Validation + +Run the complete release-sensitive validation set whenever the Next version or +override changes: ```bash +npm run check:audit:prod npm run check:audit:prod-high npm run lint npm test @@ -59,6 +63,8 @@ npm run test:e2e:pwa - Do not merge Next canary into `main` without an explicit project decision. - Do not suppress the advisory without upstream evidence. - Do not loosen production audit gates. +- Do not remove the override until stable Next resolves PostCSS to `8.5.10` or newer + without it. ## Closure Comment Template @@ -72,6 +78,7 @@ Tester: Stable Next version: Bundled postcss version: npm audit --omit=dev result: +check:audit:prod: check:audit:prod-high: lint: test: diff --git a/package-lock.json b/package-lock.json index 7fc71256..38b9a6e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "jwt-decode": "^4.0.0", "lorem-ipsum": "^2.0.8", "lucide-react": "^0.575.0", - "next": "16.2.9", + "next": "16.2.10", "pdf-lib": "^1.17.1", "qrcode": "^1.5.4", "radix-ui": "^1.4.3", @@ -50,7 +50,7 @@ "yaml": "^2.8.2" }, "devDependencies": { - "@next/bundle-analyzer": "16.2.9", + "@next/bundle-analyzer": "16.2.10", "@tailwindcss/postcss": "^4", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", @@ -69,7 +69,7 @@ "@vitest/coverage-v8": "^4.1.8", "axe-core": "^4.11.1", "eslint": "^9", - "eslint-config-next": "16.2.9", + "eslint-config-next": "16.2.10", "jsdom": "^28.1.0", "playwright": "^1.58.2", "serve-handler": "^6.1.7", @@ -1988,9 +1988,9 @@ } }, "node_modules/@next/bundle-analyzer": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/bundle-analyzer/-/bundle-analyzer-16.2.9.tgz", - "integrity": "sha512-yGWyLbC8MMn+hk9j6l6GammpbUz5S3yZzl3lpoWfVXhIpJfh6kAWG7JwF4WoG3f0VnYRY959yo1QQEI8mV/+jg==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/bundle-analyzer/-/bundle-analyzer-16.2.10.tgz", + "integrity": "sha512-KcepWhb3IVniZgm00GSSCQDEUQqZXuXtuXRh8J6e3Un342TcQ77iK4DedeEkct+fcx7yFEDL2J6z4Jeho5JDAw==", "dev": true, "license": "MIT", "dependencies": { @@ -1998,15 +1998,15 @@ } }, "node_modules/@next/env": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.9.tgz", - "integrity": "sha512-ki5VxxXfzD/9TDe13wyeTKIjQTAwBVpnr8KhRDUr8ltMUq1/NBpWNT5tiPoxiGl+PHM4X2ahSOiPk6iAimIzPg==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.10.tgz", + "integrity": "sha512-zLPxg9M0MEHmygpj5OuxjQ+vHMiy/K7cSp74G8ecYolmgUWw0RwN02tF56npup/+qaI8JB97hQgS/r2Hb6QwVA==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.9.tgz", - "integrity": "sha512-UZi8+YT/MLgTC9nrrn2Xd4lBYv1B7lVmtWHfPcthAI5Tt/C1LuDe6DfmtCtJ+WQod3ksY4VrKSvk3oMVAnL7qw==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.10.tgz", + "integrity": "sha512-Gs8D2m21VnJeFo9qvYIIqJH94frWerWYu41BprU1pLtRVF7PCQNLiFZZ3fG+iPuj3K83Cwv/rt+msLOy8Qgu3Q==", "dev": true, "license": "MIT", "dependencies": { @@ -2014,9 +2014,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.9.tgz", - "integrity": "sha512-HkfxNYUCmcct0Xsqib5KxqMSHV4AHJq857BNRchyBDs4YS19aHzVfn1kDuBYKqLLQBjXgnkIsjV2Kd4d2wzYhw==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.10.tgz", + "integrity": "sha512-v9IdJCa0H0mbo+8z5zwUpOk1Vj7RjkcI5uNYf5Ws1y6szf/p3Mzl9hLaST8SCt6L9h8NGnruZcd2+o0NTNwDhA==", "cpu": [ "arm64" ], @@ -2030,9 +2030,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.9.tgz", - "integrity": "sha512-7IAtK4MeybpqRV9GRABWEhJ62mOS+rzWOzOTFie4cSEtm12xsoOMJRcECoZx3FHPzFAqN/IJtHqWAFOLfl152w==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.10.tgz", + "integrity": "sha512-17IS0jJRViROGmA9uGdNR8VPJpfbnaVG7E9qhso5jDLkmyd0lSDORWxbcKINzcFqzZqGwGtMSnrFRxBpuUYjLQ==", "cpu": [ "x64" ], @@ -2046,9 +2046,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.9.tgz", - "integrity": "sha512-hBD75iWpUtkL9SmQmcRhmLomn9jgkPzCEkbOcLgHymPEKzv+6ONy13RRiIEz/iEObjkS2Jlb5gYS2XGoS3X4rw==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.10.tgz", + "integrity": "sha512-GRQRsRtuciNJvB54AvvuQTiq0oZtFwa1owQqtZD8wwnGpM2L39MV22kpI72YSXLKIyY40LC66EiLFv4PiicXxg==", "cpu": [ "arm64" ], @@ -2062,9 +2062,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.9.tgz", - "integrity": "sha512-qZTI3pf9SGc/obr8NkQAekBxmp1QK+kVm+VAf3BALLfFAj+1kUhkTxmrWpVos9R/UYIA8AWX2p6cGI5WdwzVUA==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.10.tgz", + "integrity": "sha512-zkN9MQYS7UQBro+FnISUq1itaQjXI9xqISzuQ+2bc921NcJ1x4yPCqrn77tVN6/dOOXaaWVX3k6/bR07pPwK+A==", "cpu": [ "arm64" ], @@ -2078,9 +2078,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.9.tgz", - "integrity": "sha512-xm0HfRNX+UkH4R3c18ynswjj5o5uEj/7iI9p9omdtTSIsRCzQqkGMA+10nzJ4EHnYC3as65IMhbbl5fWRUWHYg==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.10.tgz", + "integrity": "sha512-iCVJnwvrPYECvA6WM/7+oo+OiTvedIKLxtCLAZP4xZR3nXa1zmzZyLPbYCmWvpd4CvMYF1EMTafd0ii3DygLvA==", "cpu": [ "x64" ], @@ -2094,9 +2094,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.9.tgz", - "integrity": "sha512-QumimHkGEG6vM3PfEDWKyKen03NcqLOkeKB1EfcPe7VxzmEiCa4jNnMyBn/US5zcd/VE1CI+O8Ovb3lfjVHfGw==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.10.tgz", + "integrity": "sha512-ov2g4H0dHY9bPoOU83m91hWT7Iq5qy13bUnyyshLU3HGR1Ownn0X9QpmDPc5iIUaahTp7f7LeGAhV4DSFtackw==", "cpu": [ "x64" ], @@ -2110,9 +2110,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.9.tgz", - "integrity": "sha512-hzQpKZvw8rAwI6A2uQh6SacCSvNAXaIkPNsWwzqqfRiIMiXMfH936skDhz1OO6KpvdKkJrgHHtqQOq5PIXOvdQ==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.10.tgz", + "integrity": "sha512-DwAnhLX76HQiFFQNgWlcK+JzlnD1rZ+UK/WY0ZMI/deXpvgnesjNYrqcfo1JzBuz4Kf7o3brIBL0glI1junatA==", "cpu": [ "arm64" ], @@ -2126,9 +2126,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.9.tgz", - "integrity": "sha512-qr2VL3Ce5QrwgO2yh1ujSBawrimjVKX8FGF/cOynmdYKJY0BdHpGVNIRK1tqONB10Vkm25Ub1BD2bkjWs4+96w==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.10.tgz", + "integrity": "sha512-0JXq3b85Jk9Jg4ntLUbXSPvoDw3gpZou7twuKdoFG2jOw635v7+IiXfTaa0TxVMyx78pUjnrVYwLgjKfX4e6/A==", "cpu": [ "x64" ], @@ -7191,13 +7191,13 @@ } }, "node_modules/eslint-config-next": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.9.tgz", - "integrity": "sha512-olGtBrs07bQchpaJWeqbk9GaMoU0oGmN/pYNEBXSbfgKngb5uHnPe37X6tVeh6DJfaWFQildvinGEOrolo5fmw==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.10.tgz", + "integrity": "sha512-HSybLOY0QKf39i4FWUqPN0xWiNDi6A6UqJmZtgDkS3zMqjXTqULvj/sueXx3cdCG0mVG+qH6k5/qdegklH1d1w==", "dev": true, "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "16.2.9", + "@next/eslint-plugin-next": "16.2.10", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", @@ -10810,12 +10810,12 @@ "license": "MIT" }, "node_modules/next": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/next/-/next-16.2.9.tgz", - "integrity": "sha512-MEOJiq/UvuezAdqVSceHbqDgZt1kDw2tpGVOlsdIoJsQdbN2JY2hpVG4xnXGkbdJUOEWhnRfiu/O4Hpc9Juwww==", + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/next/-/next-16.2.10.tgz", + "integrity": "sha512-2som5AVXb3kE6Yjine3/mNbBayYF58eguBWIVVUdr1y/L426xyVEgYxgBG+1QC34P2x5E+tcDup6XkuOAX3dCA==", "license": "MIT", "dependencies": { - "@next/env": "16.2.9", + "@next/env": "16.2.10", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", @@ -10829,14 +10829,14 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.2.9", - "@next/swc-darwin-x64": "16.2.9", - "@next/swc-linux-arm64-gnu": "16.2.9", - "@next/swc-linux-arm64-musl": "16.2.9", - "@next/swc-linux-x64-gnu": "16.2.9", - "@next/swc-linux-x64-musl": "16.2.9", - "@next/swc-win32-arm64-msvc": "16.2.9", - "@next/swc-win32-x64-msvc": "16.2.9", + "@next/swc-darwin-arm64": "16.2.10", + "@next/swc-darwin-x64": "16.2.10", + "@next/swc-linux-arm64-gnu": "16.2.10", + "@next/swc-linux-arm64-musl": "16.2.10", + "@next/swc-linux-x64-gnu": "16.2.10", + "@next/swc-linux-x64-musl": "16.2.10", + "@next/swc-win32-arm64-msvc": "16.2.10", + "@next/swc-win32-x64-msvc": "16.2.10", "sharp": "^0.34.5" }, "peerDependencies": { @@ -10863,9 +10863,9 @@ } }, "node_modules/next/node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", "funding": [ { "type": "opencollective", @@ -10882,9 +10882,9 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" diff --git a/package.json b/package.json index 9ac1e1fc..7a7bdb72 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,10 @@ "minimatch": "9.0.9" }, "undici": "7.28.0", - "ws": "7.5.11" + "ws": "7.5.11", + "next": { + "postcss": "8.5.10" + } }, "scripts": { "dev": "next dev", @@ -136,6 +139,7 @@ "postprocess:export-robots-meta": "node scripts/postprocess/fix-export-robots-meta.js", "check:export-robots-meta": "node scripts/gates/check-export-robots-meta.js", "create:tool": "node scripts/scaffolding/create-tool.js", + "check:audit:prod": "npm audit --omit=dev --audit-level=moderate", "check:audit:prod-high": "npm audit --omit=dev --audit-level=high" }, "dependencies": { @@ -156,7 +160,7 @@ "jwt-decode": "^4.0.0", "lorem-ipsum": "^2.0.8", "lucide-react": "^0.575.0", - "next": "16.2.9", + "next": "16.2.10", "pdf-lib": "^1.17.1", "qrcode": "^1.5.4", "radix-ui": "^1.4.3", @@ -180,7 +184,7 @@ "yaml": "^2.8.2" }, "devDependencies": { - "@next/bundle-analyzer": "16.2.9", + "@next/bundle-analyzer": "16.2.10", "@tailwindcss/postcss": "^4", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", @@ -199,7 +203,7 @@ "@vitest/coverage-v8": "^4.1.8", "axe-core": "^4.11.1", "eslint": "^9", - "eslint-config-next": "16.2.9", + "eslint-config-next": "16.2.10", "jsdom": "^28.1.0", "playwright": "^1.58.2", "serve-handler": "^6.1.7", diff --git a/tests/guards/next-postcss-advisory-runbook.test.ts b/tests/guards/next-postcss-advisory-runbook.test.ts index 273bf125..be5706b1 100644 --- a/tests/guards/next-postcss-advisory-runbook.test.ts +++ b/tests/guards/next-postcss-advisory-runbook.test.ts @@ -6,20 +6,43 @@ function read(path: string) { } describe("issue #7 Next bundled PostCSS advisory runbook", () => { - it("keeps stable remediation evidence and closure criteria documented", () => { + it("keeps the stable remediation and regression checks documented", () => { const doc = read("docs/security/next-postcss-advisory-runbook.md") - const packageJson = read("package.json") + const ci = read(".github/workflows/ci.yml") + const packageJson = JSON.parse(read("package.json")) as { + dependencies: Record + devDependencies: Record + overrides: Record + scripts: Record + } + const packageLock = JSON.parse(read("package-lock.json")) as { + packages: Record + } expect(doc).toContain("issue #7") expect(doc).toContain("GHSA-qx2v-qp2m-jg93") expect(doc).toContain("postcss >= 8.5.10") - expect(doc).toContain("Checked on 2026-06-27") - expect(doc).toContain("16.2.9") + expect(doc).toContain("Checked on 2026-07-16") + expect(doc).toContain("16.2.10") expect(doc).toContain("8.4.31") + expect(doc).toContain("controlled npm override") + expect(doc).toContain("0 moderate/high/critical") + expect(doc).toContain("npm run check:audit:prod") expect(doc).toContain("npm run check:audit:prod-high") expect(doc).toContain("npm run test:e2e:pwa") expect(doc).toContain("Do not run `npm audit fix --force`") - expect(packageJson).toContain('"next": "16.2.9"') - expect(packageJson).toContain('"@next/bundle-analyzer": "16.2.9"') + expect(packageJson.dependencies.next).toBe("16.2.10") + expect(packageJson.devDependencies["@next/bundle-analyzer"]).toBe("16.2.10") + expect(packageJson.devDependencies["eslint-config-next"]).toBe("16.2.10") + expect(packageJson.overrides).toMatchObject({ + next: { postcss: "8.5.10" }, + }) + expect(packageJson.scripts["check:audit:prod"]).toBe( + "npm audit --omit=dev --audit-level=moderate", + ) + expect(ci).toMatch(/^\s*run: npm run check:audit:prod\s*$/m) + expect(packageLock.packages["node_modules/next/node_modules/postcss"]?.version).toBe( + "8.5.10", + ) }) }) diff --git a/tests/guards/supply-chain-audit-guard.test.ts b/tests/guards/supply-chain-audit-guard.test.ts index 3a058bf9..fca19a88 100644 --- a/tests/guards/supply-chain-audit-guard.test.ts +++ b/tests/guards/supply-chain-audit-guard.test.ts @@ -9,16 +9,25 @@ function read(relativePath: string) { } describe("supply chain audit guard", () => { - it("keeps a production high-severity npm audit command wired into repo workflows", () => { + it("keeps a production moderate-severity npm audit command wired into repo workflows", () => { const packageJson = JSON.parse(read("package.json")) as { scripts?: Record } const workflow = read(".github/workflows/ci.yml") const pullRequestTemplate = read(".github/pull_request_template.md") + const workflowLines = workflow.split(/\r?\n/).map((line) => line.trim()) + const installIndex = workflowLines.indexOf("run: npm ci") + const auditIndex = workflowLines.indexOf("run: npm run check:audit:prod") + const lintIndex = workflowLines.indexOf("run: npm run lint") + expect(packageJson.scripts?.["check:audit:prod"]).toBe( + "npm audit --omit=dev --audit-level=moderate", + ) expect(packageJson.scripts?.["check:audit:prod-high"]).toBe("npm audit --omit=dev --audit-level=high") expect(workflow).toContain("Run Production Dependency Audit") - expect(workflow).toContain("npm run check:audit:prod-high") - expect(workflow.indexOf("npm ci")).toBeLessThan(workflow.indexOf("npm run check:audit:prod-high")) - expect(workflow.indexOf("npm run check:audit:prod-high")).toBeLessThan(workflow.indexOf("npm run lint")) - expect(pullRequestTemplate).toContain("`npm run check:audit:prod-high`") + expect(installIndex).toBeGreaterThanOrEqual(0) + expect(auditIndex).toBeGreaterThanOrEqual(0) + expect(lintIndex).toBeGreaterThanOrEqual(0) + expect(installIndex).toBeLessThan(auditIndex) + expect(auditIndex).toBeLessThan(lintIndex) + expect(pullRequestTemplate).toContain("`npm run check:audit:prod`") }) })