Skip to content
This repository was archived by the owner on Jul 27, 2026. It is now read-only.

chore: Fix 33 vulnerabilities, replace dead gulp build, restore tests and CI - #1

Open
fuzzie360 wants to merge 7 commits into
masterfrom
chore/modernize-and-security
Open

chore: Fix 33 vulnerabilities, replace dead gulp build, restore tests and CI#1
fuzzie360 wants to merge 7 commits into
masterfrom
chore/modernize-and-security

Conversation

@fuzzie360

Copy link
Copy Markdown
Member

Summary

Security remediation and modernization for a repo whose last commit was 2016-12-10. The build system was not merely stale but unrunnable, npm test failed on a clean checkout, and the resolved dependency tree carried 33 vulnerabilities that Dependabot could not see.

This does not attempt to finish the transpiler — ast_generic's commented-out node handlers are the actual unfinished work, and completing them is a feature project, not modernization. The prototype status is now documented in the README instead.

Security

Dependabot reported 0 alerts, and that was a false negative. Alerts are enabled (the API returns 200 [], and vulnerability-alerts returns 204; automated fixes are off), but the repo had no lockfile — so the dependency graph could only see the four direct ranges in package.json, and every vulnerable package was transitive and therefore invisible.

Auditing the resolved tree locally showed the real picture:

before after
npm audit 33 (5 critical / 24 high / 2 moderate / 2 low) 0
packages 578 101
npm ci + build gulp build impossible; tsc emitted 9 errors clean, exit 0
npm test 6 passing (only after a manual tsc) 10 passing
  • ~27 eliminated by removing the gulp stack — gulp 3 / gulp-* / browser-sync / merge2 carried nearly all of it (an axios chain with ~23 advisories, growl command injection, immutable, debug).
  • 6 required overrides, because mocha 11.7.6 is already the newest release and still resolves vulnerable transitives:
    • brace-expansion^5.0.8 (GHSA-mh99-v99m-4gvg, high). The 2.x line mocha reaches via minimatch has no patched release, so the major jump is the only fix. This also cleared the derived minimatch and glob alerts.
    • serialize-javascript^7.0.7 (GHSA-5c6j-r48x-rmvq, high RCE + GHSA-qj8w-gfj5-8c6v, moderate); mocha pins ^6.0.2.
    • diff^8.0.4 (GHSA-73rr-hh4g-fpgx, low); mocha pins ^7.0.0.

All three cross a major boundary for mocha, so the affected code paths were exercised directly rather than assumed: the reporter still renders assertion diffs (diff), and mocha --parallel still serializes across workers (serialize-javascript). These pins are worth revisiting if mocha later widens its own ranges rather than being left indefinitely.

Bugs fixed

Each is pinned by a test confirmed to fail against the unfixed source.

  1. parse() read and wrote astConfig.retArr, but generateAstConfig() creates resArr. Result: resArr was always left empty and a stray retArr was added.
  2. ast_errorOutput() built its context from ast.src, but the source lives on astConfig. Since esprima runs with range: true, every node has a range, so this branch always fired and always threw Cannot read properties of undefined (reading 'slice') — a TypeError that replaced the descriptive error the transpiler was trying to raise. The slice was independently wrong too, passing 25 as an absolute end offset rather than a length.
  3. transpiler_esprima read esprima off a bare global, so requiring the package died with ReferenceError: esprima is not defined despite esprima being a declared dependency. Added resolveEsprima(); a global still takes precedence, so the browser path is unchanged.

