Skip to content

chore: .npmrc supply-chain hardening + esbuild 0.19 upgrade#238

Merged
Abhi3685 merged 6 commits into
mainfrom
AXE-3444-npmrc-hardening-esbuild-0.19
Jun 12, 2026
Merged

chore: .npmrc supply-chain hardening + esbuild 0.19 upgrade#238
Abhi3685 merged 6 commits into
mainfrom
AXE-3444-npmrc-hardening-esbuild-0.19

Conversation

@rajathmr2000

@rajathmr2000 rajathmr2000 commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

chikara1608 and others added 5 commits June 3, 2026 16:48
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
The AXE-3444 .npmrc hardening sets ignore-scripts=true, which suppressed
esbuild 0.10.2's postinstall that downloads its native binary, breaking
`npm run build` ("esbuild: Failed to install correctly"). esbuild 0.16+
ships its binary as @esbuild/* optionalDependencies with no install
script, so ignore-scripts=true no longer breaks the build and no
`npm rebuild esbuild` workaround is needed.

[a11y-critical]: build tooling for axe.js bundle; affects all packages.

Build verified on Node 22; axe.run smoke under jsdom passes (memoize/#1433
ordering OK, image-alt + link-name detected). Full Karma suite and
real-browser color-contrast validation still pending.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@rajathmr2000 rajathmr2000 changed the title chore(axe-core): .npmrc supply-chain hardening + esbuild 0.19 upgrade (AXE-3444) chore(axe-core): .npmrc supply-chain hardening + esbuild 0.19 upgrade Jun 12, 2026
@rajathmr2000 rajathmr2000 changed the title chore(axe-core): .npmrc supply-chain hardening + esbuild 0.19 upgrade chore: .npmrc supply-chain hardening + esbuild 0.19 upgrade Jun 12, 2026

@rajathmr2000 rajathmr2000 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review (automated) — 3 inline finding(s). Full report in the PR comment below. Verdict: Failed - see PR comment.

Comment thread package.json
Comment thread .npmrc
Comment thread doc/developer-guide.md
@rajathmr2000

Copy link
Copy Markdown
Collaborator Author

Claude Code PR Review

PR: #238Head: bc587baReviewers: stack:code-review

Summary

Hardens the committed .npmrc with supply-chain directives (ignore-scripts=true, save-exact=true, engine-strict=true, etc.), upgrades esbuild 0.10.2 → 0.19.12 (exact-pinned), and adds npm rebuild esbuild --ignore-scripts=false follow-ups to the patched CI install paths (GH test.yml build job, CircleCI dependencies job) plus the standard-version postbump chain; documents the new local-dev steps in the developer guide.

Review Table

Priority Category Check Status Notes
High Security No hardcoded secrets or credentials Pass .npmrc carries no tokens and explicitly documents that none may be added
High Security Authentication/authorization checks present N/A Config/CI/docs-only change; no routes or sockets
High Security Input validation and sanitization N/A No runtime code touched
High Security No IDOR — resource ownership validated N/A
High Security No SQL injection (parameterized queries) N/A
High Correctness Logic is correct, handles edge cases Fail ignore-scripts=true suppresses the repo's own prepare script (husky && npx patch-package); patches/colorjs.io+0.4.3.patch patches a library bundled into shipped axe.js. The release postbump chain, update-generated-files.yaml, and the umbrella repo's build_axe.sh all lost the patch silently
High Correctness Error handling is explicit, no swallowed exceptions N/A No runtime code; the failure mode here is precisely that nothing errors — builds succeed with unpatched colorjs.io
High Correctness No race conditions or concurrency issues N/A
Medium Testing New code has corresponding tests N/A CI/config change; validated by CI itself
Medium Testing Error paths and edge cases tested Fail No release dry-run evidence (standard-version --dry-run / next-release) proving the postbump chain produces a correctly patched artifact under ignore-scripts=true
Medium Testing Existing tests still pass (no regressions) Pass All 12 checks green on head bc587ba (build, fmt_check, test_node 6/18/20/22, CodeQL, semgrep, SCA)
Medium Performance No N+1 queries or unbounded data fetching N/A
Medium Performance Long-running tasks use background jobs N/A
Medium Quality Follows existing codebase patterns Pass esbuild 0.19 invocation verified compatible (build/tasks/esbuild.js uses only API-stable options); lockfile v1+v2 sections updated consistently; exact pin matches save-exact=true
Medium Quality Changes are focused (single concern) Pass One concern: install-time script hardening + the esbuild bump it forces
Low Quality Meaningful names, no dead code Pass
Low Quality Comments explain why, not what Pass .npmrc comments explain rationale (public-repo access caveat, no-tokens rule)
Low Quality No unnecessary dependencies added Pass New @esbuild/* lockfile entries are esbuild 0.19's own platform optionalDependencies

Findings

  • File: package.json:68

  • Severity: Critical

  • Reviewer: stack:code-review

  • Issue: The standard-version postbump runs npm ci && npm rebuild esbuild … && npm run sri-update, but with ignore-scripts=true the npm ci no longer triggers the repo's prepare script (husky && npx patch-package). patches/colorjs.io+0.4.3.patch patches colorjs.io, which is bundled into axe.js (lib/core/imports/index.js:8) — so the release-built artifact silently bundles unpatched colorjs.io.

  • Suggestion: Insert npm run prepare (or at minimum npm run patch) after npm ci in postbump: npm ci && npm run prepare && npm rebuild esbuild --ignore-scripts=false --foreground-scripts && npm run sri-update && git add doc/rule-descriptions.md.

  • File: .github/workflows/update-generated-files.yaml:26

  • Severity: High

  • Reviewer: stack:code-review

  • Issue: This workflow runs npm ci + npm run build and auto-commits generated files, but was not given the npm rebuild esbuild + npm run prepare follow-ups added to test.yml — same unpatched-colorjs.io problem, with the output committed back to the repo. (File not in this PR's diff.)

  • Suggestion: Add the same two steps after npm ci as in test.yml:21-22.

  • File: a11y-engine-core/build/scripts/build_axe.sh (umbrella repo — cross-repo)

  • Severity: High

  • Reviewer: stack:code-review

  • Issue: The umbrella repo's engine build does npm install + npm run build inside the axe-core/ submodule with no prepare/rebuild step. Once this .npmrc lands in the submodule, every a11y-engine-core build bundles unpatched colorjs.io (loses the CSS?.supports guard affecting color-contrast paths). Cannot be fixed in this PR.

  • Suggestion: Sequence a coordinated AXE-3443/AXE-3444 change to build_axe.sh (add npm run prepare && npm rebuild esbuild --ignore-scripts=false) before bumping the submodule pointer, and state the dependency explicitly in the PR description.

  • File: .github/workflows/release.yml:26

  • Severity: Medium

  • Reviewer: stack:code-review

  • Issue: The release job's correctness depends entirely on the postbump chain in package.json for prepare/rebuild. (File not in this PR's diff.)

  • Suggestion: After fixing postbump, also add an explicit npm run prepare to the release workflow so artifact correctness doesn't hinge on standard-version config.

  • File: .npmrc:10

  • Severity: Medium

  • Reviewer: stack:code-review

  • Issue: engine-strict=true enforces every dependency's engines range at install time, turning previous warnings into hard install failures. CI pins Node 22 via .nvmrc, but the umbrella repo's default toolchain is Node 18.20.4 — a dev running npm install inside axe-core/ on Node 18 may now hard-fail, and build_axe.sh's nvm use becomes load-bearing.

  • Suggestion: Confirm this is deliberate (the developer guide already mandates Node 22+); if so, note it in the umbrella repo's setup docs.

  • File: .circleci/config.yml:207,223

  • Severity: Medium

  • Reviewer: stack:code-review

  • Issue: npm install w3c/wcag-act-rules#main / w3c/aria-practices#main are git dependencies whose own prepare scripts are skipped under ignore-scripts=true. Both look content-only today, but the test:act/test:apg jobs were given no follow-up step. (Lines not in this PR's diff.)

  • Suggestion: Confirm both CircleCI jobs pass on this branch; add a targeted --ignore-scripts=false to those installs if they break.

  • File: doc/developer-guide.md:37

  • Severity: Low

  • Reviewer: stack:code-review

  • Issue: "fetches the esbuild bundler binary" is slightly inaccurate — with esbuild ≥0.16 the platform binary arrives as an @esbuild/* optionalDependency during npm ci; the rebuild validates/links it. The documented command also omits --foreground-scripts used everywhere else.

  • Suggestion: Reword to "validates and links the esbuild platform binary" and include the full flag set for consistency.

Positive notes from the reviewer: the esbuild 0.10 → 0.19 jump itself is safe (the sole invocation in build/tasks/esbuild.js uses only options stable across the 0.17 breaking changes); the CircleCI rebuild line is correctly placed before save_cache so downstream jobs inherit the rebuilt binary; CircleCI's build job already runs npm run prepare explicitly, so the patch-package gap does not apply there; submodule // [tag]: rule treated as satisfied (no JS source changed, no sensible anchor for inline tags, no sensitive info in .npmrc comments, no Deque packages).

Reviewer questions for the PR thread: (1) Has a full npm ci && npm run prepare && npm run build && npm test been run from a clean clone on this branch? (2) Was a release dry-run executed to prove the postbump chain end-to-end under ignore-scripts? (3) Is the umbrella build_axe.sh change merged/sequenced before the submodule pointer bump?


Verdict: FAIL — the release postbump chain and two other install→build paths silently lose the bundled colorjs.io patch under ignore-scripts=true; fix postbump and update-generated-files.yaml in this PR and sequence the umbrella build_axe.sh change before the pointer bump.

@Abhi3685 Abhi3685 merged commit da29556 into main Jun 12, 2026
12 checks passed
@Abhi3685 Abhi3685 deleted the AXE-3444-npmrc-hardening-esbuild-0.19 branch June 12, 2026 14:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants