@clear-capabilities/agentic-security-scanner 0.86.0 → 0.118.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/CHANGELOG.md +404 -0
  2. package/dist/178.index.js +1 -1
  3. package/dist/384.index.js +1 -1
  4. package/dist/637.index.js +1 -1
  5. package/dist/838.index.js +1 -1
  6. package/dist/agentic-security.mjs +84 -84
  7. package/dist/agentic-security.mjs.sha256 +1 -1
  8. package/package.json +12 -6
  9. package/src/.agentic-security/dpia.md +26 -0
  10. package/src/.agentic-security/pqc-migration-plan.json +80 -0
  11. package/src/.agentic-security/pqc-migration-plan.md +32 -0
  12. package/src/.agentic-security/sbom-history/0775d96ba4cf814520d02760cfb3010760c7684f.json +6 -0
  13. package/src/.agentic-security/sbom-history/29300441d3460832137a8e51a92b74068cad2f15.json +6 -0
  14. package/src/.agentic-security/sbom-history/3852ad2aa0e50951bed0c930c29d1861aa13d048.json +6 -0
  15. package/src/.agentic-security/sbom-history/3d96e2daec97e3226c9679bac6e64a89ca10bfe9.json +6 -0
  16. package/src/.agentic-security/sbom-history/5f0b136622fce09b469a1a6137c04d5b18652ea4.json +6 -0
  17. package/src/.agentic-security/sbom-history/64e30595d447b62344d0d9200760269df2095332.json +6 -0
  18. package/src/.agentic-security/sbom-history/71f3241902c9e7437764e3caa046ba8a3bcdd32a.json +6 -0
  19. package/src/.agentic-security/sbom-history/99c372cdc33ecc9dde92960b4ffadf7775860e61.json +6 -0
  20. package/src/.agentic-security/sbom-history/9c93b15e8cf3ff2ddb8cdae92371b82e178b463c.json +6 -0
  21. package/src/.agentic-security/sbom-history/a3544abf1863d71776b61302ca74d9ead2b26a9f.json +6 -0
  22. package/src/.agentic-security/sbom-history/dca15abdec43c508aa342db86dc5db8d7f01fbbe.json +6 -0
  23. package/src/.agentic-security/threat-model.json +3702 -0
  24. package/src/.agentic-security/threat-model.md +118 -0
  25. package/src/dataflow/.agentic-security/dpia.md +26 -0
  26. package/src/dataflow/.agentic-security/sbom-history/15d7c4d20f9d968ebec29445c9e9b3fc6f74e6a1.json +6 -0
  27. package/src/dataflow/.agentic-security/sbom-history/3852ad2aa0e50951bed0c930c29d1861aa13d048.json +6 -0
  28. package/src/dataflow/.agentic-security/sbom-history/73166872888d9ddcbe98645af818ecc7d8917b07.json +6 -0
  29. package/src/dataflow/.agentic-security/sbom-history/8869fe64035bec0152d92ece961e9369d8637076.json +6 -0
  30. package/src/dataflow/.agentic-security/sbom-history/9c93b15e8cf3ff2ddb8cdae92371b82e178b463c.json +6 -0
  31. package/src/dataflow/.agentic-security/sbom-history/b7ac2dada4a9216ccfe8969fc57f988c83f46975.json +6 -0
  32. package/src/dataflow/.agentic-security/threat-model.json +161 -0
  33. package/src/dataflow/.agentic-security/threat-model.md +43 -0
  34. package/src/dataflow/CLAUDE.md +17 -5
  35. package/src/dataflow/catalog.js +9 -0
  36. package/src/dataflow/engine.js +30 -4
  37. package/src/dataflow/index.js +8 -0
  38. package/src/dataflow/proof-gate.js +95 -0
  39. package/src/dataflow/summaries.js +44 -5
  40. package/src/engine.js +199 -5
  41. package/src/ir/.agentic-security/dpia.md +26 -0
  42. package/src/ir/.agentic-security/exploit-bundles.json +77 -0
  43. package/src/ir/.agentic-security/sbom-history/73166872888d9ddcbe98645af818ecc7d8917b07.json +6 -0
  44. package/src/ir/.agentic-security/sbom-history/b5f468a158f6a145aedd8680841c106b9809f16b.json +6 -0
  45. package/src/ir/.agentic-security/threat-model.json +143 -0
  46. package/src/ir/.agentic-security/threat-model.md +39 -0
  47. package/src/ir/CLAUDE.md +1 -0
  48. package/src/ir/callgraph.js +12 -1
  49. package/src/ir/tree-sitter-loader.js +108 -0
  50. package/src/posture/.agentic-security/dpia.md +1 -1
  51. package/src/posture/.agentic-security/pqc-migration-plan.json +1 -1
  52. package/src/posture/.agentic-security/pqc-migration-plan.md +1 -1
  53. package/src/posture/.agentic-security/sbom-history/0724c133d2943c87555695b5b2adcfcfc3373a2d.json +6 -0
  54. package/src/posture/.agentic-security/sbom-history/99c372cdc33ecc9dde92960b4ffadf7775860e61.json +6 -0
  55. package/src/posture/.agentic-security/sbom-history/b7ac2dada4a9216ccfe8969fc57f988c83f46975.json +6 -0
  56. package/src/posture/.agentic-security/threat-model.json +3 -3
  57. package/src/posture/.agentic-security/threat-model.md +1 -1
  58. package/src/posture/corpus-status.js +74 -0
  59. package/src/posture/coverage-report.js +113 -0
  60. package/src/posture/holdout-eval.js +57 -0
  61. package/src/posture/router.js +33 -3
  62. package/src/report/.agentic-security/dpia.md +26 -0
  63. package/src/report/.agentic-security/sbom-history/f94be839aa6a1011f53da016416a561698b8f338.json +6 -0
  64. package/src/report/.agentic-security/threat-model.md +1 -1
  65. package/src/report/index.js +17 -0
  66. package/src/sast/.agentic-security/dpia.md +26 -0
  67. package/src/sast/.agentic-security/pqc-migration-plan.json +65 -0
  68. package/src/sast/.agentic-security/pqc-migration-plan.md +30 -0
  69. package/src/sast/.agentic-security/sbom-history/043a7b401c68f5a298c83d4c42e94f118ff2a89a.json +6 -0
  70. package/src/sast/.agentic-security/sbom-history/0775d96ba4cf814520d02760cfb3010760c7684f.json +6 -0
  71. package/src/sast/.agentic-security/sbom-history/20d77ad7363a82777d506dedbbb1ecfed52f094a.json +6 -0
  72. package/src/sast/.agentic-security/sbom-history/29300441d3460832137a8e51a92b74068cad2f15.json +6 -0
  73. package/src/sast/.agentic-security/sbom-history/3852ad2aa0e50951bed0c930c29d1861aa13d048.json +6 -0
  74. package/src/sast/.agentic-security/sbom-history/3c6b3205f04e26f7654943d465281d00e0fa395e.json +6 -0
  75. package/src/sast/.agentic-security/sbom-history/3d96e2daec97e3226c9679bac6e64a89ca10bfe9.json +6 -0
  76. package/src/sast/.agentic-security/sbom-history/5282502e090013219eb006f7e02d8c983bc29c8e.json +6 -0
  77. package/src/sast/.agentic-security/sbom-history/5f0b136622fce09b469a1a6137c04d5b18652ea4.json +6 -0
  78. package/src/sast/.agentic-security/sbom-history/64e30595d447b62344d0d9200760269df2095332.json +6 -0
  79. package/src/sast/.agentic-security/sbom-history/71f3241902c9e7437764e3caa046ba8a3bcdd32a.json +6 -0
  80. package/src/sast/.agentic-security/sbom-history/73514b485d4770f3c5ee57d87338bccd1c61d8b7.json +6 -0
  81. package/src/sast/.agentic-security/sbom-history/96fa584fc3134512237d58aac63d6f3778b0cbcb.json +6 -0
  82. package/src/sast/.agentic-security/sbom-history/99c372cdc33ecc9dde92960b4ffadf7775860e61.json +6 -0
  83. package/src/sast/.agentic-security/sbom-history/a3544abf1863d71776b61302ca74d9ead2b26a9f.json +6 -0
  84. package/src/sast/.agentic-security/sbom-history/a4a34ab8b3d758de2adc8c3dc3e42d4cb0db00a3.json +6 -0
  85. package/src/sast/.agentic-security/sbom-history/ac236ec3602cfc8fb133f389ec3687e409084673.json +6 -0
  86. package/src/sast/.agentic-security/sbom-history/b57b3e1292f1f9f0de6c5a34a436e330c78008a3.json +6 -0
  87. package/src/sast/.agentic-security/sbom-history/dca15abdec43c508aa342db86dc5db8d7f01fbbe.json +6 -0
  88. package/src/sast/.agentic-security/sbom-history/dcbfd4935922dd165093c08a1003c338eed937c5.json +6 -0
  89. package/src/sast/.agentic-security/sbom-history/fa017f725615b0293814344c8ef7dfeb06775ca0.json +6 -0
  90. package/src/sast/.agentic-security/threat-model.json +816 -0
  91. package/src/sast/.agentic-security/threat-model.md +91 -0
  92. package/src/sast/CLAUDE.md +9 -3
  93. package/src/sast/code-injection-multilang.js +117 -0
  94. package/src/sast/crypto-protocol.js +43 -1
  95. package/src/sast/csharp-structural.js +65 -0
  96. package/src/sast/csharp.js +3 -0
  97. package/src/sast/csrf.js +24 -3
  98. package/src/sast/csv-injection.js +84 -0
  99. package/src/sast/frontend-hygiene.js +106 -0
  100. package/src/sast/go-structural.js +60 -0
  101. package/src/sast/java-structural.js +81 -0
  102. package/src/sast/js-framework-structural.js +115 -0
  103. package/src/sast/kotlin.js +72 -0
  104. package/src/sast/ldap-injection.js +73 -17
  105. package/src/sast/open-redirect.js +47 -5
  106. package/src/sast/php.js +35 -0
  107. package/src/sast/python-structural.js +83 -0
  108. package/src/sast/redos-nfa.js +75 -0
  109. package/src/sast/response-splitting.js +48 -19
  110. package/src/sast/ruby.js +38 -0
  111. package/src/sast/secret-concat.js +60 -0
  112. package/src/sast/ssrf-cloud-metadata.js +13 -0
  113. package/src/sast/stored-taint.js +88 -0
  114. package/src/sast/tree-sitter-sinks.js +62 -0
  115. package/src/sast/weak-password-hash.js +28 -0
  116. package/src/sast/weak-randomness.js +46 -5
  117. package/src/sast/wrong-context-sanitizer.js +94 -0
  118. package/src/sast/xpath-injection.js +30 -6
  119. package/src/sast/xss-reflected-multilang.js +116 -0
  120. package/src/sast/xxe.js +67 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,409 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.103.0 — Go recall: structural detectors (PRD Tier 1)
