From e54b78bcded8878114fe111a1e43b9424bec4d5b Mon Sep 17 00:00:00 2001 From: Arjun Chikara Date: Wed, 3 Jun 2026 16:48:30 +0530 Subject: [PATCH 1/3] chore(axe-core): harden .npmrc for supply-chain audit (AXE-3444) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The weekly Enigma supply-chain audit (SC-12282) flagged this repo: the committed .npmrc was missing the required hardening directives. Add ignore-scripts, strict-ssl, save-exact, audit-level, engine-strict and legacy-peer-deps. access=restricted is omitted intentionally — this is a public repository, so the restricted-access directive does not apply. ignore-scripts=true blocks dependency lifecycle scripts on install, including esbuild's postinstall that downloads its native bundler binary. Rebuild esbuild explicitly in the dependencies_unix job (the only CI job that runs `npm ci`) so the cached node_modules carries the binary to build_unix. chromedriver is unaffected — CI fetches it via browser-driver-manager, not the npm install script. patch-package still applies because build_unix runs `npm run prepare` explicitly, and ignore-scripts only suppresses pre/post hooks around an explicit `npm run`, not the invoked script itself. Document the local-dev post-install steps in the developer guide. Co-Authored-By: Claude Opus 4.8 (1M context) --- .circleci/config.yml | 4 ++++ .npmrc | 11 ++++++++++- doc/developer-guide.md | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 63acb7fc2..3773d22a2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -55,6 +55,10 @@ jobs: - browser-tools-job - <<: *set_npm_auth - run: npm ci + # .npmrc sets ignore-scripts=true (supply-chain hardening, AXE-3444), which + # skips esbuild's postinstall that fetches its native bundler binary. Rebuild + # it explicitly so the cached node_modules carries the binary to build_unix. + - run: npm rebuild esbuild --ignore-scripts=false --foreground-scripts - run: npx browser-driver-manager install chromedriver --verbose - save_cache: key: v9-cache-unix-{{ checksum "package-lock.json" }} diff --git a/.npmrc b/.npmrc index 0453efcd4..3fb5d3a80 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,10 @@ -registry=https://registry.npmjs.org \ No newline at end of file +registry=https://registry.npmjs.org + +# Supply-chain hardening directives (AXE-3444 / SC-12282) — linted weekly by Enigma. +# Do NOT add tokens or secrets here; this repo installs from the public registry. +ignore-scripts=true +strict-ssl=true +save-exact=true +audit-level=high +engine-strict=true +legacy-peer-deps=false diff --git a/doc/developer-guide.md b/doc/developer-guide.md index 0c6b12d3d..c884c2d38 100644 --- a/doc/developer-guide.md +++ b/doc/developer-guide.md @@ -34,6 +34,7 @@ Axe 3.0 supports open Shadow DOM: see our virtual DOM APIs and test utilities fo 1. You must have Node.js version 22 or higher installed. If you have [nvm](https://github.com/nvm-sh/nvm) installed, simply do `nvm use` in the root of this repository. 1. Install npm development dependencies. In the root folder of your axe-core repository, run `npm install` +1. The committed `.npmrc` sets `ignore-scripts=true` for supply-chain hardening, so dependency lifecycle scripts do not run during install. After installing, run `npm run prepare` (applies dependency patches and installs git hooks) and `npm rebuild esbuild --ignore-scripts=false` (fetches the esbuild bundler binary) before building. ### Building axe.js From 4d3248838bae161c44768067f81efdf1cd08f033 Mon Sep 17 00:00:00 2001 From: Arjun Chikara Date: Wed, 3 Jun 2026 18:52:45 +0530 Subject: [PATCH 2/3] fix(ci): rebuild esbuild after ignore-scripts installs (AXE-3444) The .npmrc supply-chain hardening sets ignore-scripts=true, which skips esbuild's postinstall that fetches its native bundler binary. Every path that installs deps and then builds (grunt -> esbuild) therefore broke: - GitHub Actions `build` job (test.yml) failed with "esbuild: Failed to install correctly" at the esbuild:core grunt task. - The standard-version `postbump` hook re-runs `npm ci` (wiping the binary) before `sri-update` -> `grunt build`, breaking the release / next_release pipelines (CircleCI release jobs and GitHub release.yml). - The nightly `npm install w3c/...` steps re-resolve the whole dependency tree under ignore-scripts, skipping native postinstalls. Fixes, keeping all six audit-required directives intact (access=restricted is omitted because this is a public repo): - test.yml: `npm rebuild esbuild --ignore-scripts=false` between ci and build - package.json postbump: rebuild esbuild after its inner `npm ci` - nightly jobs: `--ignore-scripts=false` on the trusted w3c installs - .npmrc: drop internal tool/ticket reference from the comment Verified: under ignore-scripts=true esbuild installs broken, and `npm rebuild esbuild --ignore-scripts=false` runs install.js and restores the working native binary. Co-Authored-By: Claude Opus 4.8 (1M context) --- .circleci/config.yml | 12 ++++++++---- .github/workflows/test.yml | 4 ++++ .npmrc | 3 ++- package.json | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3773d22a2..37fe1c711 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -206,8 +206,10 @@ jobs: - browser-tools-job # install ACT rules # install first as for some reason installing a single package - # also re-installs all repo dependencies as well - - run: npm install w3c/wcag-act-rules#main + # also re-installs all repo dependencies as well. .npmrc sets + # ignore-scripts=true (AXE-3444); the full re-install would otherwise skip + # native postinstalls (e.g. esbuild), so allow scripts for this trusted install. + - run: npm install w3c/wcag-act-rules#main --ignore-scripts=false - run: npx browser-driver-manager install chromedriver --verbose - <<: *restore_build - run: npm run test:act @@ -222,8 +224,10 @@ jobs: - browser-tools-job # install ARIA practices # install first as for some reason installing a single package - # also re-installs all repo dependencies as well - - run: npm install w3c/aria-practices#main + # also re-installs all repo dependencies as well. .npmrc sets + # ignore-scripts=true (AXE-3444); the full re-install would otherwise skip + # native postinstalls (e.g. esbuild), so allow scripts for this trusted install. + - run: npm install w3c/aria-practices#main --ignore-scripts=false - run: npx browser-driver-manager install chromedriver --verbose - <<: *restore_build - run: npm run test:apg diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fb7ba06ac..a4d8c1120 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,6 +18,10 @@ jobs: node-version-file: .nvmrc cache: 'npm' - run: npm ci + # .npmrc sets ignore-scripts=true (supply-chain hardening, AXE-3444), which + # skips esbuild's postinstall that fetches its native bundler binary. Rebuild + # it explicitly so `npm run build` (grunt -> esbuild) can run. + - run: npm rebuild esbuild --ignore-scripts=false --foreground-scripts - run: npm run prepare - run: npm run build - uses: actions/upload-artifact@v4 diff --git a/.npmrc b/.npmrc index 3fb5d3a80..829ddb75b 100644 --- a/.npmrc +++ b/.npmrc @@ -1,6 +1,7 @@ registry=https://registry.npmjs.org -# Supply-chain hardening directives (AXE-3444 / SC-12282) — linted weekly by Enigma. +# Supply-chain hardening directives (AXE-3444). Public repo: access is left +# unset (access=restricted applies to private repos only). # Do NOT add tokens or secrets here; this repo installs from the public registry. ignore-scripts=true strict-ssl=true diff --git a/package.json b/package.json index 6a64f5176..7bf234ef8 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ ], "standard-version": { "scripts": { - "postbump": "npm ci && npm run sri-update && git add doc/rule-descriptions.md" + "postbump": "npm ci && npm rebuild esbuild --ignore-scripts=false --foreground-scripts && npm run sri-update && git add doc/rule-descriptions.md" }, "skip": { "tag": true From 016a571495f949729c4181bea93d020fa49b0a8b Mon Sep 17 00:00:00 2001 From: Arjun Chikara Date: Fri, 5 Jun 2026 20:08:06 +0530 Subject: [PATCH 3/3] fix(ci): use targeted esbuild rebuild for nightly installs (AXE-3444) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nightly jobs used `npm install w3c/...#main --ignore-scripts=false`, which re-enabled lifecycle scripts for the whole re-resolved dependency tree and the untrusted w3c/*#main installs — defeating the ignore-scripts=true hardening. Those jobs only consume the prebuilt artifact (test:act/test:apg are mocha), so esbuild isn't needed there; drop the flag. Also remove the verbose AXE-3444 comment blocks from the CI configs per review feedback. The targeted `npm rebuild esbuild --ignore-scripts=false` steps (single trusted package) stay, matching the tech spec's sanctioned approach. Co-Authored-By: Claude Opus 4.8 (1M context) --- .circleci/config.yml | 15 ++++----------- .github/workflows/test.yml | 3 --- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 37fe1c711..fce6e4a44 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -55,9 +55,6 @@ jobs: - browser-tools-job - <<: *set_npm_auth - run: npm ci - # .npmrc sets ignore-scripts=true (supply-chain hardening, AXE-3444), which - # skips esbuild's postinstall that fetches its native bundler binary. Rebuild - # it explicitly so the cached node_modules carries the binary to build_unix. - run: npm rebuild esbuild --ignore-scripts=false --foreground-scripts - run: npx browser-driver-manager install chromedriver --verbose - save_cache: @@ -206,10 +203,8 @@ jobs: - browser-tools-job # install ACT rules # install first as for some reason installing a single package - # also re-installs all repo dependencies as well. .npmrc sets - # ignore-scripts=true (AXE-3444); the full re-install would otherwise skip - # native postinstalls (e.g. esbuild), so allow scripts for this trusted install. - - run: npm install w3c/wcag-act-rules#main --ignore-scripts=false + # also re-installs all repo dependencies as well + - run: npm install w3c/wcag-act-rules#main - run: npx browser-driver-manager install chromedriver --verbose - <<: *restore_build - run: npm run test:act @@ -224,10 +219,8 @@ jobs: - browser-tools-job # install ARIA practices # install first as for some reason installing a single package - # also re-installs all repo dependencies as well. .npmrc sets - # ignore-scripts=true (AXE-3444); the full re-install would otherwise skip - # native postinstalls (e.g. esbuild), so allow scripts for this trusted install. - - run: npm install w3c/aria-practices#main --ignore-scripts=false + # also re-installs all repo dependencies as well + - run: npm install w3c/aria-practices#main - run: npx browser-driver-manager install chromedriver --verbose - <<: *restore_build - run: npm run test:apg diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a4d8c1120..bf8d43fcd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,9 +18,6 @@ jobs: node-version-file: .nvmrc cache: 'npm' - run: npm ci - # .npmrc sets ignore-scripts=true (supply-chain hardening, AXE-3444), which - # skips esbuild's postinstall that fetches its native bundler binary. Rebuild - # it explicitly so `npm run build` (grunt -> esbuild) can run. - run: npm rebuild esbuild --ignore-scripts=false --foreground-scripts - run: npm run prepare - run: npm run build