Skip to content

Simplify the security gate now that the scanner is accurate - #20

Merged
ralyodio merged 1 commit into
mainfrom
gate-simplify
Aug 8, 2026
Merged

ralyodio merged 1 commit into
mainfrom
gate-simplify

Conversation

@ralyodio

@ralyodio ralyodio commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Simplified, now that the scanner itself is fixed.

The baseline machinery existed for one reason: 56 findings here, all 56 false positives, six of them high — so --fail-on would have blocked every PR. That turned out to be a rule problem rather than anything in this repo, and it is fixed upstream in ThreatCrush 0.4.0 (threatcrush#76, published to npm).

This repo now has zero high-severity findings, so the gate is just:

threatcrush scan . --fail-on high

.github/threatcrush-baseline.json and .github/threatcrush-gate.py are gone — about 200 lines removed. It still fails closed: a scan that produces no findings file is reported as not scanned, not as clean.

What the simpler gate gives up

Worth being explicit, because it is a real reduction in coverage. Severity depends on whether the scanner can see the taint source near the sink, so the same XSS is graded differently depending on how the code is arranged. Both measured against this tree:

shape severity blocks?
innerHTML = '<b>' + new URL(location).searchParams.get('q') + '</b>' high yes
innerHTML = '<b>' + q + '</b>', where q is a parameter medium no

So it stops a vulnerability written in one place and misses one whose source sits in another function. The old baseline gate would have caught both, because it failed on any new finding at any severity.

--fail-on medium would close the gap and today costs 31 false positives — all innerHTML sinks in multi-line templates whose interpolations are escaped, just on a different line than the assignment, which a line-oriented scanner cannot see. Closing those properly needs multi-line template awareness in the scanner, which is a reasonable next upstream change.

Both the README and the workflow now say this is a floor, not a proof.

Verification

  • 490 tests pass, tsc --noEmit clean
  • gate verified locally against the published 0.4.0: passes on this tree, exits 1 on an injected XSS with a visible taint source

The reviewed-baseline machinery existed for one reason: the scanner reported 56
findings here and all 56 were false positives, six of them high-severity, so
`--fail-on` would have blocked every pull request and been switched off.

That was a rule problem, not a repository problem, and it is fixed upstream in
ThreatCrush 0.4.0 (profullstack/threatcrush#76) — static innerHTML assignments,
the escaper guard not knowing `esc()`, `searchParams.set` counted as untrusted
input, and test fixtures read as live credentials. This repository now has zero
high-severity findings, so `threatcrush scan . --fail-on high` does the job and
~200 lines of baseline and gate script go away.

It still fails closed: a scan producing no findings file is reported as NOT
scanned rather than as clean.

What it gives up is written down rather than glossed. Severity depends on
whether the scanner can see the taint source near the sink, so the same XSS is
graded differently depending on how the code is arranged — measured both ways
against this repository:

  innerHTML = '<b>' + searchParams.get('q') + '</b>'   high,   blocks
  innerHTML = '<b>' + q + '</b>'  // q is a parameter  medium, does not

So the gate stops a vulnerability written in one place and misses one whose
source sits in another function. `--fail-on medium` would close that gap and
today costs 31 false positives. The README and the workflow both say so: this
is a floor, not a proof, and not a substitute for review.

490 tests pass, tsc clean, and the gate verified locally against the published
0.4.0 — passes on this tree, exits 1 on an injected XSS with a visible source.
@ralyodio
ralyodio merged commit 4ac4b5a into main Aug 8, 2026
5 checks passed
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

ThreatCrush Security Scan

33 finding(s)

MEDIUM: 31 | LOW: 2

Severity Rule Location
MEDIUM js-unescaped-html-sink public/app.js:41
MEDIUM js-unescaped-html-sink public/app.js:97
MEDIUM js-unescaped-html-sink public/app.js:244
MEDIUM js-unescaped-html-sink public/app.js:358
MEDIUM js-unescaped-html-sink public/app.js:385
MEDIUM js-unescaped-html-sink public/app.js:572
MEDIUM js-unescaped-html-sink public/app.js:657
MEDIUM js-unescaped-html-sink public/app.js:678
MEDIUM js-unescaped-html-sink public/app.js:800
MEDIUM js-unescaped-html-sink public/app.js:801
MEDIUM js-unescaped-html-sink public/app.js:848
MEDIUM js-unescaped-html-sink public/app.js:1129
MEDIUM js-unescaped-html-sink public/app.js:1169
MEDIUM js-unescaped-html-sink public/app.js:1218
MEDIUM js-unescaped-html-sink public/app.js:1234
MEDIUM js-unescaped-html-sink public/app.js:1377
MEDIUM js-unescaped-html-sink public/app.js:1379
MEDIUM js-unescaped-html-sink public/app.js:1396
MEDIUM js-unescaped-html-sink public/app.js:1603
MEDIUM js-unescaped-html-sink public/auth.js:41
MEDIUM js-unescaped-html-sink public/auth.js:59
MEDIUM js-unescaped-html-sink public/auth.js:106
MEDIUM js-unescaped-html-sink public/auth.js:256
MEDIUM js-unescaped-html-sink public/auth.js:258
MEDIUM sql-template-interpolation src/cli.ts:552
MEDIUM sql-template-interpolation src/cli.ts:1033
MEDIUM sql-template-interpolation src/server.ts:449
MEDIUM redos-nested-quantifier src/signals/boilerplate.ts:44
MEDIUM sql-template-interpolation src/symbols/routes.ts:75
MEDIUM js-dynamic-code-execution test/dashboard-crypto.test.ts:216
MEDIUM insecure-temp-file test/news.test.ts:420
LOW secret-generic-api-key test/auth.test.ts:190
LOW secret-generic-credential test/credits.test.ts:24

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio deleted the gate-simplify branch August 8, 2026 02:56
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