4
+
5
+ - **`go-structural.js`** (new): SQL injection via a `database/sql` query method
6
+ built with `fmt.Sprintf` or string concat (CWE-89), and path traversal via
7
+ `os.Open`/`os.ReadFile`/etc. built with concat or `fmt.Sprintf` (CWE-22).
8
+ High precision: parameterized queries (`db.Query("… ?", x)`) and
9
+ canonicalized paths (`filepath.Base` + `strings.HasPrefix`) have no
10
+ Sprintf/concat in the sink argument, so they don't match.
11
+ - The centralized guard pass (`dropGuardedFindings`) gained Go path-guard
12
+ tokens (`filepath.Base`/`Abs`, `HasPrefix`).
13
+ - **Measured: 2 FN → TP** (gin-sqli, go-path). **Go F1 0.75 → 1.000;** corpus
14
+ FN 12 → 10; aggregate **F1 0.916 → 0.929.** No new FPs; full gate green.
15
+
16
+ ## 0.102.0 — JS/Python framework recall: structural detectors (PRD Tier 1)
17
+
18
+ JavaScript and Python were the weakest languages (F1 ~0.75) — their remaining
19
+ FNs were framework handlers where input arrives via a framework source
20
+ (`@Query`, `ctx.query`, `request.GET`) and is concatenated into a sink, which
21
+ the taint engine misses without a modeled source.
22
+
23
+ - **`js-framework-structural.js`** (new): SQLi via `.query`/`.execute` concat /
24
+ template-literal (TypeORM/mysql/NestJS, CWE-89); koa-send path traversal
25
+ (CWE-22); `ctx.body` reflected XSS, suppressed by `escape()` (CWE-79);
26
+ NestJS `HttpService.get` SSRF (CWE-918); deep-merge prototype pollution,
27
+ suppressed by a `__proto__`/`FORBIDDEN_KEYS` filter (CWE-1321).
28
+ - **`python-structural.js`** (new): Flask `render_template_string` built from
29
+ input — reflected XSS + SSTI (CWE-79; a Jinja `{{ }}` template is safe and
30
+ does NOT match); Django `.raw`/`.extra` and `cursor.execute` built with
31
+ concat / f-string / `%`-operator format (CWE-89; the `%s` parameterized
32
+ placeholder form does NOT match).
33
+ - High precision (verified on the corpus pre/post pairs + a regression the
34
+ unit test caught: `execute("… %s", [v])` is the *safe* DB-API placeholder,
35
+ not `%`-format injection).
36
+ - **Measured: 8 FN → TP** (flask-xss, hoek, koa-path, koa-xss, nestjs-sqli,
37
+ node-sqli, django, nestjs-ssrf). **Corpus FN 20 → 12; aggregate F1 0.861 →
38
+ 0.916.** JavaScript 0.76 → 0.90, Python 0.82 → 0.88. No new FPs; full gate green.
39
+
40
+ ## 0.101.0 — SSRF/path guard recognition: precision (PRD #1)
41
+
42
+ The recall work (v0.98–v0.100) lifted aggregate F1 ~0.50 → ~0.80 but surfaced a
43
+ precision problem: the scanner flagged **14 *fixed* files as still-vulnerable**
44
+ (precision ~0.83) — mostly SSRF, where the fix adds a host allow/deny check, and
45
+ path traversal, where the fix adds a containment guard. Multiple independent
46
+ detectors decided SSRF/path with no shared notion of "this sink is hardened."
47
+
48
+ - **`ssrf-cloud-metadata.js`**: rule #1 no longer flags the `169.254.169.254`
49
+ literal when it sits in a deny-list / host-comparison guard (the remediation,
50
+ not the vuln).
51
+ - **`engine.js` `dropGuardedFindings(findings, fc)`** — a single, centralized
52
+ pass after all detectors that drops a CWE-918 finding when the sink window has
53
+ a host allow/deny check (deny/allow-list, `getHost`/`hostname` comparison,
54
+ RFC1918/metadata prefix check, `ipaddress`/`getaddrinfo`/`ssrf-req-filter`), or
55
+ a CWE-22 finding when the window has a path containment guard
56
+ (`basename`/`GetFileName`/`secure_filename`/`send_from_directory`, or
57
+ canonicalize + `startsWith`). Uniform across every emitter (regex, structural,
58
+ per-language flow, PY-SAST, CSHARP, GO). The window is **comment-stripped** so
59
+ a "no allow-list / 169.254…" vuln comment can't read as a guard. Opt out:
60
+ `AGENTIC_SECURITY_NO_GUARD_RECOGNITION=1`.
61
+ - **Measured: corpus false-positives 14 → 2** (the 2 remaining are XSS
62
+ escape-html recognition — a different family, next up). **Precision ≈ 0.97;
63
+ aggregate F1 0.80 → 0.861 — past the 0.85 target.** Java + C# now 1.000;
64
+ Python/Go/PHP at 0 FP. **Zero recall loss** (vulnerable-tier TP=68 unchanged) —
65
+ the invariant that proves the guards don't hide real flows.
66
+ - New `guard-recognition.test.js`; full gate green.
67
+
68
+ ## 0.100.0 — Java/Spring + C# recall: structural detectors (PRD Tier 1)
69
+
70
+ Continues the recall-led PRD. The flow-based `csharp.js` and AST/bench Java
71
+ modules miss standalone methods whose tainted-by-convention parameter has no
72
+ in-file source; Java has no string templates, so the shape is concatenation.
73
+
74
+ - **`java-structural.js`** (new): SQLi (executeQuery/createQuery + concat,
75
+ CWE-89), command injection (Runtime.exec/ProcessBuilder + concat, CWE-78),
76
+ path traversal (new File/Paths.get + concat, CWE-22, suppressed by
77
+ canonical/normalize/startsWith guards), SSRF (new URL/URI from a non-literal,
78
+ CWE-918, suppressed by a host allow/deny guard).
79
+ - **`csharp-structural.js`** (new): hardcoded credential in a const/static
80
+ field — including the **split-concat evasion** (`"sk_" + "live_…"`) that
81
+ defeats plain secret regexes (CWE-798, value gated on length/known-prefix so
82
+ header-name constants aren't flagged); guarded SSRF via WebClient/HttpClient
83
+ (CWE-918).
84
+ - **Measured: 5 more FN → TP** (spring-path, spring-ssrf, spring-sqli,
85
+ cs-hardcoded, cs-ssrf). Both modules return clean on the fixed versions.
86
+ - **Session total: corpus false-negatives 35 → 20** (Kotlin 6 + Ruby/PHP 4 +
87
+ Java/C# 5 = 15 FN closed). Additive, language-scoped, no regressions. New
88
+ `java-csharp-structural.test.js` + fixtures; full gate green.
89
+
90
+ Note: spring-ssrf / cs-ssrf still show a pre-existing `post:FP` from other
91
+ detectors on the fixed file (not introduced here) — a precision item for later.
92
+
93
+ ## 0.99.0 — Ruby + PHP recall: structural injection detectors (PRD Tier 1)
94
+
95
+ Continues the recall-led PRD down the corpus FN list. Same root cause as
96
+ Kotlin: the fixtures route request data through a local var
97
+ (`name = params[:name]` / `$f = $r->query->get(...)`) before the sink, so the
98
+ existing rules — which require the literal `params`/`$_GET` token on the sink
99
+ line — miss them.
100
+
101
+ - **`ruby.js`**: structural ActiveRecord SQLi (`.where("… #{x}")` / concat,
102
+ CWE-89) and shell cmdi (backtick / `system` / `exec` with `#{}` or concat,
103
+ CWE-78).
104
+ - **`php.js`**: structural shell cmdi (`shell_exec`/`exec`/`system`/`proc_open`
105
+ with `'…' .` concat or `"… $var"` interpolation, CWE-78) and raw SQLi
106
+ (`DB::raw`/`whereRaw`/`mysqli_query` with concat/interp, CWE-89).
107
+ - High precision: parameterized queries (`.where('… ?', x)`,
108
+ `DB::select('…?', [$x])`), array-form exec (`proc_open(['gzip',$f])`,
109
+ `Open3.capture2('finger', user)`) do **not** match.
110
+ - **Measured: 4 more FN → TP** (rails-sqli, rails-cmdi, symfony-cmdi,
111
+ laravel-sqli). **Total corpus FN this session: 35 → 25** (Kotlin 6 + Ruby/PHP
112
+ 4). Purely additive, language-scoped, no regressions. New
113
+ `ruby-php-structural.test.js` + fixtures; full gate green.
114
+
115
+ Deferred: symfony-csrf (CSRF is a state-change/token detector, FP-prone as a
116
+ regex — needs framework-aware modeling, not a structural injection rule).
117
+
118
+ ## 0.98.0 — Kotlin recall: close all 6 corpus false-negatives (roadmap Tier 1)
119
+
120
+ First execution of the "perfect SAST" PRD, which leads with **recall** (the
121
+ measured CVE-replay F1 ≈ 0.50 is a missed-detection problem). Kotlin was the
122
+ worst single language — 6 FNs across SQLi/cmdi/path/SSRF/XXE/deser — because
123
+ those fixtures are standalone DAO/handler methods with a tainted-by-convention
124
+ parameter and no in-file taint source, so the taint engine saw nothing.
125
+
126
+ - **`sast/kotlin.js`** gains taint-independent **structural** detectors: a
127
+ dangerous sink built with a Kotlin string template (`${x}`) or concat
128
+ (`"…" +`) is the injection shape regardless of variable names —
129
+ SQLi (CWE-89), command injection (CWE-78), path traversal (CWE-22). Plus
130
+ guarded SSRF (CWE-918, suppressed when a host allow/deny check is present),
131
+ insecure XML config XXE (CWE-611, suppressed when secure-processing is set),
132
+ and `ObjectInputStream.readObject` deserialization (CWE-502).
133
+ - High precision: a parameterized query / array-form exec / literal URL / XML
134
+ factory with `setFeature(...)` does **not** match. Verified on the corpus
135
+ pre/post pairs — all 6 vulnerable fixtures fire, all 6 fixed versions stay
136
+ clean (scanner-level).
137
+ - **Measured result: 6 Kotlin FN → TP; total corpus false-negatives 35 → 29.**
138
+ No regressions (purely additive, `.kt`-scoped). New `kotlin-structural.test.js`
139
+ + fixtures; full gate green.
140
+
141
+ Establishes the per-family/per-language porting pattern; next: Rails/Laravel/
142
+ Symfony/Gin/Spring SQLi+cmdi and the remaining second-tier FNs.
143
+
144
+ ## 0.97.0 — Tree-sitter foundation for long-tail languages (roadmap #8)
145
+
146
+ First step of bringing languages with no first-class IR parser
147
+ (rust/solidity/cpp/c/go/swift/dart) onto real AST analysis.
148
+
149
+ - **Optional, ABI-pinned dependency:** `web-tree-sitter` 0.20.8 +
150
+ `tree-sitter-wasms` 0.1.13 as **optionalDependencies**, marked `--external`
151
+ in the ncc build so the committed bundle never embeds WASM (verified: bundle
152
+ stays ~3.5 MB and contains only the external `require`, not the grammars).
153
+ - **`ir/tree-sitter-loader.js`** — lazy, cached, graceful. Loads the runtime +
154
+ grammar at runtime and returns null when absent, so the scanner stays fully
155
+ bootable offline / without the optional deps (long-tail languages fall back
156
+ to the existing pattern detectors).
157
+ - **`sast/tree-sitter-sinks.js`** — first AST-accurate detector, gated behind
158
+ `AGENTIC_SECURITY_TREE_SITTER=1`: Rust shell-spawn command injection (CWE-78,
159
+ `Command::new("sh").arg("-c").arg(<dynamic>)`). Anchoring on real AST nodes
160
+ means the same pattern in a comment or string literal does NOT false-match —
161
+ the precision win over regex.
162
+ - Opt-in + optional-dep gated ⇒ default scan behavior and the committed bundle
163
+ are unchanged. Tests + full gate green.
164
+
165
+ Next on #8: more languages/rules (Solidity dangerous primitives, Go `exec`),
166
+ and eventually CFG construction to route long-tail languages through the full
167
+ taint engine.
168
+
169
+ ## 0.96.0 — Surface coverage honesty + make the corpus reporter runnable
170
+
171
+ Completes the measurement threads from 0.91/0.95 — the data existed but wasn't
172
+ usable. No detection changes.
173
+
174
+ - **Coverage line in the scan report.** `toCLI` now renders a one-line
175
+ blind-spot summary from `_scanMeta`: files scanned, which languages got flow
176
+ analysis (`flow=[…]`) vs pattern-only, how many files were skipped, and the
177
+ unmodeled-sink-candidate count. The #5/#6 data is finally visible to users
178
+ instead of sitting unread in the JSON.
179
+ - **`bench/cve-replay/corpus-status.mjs`** — a runnable report for #10:
180
+ `node bench/cve-replay/corpus-status.mjs [--gaps|--json]` prints progress
181
+ toward the 500-entry target and the empty CWE×language cells. On the current
182
+ corpus: 88/500 entries, 64/160 cells covered, 96 gaps (thinnest: ruby/go/php).
183
+
184
+ ### Deliberately NOT done this round
185
+ - **#8 long-tail languages onto the IR** — needs a tree-sitter dependency
186
+ (can't be installed/bundle-validated in this environment) or five hand-written
187
+ parsers. A dependency decision, not a safe single-pass change.
188
+ - **#1 full** — context-tagged taint lattice (core-engine rewrite).
189
+
190
+ ## 0.95.0 — Stored taint (#2, opt-in) + corpus coverage reporter (#10)
191
+
192
+ - **#2 — Second-order / stored taint** (`sast/stored-taint.js`, **opt-in** via
193
+ `AGENTIC_SECURITY_STORED_TAINT=1`). Flags a value read from a persistence
194
+ store (DB/cache) that flows into an injection sink (XSS/SQLi/cmd/code)
195
+ without re-validation — the stored-XSS / second-order-SQLi shape the forward
196
+ taint engine can't see across the persistence boundary. Lower confidence by
197
+ design (stored data isn't always attacker-controlled), so OFF by default:
198
+ enabling it cannot change default scan behavior.
199
+ - **#10 — Corpus coverage reporter** (`posture/corpus-status.js`). Turns the
200
+ CVE-replay corpus into an actionable map: progress toward the 500-entry
201
+ target and exactly which CWE×language cells have zero ground-truth entries.
202
+ Measures the existing corpus only — it never fabricates entries. This is the
203
+ measurement substrate that makes scaling the corpus prioritizable.
204
+
205
+ Tests for both; full gate green.
206
+
207
+ ### Still genuinely open (need a decision or are research-grade)
208
+ - **#8 long-tail languages onto the IR** — five net-new IR parsers, or a
209
+ tree-sitter dependency (which conflicts with the offline-degradation rule).
210
+ This is a dependency decision, not a quick fix.
211
+ - **#1 full** — context-tagged taint lattice (a core-engine rewrite); the
212
+ practical wrong-context slice already ships.
213
+
214
+ ## 0.94.0 — Same-file-preference call resolution (roadmap #3)
215
+
216
+ The cross-file resolver (`ir/callgraph.js` `resolve()`) was name-based: a bare
217
+ name defined in several files (`handler`, `save`, `query`, …) resolved to
218
+ whichever file Map was iterated first, mis-targeting interprocedural taint to a
219
+ same-named function in an unrelated file (wrong-target FPs and missed-target
220
+ FNs).
221
+
222
+ - `resolve(name, callerFile)` now **prefers the caller's own file** when it
223
+ defines the name — overwhelmingly the intended callee. The taint engine
224
+ threads the caller's file at all three resolution sites (assign-call,
225
+ plain-call, higher-order callback).
226
+ - **Backward-compatible by construction:** with no `callerFile` or no local
227
+ match, the original resolution order is unchanged, so no resolution edge is
228
+ ever dropped (no new false negatives). The full gate — incl. interproc-k2's
229
+ context test, deep-taint, calibration — stays green.
230
+ - New `test/callgraph-resolve.test.js`.
231
+
232
+ ## 0.93.0 — CSV/formula injection (CWE-1236) + scoped validation-lib sanitizers (#7)
233
+
234
+ - **CSV / formula injection** (`sast/csv-injection.js`, CWE-1236): flags user
235
+ data written to a CSV/spreadsheet writer without neutralizing leading formula
236
+ characters (`= + - @` / tab). High precision — requires a CSV-write API + a
237
+ user-taint hint on the same statement and no formula-escape helper nearby.
238
+ - **#7 validation-library sanitizers (correct, narrow):** zod `safeParse`/
239
+ `parseAsync` and class-validator `validateOrReject` are now modeled as
240
+ sanitizers **scoped to `mongo-operator` (NoSQL/operator injection) ONLY**.
241
+ Validating a typed shape defeats operator injection, but a validated string
242
+ is still an XSS/SQL payload — so they are deliberately NOT tagged for those
243
+ families (a blanket tag would cause false negatives). Distinctive callees
244
+ only, to avoid colliding with `JSON.parse`.
245
+
246
+ Fixtures + tests for both; full gate green.
247
+
248
+ ## 0.92.0 — Front-end hygiene detectors (3 verified-missing, additive)
249
+
250
+ New `sast/frontend-hygiene.js` — three high-precision client-side detectors,
251
+ each a verified coverage gap (no prior matches in the detector tree). All
252
+ additive: they emit new finding classes and cannot reduce existing detection.
253
+
254
+ - **Reverse tabnabbing (CWE-1022):** `<a target="_blank">` without
255
+ `rel="noopener"` — the opened page can rewrite `window.opener`. (low)
256
+ - **Missing Subresource Integrity (CWE-353):** a cross-origin
257
+ `<script>` / stylesheet `<link>` with no `integrity=` — a compromised CDN
258
+ runs in your origin. Skips same-origin/relative assets. (medium/low)
259
+ - **Angular sanitizer bypass (CWE-79):** `DomSanitizer.bypassSecurityTrust*`
260
+ on a non-literal value explicitly disables Angular's XSS protection on
261
+ attacker-influenced data. Skips constant-string arguments. (high)
262
+
263
+ Fixtures + 6 tests; full gate green.
264
+
265
+ ## 0.91.0 — Coverage honesty (#5+#6) + wrong-context encoding (#1 slice)
266
+
267
+ Additive precision/trust features that cannot reduce existing detection.
268
+
269
+ ### #5 + #6 — Analysis-coverage honesty report
270
+ - New `posture/coverage-report.js`. `_scanMeta` now publishes the scanner's
271
+ blind spots: per-language **analysis tier** (which languages got the IR +
272
+ taint engine vs. pattern-only — c/c++/rust/swift/solidity/dart are
273
+ pattern-only today), a `filesDenseSkipped` counter (dense files were
274
+ previously dropped with no count), and **unmodeled-sink candidates** —
275
+ dangerous-call shapes (eval/exec/deserialize/yaml.load/…) with no finding on
276
+ their line, i.e. recall blind spots to verify.
277
+
278
+ ### #1 (practical slice) — Wrong-context output encoding (CWE-79)
279
+ - New `sast/wrong-context-sanitizer.js` flags an HTML-entity encoder
280
+ (`escapeHtml`/`htmlspecialchars`/`he.encode`/`lodash.escape`) applied to a
281
+ value used in a URL context (`href`/`src`/`location`). HTML-entity encoding
282
+ does NOT neutralize `javascript:`/`data:` schemes, so the value is still XSS
283
+ while looking sanitized. High precision: excludes `encodeURIComponent` (a
284
+ different, non-XSS mistake) and suppresses when a URL-scheme allow-list is
285
+ near. Fixtures + tests; full gate green.
286
+
287
+ ### Deferred (need core-engine work or carry correctness traps — not faked)
288
+ - **#1 (full):** context-tagged taint through the lattice — the engine's taint
289
+ is binary and can't express "sanitized-for-HTML vs -for-JS". Core change.
290
+ - **#7 validation libraries:** modeling zod/joi/etc. as blanket sanitizers
291
+ would cause FALSE NEGATIVES — a schema-validated string is still an XSS/SQL
292
+ payload. Needs schema-type awareness (validation defeats type-confusion /
293
+ NoSQL-operator injection, not output injection). Deferred deliberately.
294
+ - **#2 stored/second-order taint, #3 import/type-aware call resolution,
295
+ #8 long-tail languages onto the IR, #10 corpus scale** — each its own lift.
296
+
297
+ ## 0.90.0 — Context-sensitive taint completed + bounded (roadmap #2, FR-SEM-2)
298
+
299
+ The interprocedural engine was already value-context sensitive at the
300
+ assign-call site (a distinct summary per entry-taint-state, computed lazily;
301
+ v0.66). This release completes and bounds it.
302
+
303
+ - **Plain-call sites now lazily compute context-specific summaries too.**
304
+ Previously the plain (non-assign) call site only did a cache `get`, so a
305
+ param mutated *only* when the callee is invoked with tainted input was
306
+ missed there. It now mirrors the assign-call site (compute under the actual
307
+ tainted-arg context on a miss).
308
+ - **Per-function context cap** in `SummaryCache` bounds the number of distinct
309
+ non-empty entry contexts kept per function so lazy per-call-site computation
310
+ can't blow up. Over the cap → reuse the empty-entry (monovariant) summary.
311
+ Tunable via `AGENTIC_SECURITY_KCFA_MAX_CONTEXTS` (default 16; **0 = pure
312
+ monovariant**, a clean kill-switch).
313
+ - **Docs corrected:** `summaries.js` and `dataflow/CLAUDE.md` previously called
314
+ the engine "k=1 monovariant / one summary under empty entry." It is in fact
315
+ value-context sensitive; the real remaining limits are call-string (k>1)
316
+ sensitivity and param-level (not access-path-level) entry granularity.
317
+ - New `test/kcfa-context.test.js` (context-sensitivity + cap + kill-switch +
318
+ clear); full gate green.
319
+
320
+ ## 0.89.0 — Per-language metrics (#9) + roadmap audit (#3, #5 already shipped)
321
+
322
+ Continuing the multi-language SAST roadmap. Investigation revealed several
323
+ items were already implemented — so this release delivers the one genuinely
324
+ missing safe item (#9) and corrects the record, rather than re-building what
325
+ exists.
326
+
327
+ ### #9 — Per-language precision/recall (new)
328
+ - `holdout-eval.js` now records a `language` per labeled sample (explicit
329
+ `language` field or derived from the `file` extension) and exposes
330
+ `perLanguage()` + `summarizePerLanguage()`.
331
+ - `evaluateHeldOut` returns a `perLanguage` breakdown and **flags any language
332
+ whose precision trails the aggregate by >0.15** (n≥20) — the regression an
333
+ aggregate would otherwise mask (a 90%-JS corpus hiding poor Ruby precision).
334
+ - New tests in `test/holdout-eval.test.js`.
335
+
336
+ ### #3 — Already implemented; doc corrected
337
+ Audit found mutated-parameter taint (`applyAtCallSite`), higher-order callback
338
+ taint (`_higherOrderInvocations` fed back into the worklist), and recursion via
339
+ a multi-pass fixed point (`MAX_FP_ITERS`) were all shipped in v0.66 and covered
340
+ by `interproc-k2` / `closure-capture` / `phase6-taint` tests. The stale
341
+ `dataflow/CLAUDE.md` "what we do NOT model" section is corrected.
342
+
343
+ ### #5 — Already at parity
344
+ The source/sink catalog already spans Spring, ASP.NET, Gin, Echo, Fiber, Chi,
345
+ Gorilla, Buffalo, Laravel, Symfony, Rails, Sinatra, Ktor, JDBC/JPA/Hibernate,
346
+ Dapper/ADO across all 8 languages. No new work needed.
347
+
348
+ ### Still deferred (research-grade; will ship as their own releases)
349
+ #1 universal IR (no tree-sitter dep; `universal-ir.js` unwired), #2 k-CFA
350
+ context-sensitivity (FR-SEM-2), #4 auto-derived library summaries, #7 dynamic
351
+ dispatch + type inference, #8 incremental-by-default (needs a cold==warm gate),
352
+ #10 LLM closed-loop validator. These are not faked into the engine.
353
+
354
+ ## 0.88.0 — Proof-gate precision pass (multi-language SAST roadmap #6)
355
+
356
+ First flagship of the "perfect multi-language SAST" program: report only
357
+ provably-feasible flows, and demote — never drop — flows we can prove are
358
+ clean or infeasible.
359
+
360
+ - **New `dataflow/proof-gate.js`** consolidates the engine's two independent
361
+ flow-proof signals (`provenClean` from `proven-clean.js`, `_provenUnreachable`
362
+ from `exploit-prover.js`) into one verdict per finding:
363
+ `finding.proof = { verdict: 'feasible' | 'proven-clean' | 'proven-infeasible' | 'unproven', reasons[] }`.
364
+ - **Wired the previously-dead `proven-clean.js`** into `runDeepAnalysis` — SQL
365
+ sinks reached only through a parameterizer are now proven clean by default.
366
+ - **Recall-preserving demotion:** proven-clean / proven-infeasible findings get
367
+ lowered `confidence` + `confidenceTier` + `exploitabilityTier` (and rank below
368
+ feasible findings), but **`severity` is left untouched** so a heuristic proof
369
+ can never hide a finding from a severity-based CI gate.
370
+ - Default on; opt out with `AGENTIC_SECURITY_NO_PROOF_GATE=1`. New
371
+ `test/proof-gate.test.js`; full gate green.
372
+
373
+ Remaining roadmap items (#1 universal IR, #2 k-CFA, #3 dormant taint paths,
374
+ #4 library summaries, #5 framework catalog parity, #7 dynamic dispatch,
375
+ #8 incremental-by-default, #9 per-language metrics, #10 LLM gate) ship as
376
+ their own benchmarked releases.
377
+
378
+ ## 0.87.0 — Sharpen the 12
379
+
380
+ Ergonomics + power features for the 12-command surface left after the
381
+ v0.86.0 consolidation.
382
+
383
+ ### Cross-cutting
384
+ - **Legacy-alias redirect hook** (`UserPromptSubmit`, `hooks/legacy-alias-redirect.js`):
385
+ typing a removed alias (`/status`, `/show-findings`, `/harden`, …) now
386
+ injects context that maps it to the new dispatcher mode, so the request
387
+ still runs. Covers all 44 removed aliases.
388
+ - **Trend-aware `/secure` router**: compares the last two scans and shows a
389
+ `↑ / → / ↓` arrow with what changed (never invents a trend from one scan).
390
+ - **Bare-invocation mode menus** and uniform `--json` documented across the
391
+ dispatchers.
392
+ - **Task-oriented `/secure --help`** + an old→new alias map.
393
+
394
+ ### Per-command
395
+ - `/scan --pick` — interactive mode menu.
396
+ - `/fix --checkpoint` — run a batch fix on a throwaway git branch (atomic revert).
397
+ - `/compliance --gap` — Not-Compliant worklist with the exact closing command per control; `--format oscal|json` machine-readable export.
398
+ - `/supply` — offer to bundle safe patch/minor upgrades into one PR after `--check`.
399
+ - `/posture` (bare) — combined dashboard (status + grade + trend).
400
+ - `/find-and-fix-everything` — auto checkpoint branch + PR-ready summary.
401
+ - `/triage` — order findings likely-FP-first from triage history.
402
+ - `/three-agent-review` — echo the call/wall-time budget before running.
403
+ - `/ci` — validate generated workflow YAML + offer a PR.
404
+ - `/labs` — graduation-status table for experimental modes.
405
+ - `/setup --all` — install hooks + CI + bodyguard + destructive-guard in one pass.
406
+
3
407
  ## 0.76.0 — Command consolidation: 80 → 38 slash commands
4
408
 
5
409
  Simplified the command surface from 80 individual slash commands down to
package/dist/178.index.js CHANGED
@@ -13,7 +13,7 @@ export const modules = {
13
13
  /* harmony import */ var node_child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1421);
14
14
  /* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3024);
15
15
  /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6760);
16
- /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(2271);
16
+ /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4097);
17
17
  // Time-travel + counterfactual scanning (v0.68).
18
18
  //
19
19
  // Two new modes that exploit the pure-input shape of runFullScan:
package/dist/384.index.js CHANGED
@@ -8,7 +8,7 @@ export const modules = {
8
8
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9
9
  /* harmony export */ scanCredentials: () => (/* reexport safe */ _engine_js__WEBPACK_IMPORTED_MODULE_0__.Sv)
10
10
  /* harmony export */ });
11
- /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2271);
11
+ /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4097);
12
12
  // Secrets submodule view of the engine — credential + entropy + TODO scanning.
13
13
 
14
14
 
package/dist/637.index.js CHANGED
@@ -10,7 +10,7 @@ export const modules = {
10
10
  /* harmony export */ renderPrDeltaText: () => (/* binding */ renderPrDeltaText)
11
11
  /* harmony export */ });
12
12
  /* harmony import */ var node_child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1421);
13
- /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2271);
13
+ /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4097);
14
14
  // Shadowscan / security-DELTA on PR (v0.72).
15
15
  //
16
16
  // Most SAST PR-comment integrations show absolute counts — "12 findings
package/dist/838.index.js CHANGED
@@ -14,7 +14,7 @@ __webpack_require__.r(__webpack_exports__);
14
14
  /* harmony import */ var node_child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1421);
15
15
  /* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3024);
16
16
  /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6760);
17
- /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(2271);
17
+ /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4097);
18
18
  // Closed-loop /fix verification (Sentinel-parity FR-L4-4, FR-L4-5).
19
19
  //
20
20
  // Given a candidate patch (the new file content + the finding stableId being