Skip to content

Add oxlint engine and make it the default JS/TS scanner - #79

Open
sraisl wants to merge 2 commits into
mainfrom
feat/oxlint-engine
Open

sraisl wants to merge 2 commits into
mainfrom
feat/oxlint-engine

Conversation

@sraisl

@sraisl sraisl commented Oct 21, 2026

Copy link
Copy Markdown
Owner

Summary

The ESLint engine runs with ESLint's default parser, which cannot read TypeScript syntax. Parse errors have no rule ID and were silently dropped (src/engines/eslint.ts), so .ts/.tsx files never produced findings. Verified locally with ESLint 10.12.0 on test-fixtures/src/vuln.ts: Parsing error: The keyword 'interface' is reserved.

This PR adds an oxlint engine and makes it the default JS/TS scanner.

  • New engine src/engines/oxlint.ts: pinned, SHA-256-verified standalone binary (oxlint_v1.89.0, digest matches the GitHub release API). It parses JS/TS/JSX/TSX natively and is skipped without a download when the target has no JS/TS files.
  • Rules: no-eval, no-implied-eval, no-new-func, no-script-url, react/no-danger. All default categories are off. Nested project configs are disabled so a scanned repo cannot switch the rules off. react/jsx-no-script-url is left out because no-script-url reports the same location.
  • Unparseable files are listed in the engine note instead of being dropped.
  • engines: "all" now runs oxlint instead of eslint. ESLint stays available as an opt-in engine.
  • engines:check previously failed for oxc-project/oxc because the repo also publishes oxfmt_v… and crates_v… releases. It now picks the newest stable release whose tag matches the tool's template.
  • Selftest asserts an oxlint/no-eval finding in test-fixtures/src/vuln.ts.
  • Docs: README, CHANGELOG, action.yml, Copilot instructions.

⚠️ Behavior change

Findings move from eslint/<rule> to oxlint/<rule> rule IDs, so existing code scanning alerts close and reopen under the new IDs. Together with the changed all default, this probably warrants a major release (v17).

Test plan

  • npm run typecheck, npm test (214/214), npm run build with Node 24. The dist/ diff contains only the expected changes.
  • Ran the built adapter on test-fixtures: no-eval in vuln.js:7 and src/vuln.ts:10. It is skipped on a directory without JS/TS files.
  • Downloaded the Linux release asset, verified its checksum and extracted it. The archive holds oxlint-x86_64-unknown-linux-gnu, which matches the rename in the installer.
  • Selftest on a Linux runner (this PR's CI).

Note: in a local test, oxlint's no-implied-eval did not flag setTimeout("alert(1)", 1). It stays in the config at no cost.

🤖 Generated with Claude Code

The ESLint engine runs with ESLint's default parser, which cannot read
TypeScript syntax. Parse errors carry no rule ID and were dropped, so
.ts/.tsx files never produced findings.

oxlint parses JS, TS, JSX and TSX natively and ships as a single
SHA-256-verified binary, so no npm install is needed per run. The engine
runs no-eval, no-implied-eval, no-new-func, no-script-url and
react/no-danger with nested project configs disabled, and reports
unparseable files in the engine note instead of dropping them.

`engines: "all"` now runs oxlint instead of eslint. ESLint remains
available as an opt-in engine.

engines:check now finds a tool's latest release in monorepos such as
oxc-project/oxc, where the repository-wide latest release can belong to
another product (oxfmt, crates).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Unchecked PATH versions and unsafe source discovery can undermine reproducibility, omit scanned files, or discard valid findings.

2 open issues
What changed in this PR

Adds oxlint as PolyScan’s default JavaScript/TypeScript scanner, replacing ESLint in engines: "all" while retaining ESLint as an opt-in.

Changes:

  • Adds oxlint provisioning, security rules, and diagnostic parsing.
  • Updates release discovery for repositories publishing multiple tools.
  • Adds tests, documentation, and the generated runtime bundle.
File Description
tools.lock.json Pins oxlint version and checksum.
test/​tool-versions.test.ts Checks oxlint download metadata.
test/​oxlint-parser.test.ts Tests parsing and source detection.
test/​engines.test.ts Verifies updated default engines.
test/​engine-tools-cli.test.ts Tests product-specific release discovery.
src/​summary.ts Updates URL documentation comments.
src/​schema.ts Documents oxlint-provided URLs.
src/​main.ts Dispatches oxlint scans.
src/​engines/​oxlint.ts Implements the oxlint adapter.
src/​engines.ts Registers oxlint and changes defaults.
scripts/​engine-tools.mjs Matches releases against tool tag templates.
README.md Documents oxlint and changed defaults.
dist/​index.js Bundles the updated implementation.
CHANGELOG.md Records additions and migration effects.
action.yml Updates engine input documentation.
.github/​workflows/​selftest.yml Checks TypeScript detection through SARIF.
.github/​skills/​engine-tool-update/​SKILL.md Adds oxlint to skill scope.
.github/​copilot-instructions.md Adds oxlint to the engine overview.

Review effort: Balanced

Comment thread src/engines/oxlint.ts Outdated
Comment thread src/engines/oxlint.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Repository suppression files can hide security findings, and some collected sources are silently left unscanned.

3 open issues
2 resolved since last review

Review effort: Balanced

Comment thread src/engines/oxlint.ts
const findings: Finding[] = [];
const parseErrorFiles = new Set<string>();
for (const batch of oxlintBatches(files, flags)) {
const res = await run(bin, [...flags, ...batch], { cwd: abs });
Comment thread src/engines/oxlint.ts
fs.writeFileSync(configPath, JSON.stringify(CONFIG));

// Nested project configs are disabled so a repository cannot switch the security rules off.
const flags = ["--config", configPath, "--disable-nested-config", "--no-ignore", "--format", "json", "--"];
Comment thread src/engines/oxlint.ts
const unparsed = [...parseErrorFiles].sort();
return {
engine: "oxlint",
findings,
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.

3 participants