Modernization

  • Removed the gulp 3 pipeline. It was unrunnable on modern Node — ReferenceError: primordials is not defined — and its tsbuild task could never have worked anyway, since it used --out against export default code, which tsc rejects. Replaced with a plain tsc invocation.
  • Upgraded esprima 3→4, TypeScript 2→5, mocha 3→11, chai 3→4. chai is deliberately held at 4.x: chai 5 is ESM-only and this harness is CommonJS.
  • Fixed npm test on a clean checkout. It previously failed with Cannot find module '../bin/transpiler_base.js'bin/ is gitignored and no script built it. Added build / prepare / pretest.
  • Fixed main, which pointed at dist/gpujs-transpile.js, a bundle nothing could produce. Now bin/transpiler_esprima.js, with declarations emitted.
  • Modernized tsconfig: dropped the obsolete atom-typescript filesGlob / compileOnSave keys and noResolve, added include and "types": [].
  • Added CI — Node 20/22/24 matrix, plus a separate npm audit --audit-level=high job so a newly published advisory turning CI red is not mistaken for a test regression. Committed the lockfile so Dependabot can finally see the tree.
  • Replaced gulp copy with npm run copy:lib, preserving the browser test page and dropping its reference to requirejs, which was never actually a dependency.

Testing

From a fresh clone of this branch: tsc exits 0, npm test → 10 passing, npm audit → 0 vulnerabilities.

Pre-existing failures, distinguished from new work:

  • npm test was already broken on a clean checkout before these changes — it only passed if you manually ran tsc first.
  • tsc under TypeScript 2.9 already emitted 9 errors from auto-included @types/node and @types/ws (TS1084, TS1005, TS1011). It still emitted JS because noEmitOnError was unset, so the breakage was silent. Fixed by "types": [].
  • gulp was already 100% dead on modern Node.

No new failures were introduced. The 4 new tests were each confirmed to fail against the unfixed source with the exact expected symptom.

Deliberately out of scope: no transpiler rewrite, no strict TypeScript (the code is implicit-any throughout; enabling it would be a large diff with no security or compatibility benefit), no chai 5 / ESM migration, no test-runner swap.

🤖 Generated with Claude Code

fuzzie360 and others added 7 commits July 27, 2026 10:37
The gulp 3 toolchain has not been runnable for years. On Node 22 it dies
immediately with "ReferenceError: primordials is not defined" (gulp 3 ->
vinyl-fs -> graceful-fs 1.x -> natives), so neither `gulp tsbuild` nor
`gulp copy` could be executed at all. The gulp stack was also the source
of nearly all of the project's vulnerable transitive dependencies.

Replace it with a direct `tsc` invocation and npm scripts:

- Drop gulp, gulp-*, browser-sync, merge2 and the stray babel plugin.
- Upgrade the remaining toolchain to current releases: esprima 3 -> 4,
  TypeScript 2 -> 5, mocha 3 -> 11, chai 3 -> 4. chai is deliberately
  held at 4.x because chai 5 is ESM-only and the test harness is CommonJS.
- Add `build`, `prepare` and `pretest` scripts. Previously `npm test`
  failed on a clean checkout with "Cannot find module '../bin/transpiler_base.js'"
  because bin/ is gitignored and nothing built it; `pretest` fixes that.
- Add `copy:lib` to stage the browser test libraries that `gulp copy`
  used to produce, dropping its reference to requirejs which was never
  actually a dependency.
- Point `main` at bin/transpiler_esprima.js and emit declarations. The
  old `main` referenced dist/gpujs-transpile.js, a bundle only the (now
  removed) gulp task claimed to build, and which that task could not
  have produced anyway since `--out` is rejected for module-based code.
- Modernize tsconfig: replace the obsolete `filesGlob`/`compileOnSave`
  atom-typescript keys with `include`, drop `noResolve`, and set
  `"types": []`. Under TypeScript 2.9 the auto-included @types/node and
  @types/ws emitted 9 parse errors on every build; those are now gone.

Node >=20 is declared in engines.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ript

mocha 11.7.6 is the latest release and still resolves three transitively
vulnerable packages. There is no newer mocha to upgrade to, so pin the
patched versions with npm `overrides`:

- brace-expansion -> ^5.0.8 for GHSA-mh99-v99m-4gvg (high, DoS via
  unbounded expansion). Affects <= 5.0.7; the 2.x line mocha resolves
  through minimatch has no patched release, so 5.0.8 is the only fix.
  This also clears the derived minimatch and glob advisories.
