Skip to content

feat(scan): LDAP/XPath/NoSQL/host-header rules — coverage 79.8% → 83.0% (v0.11.0) - #104

Merged
ralyodio merged 1 commit into
masterfrom
feat/coverage-tier-3
Aug 11, 2026
Merged

feat(scan): LDAP/XPath/NoSQL/host-header rules — coverage 79.8% → 83.0% (v0.11.0)#104
ralyodio merged 1 commit into
masterfrom
feat/coverage-tier-3

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Release 0.11.0. The third and final coverage tier: 79.8% → 83.0% true-positive against the testbed, FPR still 0%, zero new findings on capacitor (10), the self-scan (67), ShortsStudio (0) or koajs/router (0).

Two rules from 0.10.0 that never actually fired

They passed their unit tests but did nothing on the real corpus — the tests used simplified fixtures. The testbed caught what the unit tests didn't, which is exactly why the coverage gate exists.

  • py-ldap-injection — the fixture is under if False: with a module-level from ldap.filter import escape_filter_chars, and my /escap/i guard matched that import in every window, exonerating the unescaped filter too. The guard is now an escaper call (escape…(), which an import is not.
  • py-xpath-injection — the "-delimited f-string contains a ' (text()='{x}'), and a class excluding both quotes stopped before the interpolation. Now one variant per delimiter quote.

Both tests now reproduce the real conditions (import-present, inner-quote) so they'd fail against the old rules.

Two new precise rules

  • js-nosql-injection (CWE-943) — a request object passed straight to find/update, i.e. Mongo operator injection / auth bypass.
  • js-host-header-trust (CWE-346) — a URL built from req.headers.host, the password-reset-poisoning shape.

Built and dropped: recursive-merge prototype pollution

High-value for npm, so I tried hard — three rounds of tightening. But the bare target[key] = source[key] copy is the safe allow-listed shape (updates[field] = body[field] over an allowedFields list) as often as the vulnerable one. Whether it's a sink depends on where the key comes from and which of endless guard idioms filters it — a whole-function question line matching can't decide. It flagged legitimate merges in Capacitor and in this repo's own web app, so it's left to KNOWN_GAPS. js-prototype-pollution still catches the explicit __proto__ literal.

That's the discipline the whole effort runs on: a rule that FPs on real code gets a scanner uninstalled, so one ambiguous class isn't worth two testbed lines.

Where coverage stands

The remaining ~22 misses are now almost entirely the classes that genuinely need whole-function reasoning — CSRF, IDOR, TOCTOU, missing authorization, session fixation, integer overflow, and the sanitize-upstream family whose vulnerable and safe lines are identical. Chasing them with line matching spends the 0% FPR that makes the tool worth installing.

Verification

  • Testbed 83.0% TPR / 0% FPR; gate floor raised 76 → 80.
  • Zero new findings across all four real repos.
  • 157 tests (up from 154). tsc --noEmit clean.

Detection coverage 79.8% to 83.0% (103 to 107 of 129), FPR still 0%, and zero
new findings on capacitor (10), the self-scan (67), ShortsStudio (0) or
koajs/router (0).

Two of the rules shipped in 0.10.0 passed their unit tests but did nothing on
the real corpus — the tests used simplified fixtures. Both fixed, and the tests
now reproduce the real conditions:

  - py-ldap-injection: the whole fixture is under `if False:` with a
    module-level `from ldap.filter import escape_filter_chars`, and the
    `/escap/i` guard matched that import in every window — exonerating the
    unescaped filter too. The guard is now an escaper *call* (`escape…(`), which
    the import is not.
  - py-xpath-injection: the `"`-delimited f-string contains a `'`
    (`text()='{x}'`), and a class excluding both quotes stopped before the
    interpolation. Now one variant per delimiter quote.

Two new, precise rules:

  - js-nosql-injection: a request object passed straight to `find`/`update`
    (CWE-943) — a Mongo operator-injection / auth bypass.
  - js-host-header-trust: a URL built from `req.headers.host` (CWE-346) — the
    password-reset-poisoning shape.

Also built and dropped: a recursive-merge prototype-pollution rule. The bare
`target[key] = source[key]` copy is the safe allow-listed shape
(`updates[field] = body[field]` over an `allowedFields` list) as often as the
vulnerable one — whether it is a sink depends on the key's origin and which of
endless guard idioms filters it, which line matching cannot decide. It flagged
legitimate merges in Capacitor and in this repo's own web app, so it is left to
KNOWN_GAPS; `js-prototype-pollution` still catches the explicit `__proto__`
literal.

Gate floor moves 76 to 80. 157 tests, up from 154.

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

Copy link
Copy Markdown

ThreatCrush Security Scan

67 finding(s)

HIGH/CRITICAL: 11 | MEDIUM: 55 | LOW: 1

Severity Rule Location
HIGH secret-aws-access-key prd/0003-detect-hardcoded-secrets-before-they-are-committed-or-served.md:126
HIGH sh-eval-expansion .githooks/pre-commit:26
HIGH sh-remote-script-execution apps/web/public/install.sh:272
HIGH sh-remote-script-execution apps/web/public/install.sh:320
HIGH secret-generic-credential modules/spend-guard/config/example.conf.toml:13
HIGH secret-generic-credential modules/spend-guard/README.md:84
HIGH secret-generic-credential PRD.md:268
HIGH tls-verification-disabled prd/0004-find-dangerous-code-patterns-without-pretending-to-be-a-compiler.md:121
HIGH tls-verification-disabled prd/0004-find-dangerous-code-patterns-without-pretending-to-be-a-compiler.md:122
HIGH sh-remote-script-execution scripts/smoke-test.sh:46
HIGH sh-remote-script-execution scripts/smoke-test.sh:47
MEDIUM insecure-temp-file .githooks/commit-msg:16
MEDIUM insecure-temp-file .githooks/post-commit:20
MEDIUM js-shell-exec-interpolation apps/cli/src/commands/init.ts:70
MEDIUM js-shell-exec-interpolation apps/cli/src/commands/init.ts:79
MEDIUM js-shell-exec-interpolation apps/cli/src/commands/service.ts:88
MEDIUM js-shell-exec-interpolation apps/cli/src/commands/service.ts:111
MEDIUM sql-template-interpolation apps/cli/src/core/state.ts:121
MEDIUM sql-template-interpolation apps/cli/src/core/state.ts:125
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:31
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:33
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:34
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:35
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:36
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:43
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:49
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:56
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:63
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:82
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:84
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:85
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:93
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:98
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:105
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:112
MEDIUM js-shell-exec-interpolation apps/cli/src/index.ts:419
MEDIUM js-unescaped-html-sink apps/web/src/app/about/page.tsx:180
MEDIUM js-unescaped-html-sink apps/web/src/app/about/page.tsx:184
MEDIUM js-open-redirect apps/web/src/app/auth/login/page.tsx:50
MEDIUM js-unescaped-html-sink apps/web/src/app/blog/[slug]/page.tsx:125
MEDIUM js-unescaped-html-sink apps/web/src/app/blog/[slug]/page.tsx:153
MEDIUM js-unescaped-html-sink apps/web/src/app/blog/[slug]/page.tsx:157
MEDIUM js-unescaped-html-sink apps/web/src/app/get-whitepaper/page.tsx:346
MEDIUM js-unescaped-html-sink apps/web/src/app/layout.tsx:211
MEDIUM js-unescaped-html-sink apps/web/src/app/layout.tsx:215
MEDIUM js-unescaped-html-sink apps/web/src/app/layout.tsx:219
MEDIUM js-unescaped-html-sink apps/web/src/app/page.tsx:120
MEDIUM js-unescaped-html-sink apps/web/src/app/store/[slug]/page.tsx:107
MEDIUM js-open-redirect apps/web/src/components/funding/FundingClient.tsx:97
MEDIUM manifest-install-lifecycle-script package.json:24

…and 17 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit d9a1c84 into master Aug 11, 2026
11 checks passed
@ralyodio
ralyodio deleted the feat/coverage-tier-3 branch August 11, 2026 09:01
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