diff --git a/.github/workflows/repository-integrity.yml b/.github/workflows/repository-integrity.yml index dd834e1..8f2aacd 100644 --- a/.github/workflows/repository-integrity.yml +++ b/.github/workflows/repository-integrity.yml @@ -13,3 +13,32 @@ permissions: jobs: integrity: uses: PastureStack/.github/.github/workflows/repository-integrity.yml@0830b344f020bcb7792a112c376bbfac48e63fa5 # pinned main + + build-test-audit: + name: Build, test, and dependency audit + runs-on: ubuntu-24.04 + timeout-minutes: 30 + steps: + - name: Check out candidate + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Install Node.js + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: '24.20.0' + package-manager-cache: false + + - name: Use the locked npm release + shell: bash + run: npm install --global npm@12.0.2 --ignore-scripts --no-audit --no-fund + + - name: Build, audit, and verify reproducibility + shell: bash + run: | + set -euo pipefail + test "$(node --version)" = 'v24.20.0' + test "$(npm --version)" = '12.0.2' + npm ci --no-audit --no-fund + npm test diff --git a/package-lock.json b/package-lock.json index b303691..17e7e22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55,9 +55,9 @@ } }, "node_modules/@xmldom/xmldom": { - "version": "0.9.10", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.10.tgz", - "integrity": "sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==", + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.12.tgz", + "integrity": "sha512-5AXjrcMClTryPe9LgZrygpB1lj7s0S9E0+W+AHaVKAVyHanafK86iPSvG5xHVSp/jC+VH1UXu0TAEmY279xH7A==", "dev": true, "license": "MIT", "engines": { diff --git a/scripts/audit.mjs b/scripts/audit.mjs index db99086..259c3fb 100644 --- a/scripts/audit.mjs +++ b/scripts/audit.mjs @@ -10,7 +10,7 @@ const personalDenylist = [ Buffer.from("6368656e3231303139", "hex").toString("utf8"), Buffer.from("6368656e323130313940676d61696c2e636f6d", "hex").toString("utf8") ]; -const expectedRootLicenseHash = "1d5afc26765f4da03ed7605f2944198b985dac1ddac0ec0b5ace57fe06b94330"; +const expectedRootLicenseHash = "809fa1ed21450f59827d1e9aec720bbc4b687434fa22283c6cb5dd82a47ab9c0"; const expectedLucideLicenseHash = "b495047bd93a9b06913511076f504daba17d5bbeb3e0650f3bb53a4220329c57"; const expectedLucideTtfHash = "2ff7709e2f12f6ce07b2df9d3bad4120b622bfdb12c5c8eeaf5a713cf5bba233"; @@ -18,6 +18,10 @@ function sha256(value) { return createHash("sha256").update(value).digest("hex"); } +function normalizeTextLineEndings(value) { + return Buffer.from(value.toString("utf8").replaceAll("\r\n", "\n"), "utf8"); +} + async function filesUnder(directory, relative = "") { const files = []; for (const entry of await readdir(directory, { withFileTypes: true })) { @@ -69,7 +73,7 @@ for (const disallowed of ["selection.json", "compatibility-baseline.json"]) { } } -if (sha256(await readFile(path.join(root, "LICENSE"))) !== expectedRootLicenseHash) { +if (sha256(normalizeTextLineEndings(await readFile(path.join(root, "LICENSE")))) !== expectedRootLicenseHash) { throw new Error("Root LICENSE changed unexpectedly"); } const lucideLicense = await readFile(path.join(root, "node_modules", "lucide-static", "LICENSE"));