Skip to content

Add CI for pull requests, clear the sprintf-js Dependabot alert, fix coverage runs - #57

Merged
jameskfry merged 3 commits into
mainfrom
ci/pr-checks-and-dependabot
Oct 8, 2026
Merged

jameskfry merged 3 commits into
mainfrom
ci/pr-checks-and-dependabot

Conversation

@jameskfry

Copy link
Copy Markdown
Owner

Summary

Three small, separate commits:

  1. Fix jest --coverage, which fails on main today (4 tests).
  2. Resolve the open Dependabot alert (sprintf-js, #89) by moving js-yaml to 4.x.
  3. Add CI for pull requests. Until now tests only ran when a release was published, so #56 merged without a single check.

1. Coverage runs were broken (already on main)

toRegExp() and resolveAllowedConstant() are serialised into the code compile() generates with Function#toString(). When Istanbul instruments them, the serialised text calls coverage counters (cov_xxx()) that don't exist where the compiled code later runs, so npm test -- --coverage failed 4 tests with ReferenceError: cov_... is not defined. Shipped code is unaffected (nothing instruments lib/). An istanbul ignore next hint on the two functions fixes it; coverage is now 96% statements / 91% branches.

2. Dependabot

npm audit reports "34 vulnerabilities", but that is 2 advisories plus the 32 packages that depend on them. Both are in development tooling only; npm audit --omit=dev is clean.

Advisory Reached through Patched version? What I did
sprintf-js <= 1.1.3, DoS via unbounded precision (#89, the only open alert) jest > ... > babel-plugin-istanbul > @istanbuljs/load-nyc-config > js-yaml 3.x > argparse 1.x None (1.1.3 is the latest) Removed it from the tree (below)
braces <= 3.0.3, stack exhaustion (GHSA-vfj7-8cjw-p6xm; alert #88) jest > @jest/core > micromatch, and @babel/cli > chokidar None (3.0.3 is the latest) Nothing possible; GitHub auto-dismissed alert #88 itself

The fix for sprintf-js: overrides: { "js-yaml": "^4.1.0" }. js-yaml 4 uses argparse 2, which no longer depends on sprintf-js. The only consumer, @istanbuljs/load-nyc-config, calls require('js-yaml').load(...) on a .nycrc.yaml, which means the same thing in v4. The lockfile loses sprintf-js and esprima and changes only js-yaml and argparse. Verified: tests, a coverage run, loading a real .nycrc.yaml through load-nyc-config, both builds. Dependabot should close #89 on its own after merge (the vulnerable package leaves the lockfile).

Why braces can't be removed: it is jest's own glob engine (micromatch). Overriding Babel's chokidar would not help, and @babel/cli 8 means a full Babel 8 migration for no gain. It will clear when a patched braces is published. npm audit will keep listing it until then, which is why the CI audit job is scoped to runtime dependencies.

3. CI (.github/workflows/ci.yml)

Runs on pull requests, pushes to main, and manually.

Job Why it exists
Tests on Node 20 / 22 / 24 the basics
Tests with coverage catches code that breaks when instrumented (see 1)
Build + smoke test of lib/ and the minified bundle several features serialise functions into compiled code; that only works while the build keeps them self-contained (scripts/smoke-build.cjs, 13 checks per bundle)
TypeScript definitions (tsc --strict) typescript is now a devDependency
PHP dump vs Symfony's own loader pinned to the Symfony commit the fixtures were generated from, so upstream can't turn CI red by itself; uses the runner's PHP, no third-party action
npm audit --omit=dev --audit-level=high runtime dependencies only (currently 0)
CI (aggregate) the one check to require in branch protection

New scripts: test:coverage, test:types, test:build (documented in the README's Contributing section).

Not verified locally

I ran every step on Node 22 / PHP 8.2 and from a clean npm ci, but not on the runner. This PR's own run is the first real test of Node 20 and 24 and of the runner's PHP. If one of those fails for an environmental reason I'll fix it in a follow-up commit here.

After merging, consider requiring the CI check in the branch protection rules for main (a repo setting, so I haven't touched it).

🤖 Generated with Claude Code

jameskfry and others added 3 commits October 8, 2026 12:37
toRegExp() and resolveAllowedConstant() are embedded in the code compile()
generates through Function#toString(). When Istanbul instruments them, the text
holds calls to coverage counters (`cov_xxx()`) that do not exist where the
compiled code later runs, so four tests failed under `jest --coverage`
(already the case on main). Shipped code is not affected: nothing instruments lib/.

`istanbul ignore next` leaves the two functions as they are written.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Dependabot alert #89 (GHSA-hp3w-g68c-fv3c, DoS through unbounded precision in
sprintf-js <= 1.1.3) has no patched release: 1.1.3 is the latest. The package is
only reached by the test tooling, through
jest > @jest/core > @jest/transform > babel-plugin-istanbul >
@istanbuljs/load-nyc-config > js-yaml 3.x > argparse 1.x > sprintf-js,
and js-yaml 4 (argparse 2) no longer depends on it.

load-nyc-config only calls `require('js-yaml').load(...)` to read a
.nycrc.yaml, which has the same meaning in js-yaml 4. Checked: the tests, a
coverage run, loading a .nycrc.yaml through load-nyc-config, and both builds.

The lockfile loses sprintf-js and esprima and changes only js-yaml and
argparse. Nothing that ships is affected (`npm audit --omit=dev` was clean).

Not addressed: braces <= 3.0.3 (GHSA-vfj7-8cjw-p6xm), which is also unpatched
and comes from jest's own micromatch. GitHub auto-dismissed that alert (#88).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Until now the tests only ran when a release was published, so a pull request
had no checks at all. The CI workflow runs on pull requests and on pushes to
main:

- Tests on Node 20, 22 and 24.
- Tests with coverage: instrumented sources have to work too (this is what
  caught the functions compile() serialises with Function#toString()).
- Build and smoke test of what is published, lib/ (Babel) and the minified
  browser bundle (scripts/smoke-build.cjs): the serialised functions only work
  while the build leaves them self-contained.
- TypeScript definitions (`tsc --strict`; typescript is now a devDependency).
- The PHP generated by dumpCompiled() evaluated by Symfony's own
  CompiledExpressionLanguage, against a Symfony commit pinned in the
  workflow, with the PHP of the runner.
- `npm audit --omit=dev --audit-level=high`: the runtime dependencies only,
  as the development tools have advisories that no release fixes yet.
- A final `CI` job aggregating the others, to require in branch protection.

New npm scripts: test:coverage, test:types, test:build. The README's
Contributing section lists them.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@jameskfry
jameskfry merged commit a8edd6d into main Oct 8, 2026
9 checks passed
@jameskfry
jameskfry deleted the ci/pr-checks-and-dependabot branch October 8, 2026 19:03
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.

1 participant