Skip to content

test(security): cross-check the TS lens against eslint-plugin-security - #72

Merged
Rinkia merged 1 commit into
masterfrom
feat/ts-crosscheck
Jul 23, 2026
Merged

Rinkia merged 1 commit into
masterfrom
feat/ts-crosscheck

Conversation

@Rinkia

@Rinkia Rinkia commented Jul 23, 2026

Copy link
Copy Markdown
Owner

What

Closes the validation asymmetry M4 (#71) left behind. Python was measured against Bandit at 27/27; TypeScript had only planted fixtures. This measures it against eslint-plugin-security — the JS counterpart to Bandit, and again a tool this project doesn't control.

Result

Measured across shelljs, cross-spawn, rechoir, liftoff, open, resolve:

In-scope recall 10/10
eslint-only findings (catalog gaps) 0

Same scoping discipline as the Bandit check

js/eslint.config.mjs enables exactly the three rules inside the lens's claims — detect-eval-with-expression (code_exec), detect-child-process (process), detect-non-literal-require (dynamic_load). The plugin's other rules (unsafe regex, object injection, timing attacks, fs filenames, pseudo-random bytes) are off: the lens doesn't claim them, so counting them would measure a promise never made.

Documented asymmetries (not defects):

  • eslint flags eval only with a computed argument; the lens flags every eval, matching Bandit's Python stance.
  • eslint has no rule for new Function, vm.*, or string-bodied timers — lens-only coverage.
  • eslint's detect-child-process covers exec but not spawn/execFileSync; the lens covers the family, split shell vs no-shell.

A bug the harness found in itself — worth reading

The first run reported 6/6. It was wrong. Reading eslint's stdout with the platform locale hit a cp1252 decode failure on Windows that silently dropped findings — turning four real agreements on resolve into apparent "lens-only" divergences, and inflating the recall ratio by shrinking the denominator.

Fixed by decoding UTF-8 explicitly; true figure is 10/10. Recorded in the README, because a validation harness that fails quietly is worse than no harness at all.

Test plan

  • Harness runs clean; recall table reproduces.
  • Suite 25/25, benchmark 9/20 (harness is standalone).
  • node_modules/ gitignored and confirmed not staged; package.json + package-lock.json committed for reproducibility.
  • Apache header on the new .py; printed output is ASCII.

Both lenses now have an external authority: Python 27/27 vs Bandit, TS 10/10 vs eslint-plugin-security.

🤖 Generated with Claude Code

Closes the validation asymmetry M4 left behind. The Python lens was measured
against Bandit at 27/27; TypeScript had only planted fixtures. This measures it
against eslint-plugin-security — the JS counterpart to Bandit, and again a tool
this project does not control, so the check cannot flatter itself.

Scope is restricted the same way: eslint.config.mjs enables exactly the three
rules inside the lens's claims — detect-eval-with-expression, detect-child-process
and detect-non-literal-require. The plugin's other rules (unsafe regex, object
injection, timing attacks, fs filenames) are left off, because the lens does not
claim them and counting them would measure a promise never made.

Measured across shelljs, cross-spawn, rechoir, liftoff, open and resolve:
10/10 in-scope recall, no eslint-only findings.

The expected asymmetries are documented rather than hidden: eslint flags eval
only with a computed argument while the lens flags every eval (matching Bandit),
and eslint has no rule for new Function, the vm module or string-bodied timers,
which the lens catalogues.

Worth recording: the harness initially reported 6/6, and it was wrong. Reading
eslint's output with the platform locale hit a cp1252 decode failure on Windows
that silently dropped findings, turning four real agreements on `resolve` into
apparent lens-only divergences. Decoding UTF-8 explicitly fixed it and the true
figure is 10/10. A validation harness that fails quietly is worse than none, so
the cause is noted in the README.

node_modules/ is gitignored; package.json and package-lock.json are committed so
the toolchain and scan targets are reproducible. Suite 25/25; benchmark 9/20.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

MODScan extension-point diff

No breaking changes to extension points

@github-actions

Copy link
Copy Markdown

MODScan attack-surface diff — modscan

No new execution sinks

New attack surface to review — NOT a vulnerability verdict. This compares enumerated execution sinks between two snapshots. It does not trace whether input reaches them (no taint analysis), match CVEs, or detect secrets. An empty diff means no new catalogued sinks — not that the change is safe.

The two snapshots enumerate the same execution sinks.

@Rinkia
Rinkia merged commit 3507bc7 into master Jul 23, 2026
7 checks passed
@Rinkia
Rinkia deleted the feat/ts-crosscheck branch July 23, 2026 14:36
@Rinkia Rinkia mentioned this pull request Jul 23, 2026
3 of 5 tasks
Rinkia added a commit that referenced this pull request Jul 23, 2026
Bump to 0.1.7 and move the [Unreleased] entries under [0.1.7]. Ships the Java
front-end, the security lens over MCP and for TypeScript/JavaScript, the process
sinks the Bandit cross-check exposed, and two TypeScript front-end fixes.

Also adds the two CHANGELOG entries that were missing: recognising CommonJS
exports (#73) and no longer scanning .d.ts declaration files (#76). Both change
what `--language typescript` returns, so both belong in a user-facing changelog.
The cross-check harnesses (#70, #72) and benchmark targets (#74, #78) stay out:
they are repository artifacts, not shipped in the wheel.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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