- diff -> ^8.0.4 for GHSA-73rr-hh4g-fpgx (low, ReDoS in parsePatch /
  applyPatch). Affects >= 6.0.0 < 8.0.3; mocha pins ^7.0.0.
- serialize-javascript -> ^7.0.7 for GHSA-5c6j-r48x-rmvq (high, RCE via
  RegExp.flags) and GHSA-qj8w-gfj5-8c6v (moderate, CPU exhaustion).
  Affects <= 7.0.2 and < 7.0.5 respectively; mocha pins ^6.0.2.

All three cross a major version boundary for mocha, so the affected code
paths were exercised directly: the test suite passes, the reporter still
renders assertion diffs correctly (diff), and `mocha --parallel` still
serializes results across workers (serialize-javascript).

npm audit goes from 33 vulnerabilities (2 low, 2 moderate, 24 high,
5 critical) to 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The repository had no lockfile, which meant GitHub's dependency graph
could only see the four direct ranges in package.json and none of the
transitive tree. That is why Dependabot reported zero alerts for a
dependency set that npm audit scored at 33 vulnerabilities: the
vulnerable packages were all transitive and therefore invisible.

Committing the lockfile makes the resolved tree visible to Dependabot,
pins the `overrides` added in the previous commit, and lets CI use
`npm ci` for reproducible installs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The repository had no CI at all, so the fact that the build had stopped
working entirely on modern Node went unnoticed.

Adds a GitHub Actions workflow running `npm ci`, `npm run build` and
`npm test` against the currently supported Node.js release lines, plus a
guard that the build really emitted bin/*.js. bin/ is gitignored, so a
skipped build would otherwise surface only as a confusing "Cannot find
module" from inside mocha.

`npm audit --audit-level=high` runs as a separate job so a newly
published advisory turning CI red is never mistaken for a build or test
regression.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two defects made transpiler_base.parse() unable to reach any of its own
error reporting.

generateAstConfig() creates a field named `resArr` (matching `resStr`),
but parse() read and wrote `astConfig.retArr`. That field is always
undefined, so parse() passed undefined into ast_generic() and then
called .join() on whatever came back, silently leaving `resArr` empty
and adding a stray `retArr` to the returned config.

ast_errorOutput() built its error context from `ast.src`, but the source
text lives on astConfig, not on the AST node. Since esprima is invoked
with `range: true` every node has a `range`, so the guarded branch was
always taken and always threw "Cannot read properties of undefined
(reading 'slice')" — a TypeError that replaced the descriptive message
the transpiler was trying to raise. The slice was also wrong
independently: it passed 25 as an absolute end offset rather than a
length, so any node starting past offset 25 would yield an empty string.

Read from astConfig.src and slice 25 characters from the node's own
start offset.

Both fixes are covered by new tests, which fail against the previous
source with exactly these symptoms.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
transpiler_esprima read esprima off a bare global. That works for the
browser build, which loads it with a <script> tag, and for the node test
harness, which happens to leak an implicit global from run-node.js. It
does not work for anyone requiring the package: `main` resolves fine but
the first generateAst() call dies with "ReferenceError: esprima is not
defined", despite esprima being a declared runtime dependency.

Add resolveEsprima(), which prefers an existing global and otherwise
requires the esprima package. The browser path is unchanged, since the
global still takes precedence, and both branches are covered by tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The README only mentioned `npm test` and predates both the build script
and the removal of gulp, so nothing explained how bin/ gets produced or
how to run the browser suite now that `gulp bsync` is gone.

Also records that the transpiler is an unfinished prototype. Every
concrete AST node type in ast_generic is still commented out, so parse()
raises "Unknown ast type" for real input; that is worth stating up front
rather than leaving a reader to discover it from the source.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​esprima@​4.0.11001008775100
Addednpm/​chai@​4.5.010010010085100
Addednpm/​typescript@​5.9.31001009010090
Addednpm/​mocha@​11.7.6981009595100

View full report

@socket-security

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm yargs is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: package-lock.jsonnpm/mocha@11.7.6npm/yargs@17.7.3

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/yargs@17.7.3. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant