@clear-capabilities/agentic-security-scanner 0.134.0 → 0.136.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 (93) hide show
  1. package/CHANGELOG.md +196 -0
  2. package/bin/agentic-security-audit.js +2 -1
  3. package/bin/agentic-security-consistency.js +2 -1
  4. package/bin/agentic-security.js +269 -44
  5. package/dist/113.index.js +6 -6
  6. package/dist/117.index.js +3 -1
  7. package/dist/178.index.js +1 -1
  8. package/dist/220.index.js +5 -3
  9. package/dist/238.index.js +4 -4
  10. package/dist/317.index.js +270 -0
  11. package/dist/384.index.js +1 -1
  12. package/dist/435.index.js +13 -7
  13. package/dist/513.index.js +7 -3
  14. package/dist/526.index.js +6 -6
  15. package/dist/637.index.js +1 -1
  16. package/dist/675.index.js +7 -5
  17. package/dist/839.index.js +4 -3
  18. package/dist/905.index.js +1173 -0
  19. package/dist/agentic-security.mjs +14 -14
  20. package/dist/agentic-security.mjs.sha256 +1 -1
  21. package/dist/compliance-frameworks/ccpa.json +32 -0
  22. package/dist/compliance-frameworks/eu-ai-act.json +51 -0
  23. package/dist/compliance-frameworks/gdpr.json +45 -0
  24. package/dist/compliance-frameworks/hipaa-security-rule.json +56 -0
  25. package/dist/compliance-frameworks/nist-ai-600-1.json +51 -0
  26. package/dist/compliance-frameworks/nist-csf-2.json +73 -0
  27. package/dist/compliance-frameworks/nist-privacy-1-1.json +846 -0
  28. package/dist/compliance-frameworks/owasp-asvs-5.json +79 -0
  29. package/dist/compliance-frameworks/owasp-llm-top-10.json +69 -0
  30. package/package.json +9 -5
  31. package/src/badge.js +2 -1
  32. package/src/dataflow/cross-service-taint.js +2 -1
  33. package/src/dataflow/ifds-precise.js +6 -4
  34. package/src/dataflow/incremental.js +7 -5
  35. package/src/discovery/CLAUDE.md +10 -0
  36. package/src/discovery/index.js +175 -3
  37. package/src/discovery/llm-invoke.js +90 -1
  38. package/src/discovery/memory.js +163 -0
  39. package/src/engine.js +45 -8
  40. package/src/integrations/tickets.js +7 -6
  41. package/src/ir/ir-stats.js +1 -1
  42. package/src/ir/parser-cpp.js +1 -1
  43. package/src/leaderboard.js +3 -2
  44. package/src/llm-validator/consistency.js +6 -2
  45. package/src/llm-validator/index.js +1 -2
  46. package/src/mcp/audit.js +2 -1
  47. package/src/mcp/tools.js +4 -3
  48. package/src/posture/CLAUDE.md +26 -1
  49. package/src/posture/accuracy-scorecard.js +59 -1
  50. package/src/posture/agents-memory.js +5 -3
  51. package/src/posture/auditor-walkthrough.js +9 -7
  52. package/src/posture/auth-posture-import.js +5 -4
  53. package/src/posture/compliance-frameworks/nist-privacy-1-1.json +846 -0
  54. package/src/posture/compliance-policy.js +7 -9
  55. package/src/posture/custom-rules.js +7 -5
  56. package/src/posture/cve-alert-daemon.js +6 -5
  57. package/src/posture/dep-add-guard.js +2 -1
  58. package/src/posture/deterministic.js +3 -2
  59. package/src/posture/evidence-bundle.js +246 -0
  60. package/src/posture/exploitability-probability.js +2 -1
  61. package/src/posture/feature-flags.js +3 -2
  62. package/src/posture/findings-memory.js +3 -3
  63. package/src/posture/fix-history.js +5 -2
  64. package/src/posture/fix-metrics.js +5 -5
  65. package/src/posture/fix-plan.js +2 -1
  66. package/src/posture/grader-calibration.js +3 -4
  67. package/src/posture/intent-context.js +2 -1
  68. package/src/posture/learning.js +4 -3
  69. package/src/posture/license-attributions.js +5 -7
  70. package/src/posture/license-graph.js +2 -1
  71. package/src/posture/license-policy.js +2 -1
  72. package/src/posture/model-rescan.js +4 -3
  73. package/src/posture/network-policy-import.js +3 -2
  74. package/src/posture/pqc-migration-plan.js +7 -5
  75. package/src/posture/pr-augment.js +8 -5
  76. package/src/posture/privacy-framework.js +262 -0
  77. package/src/posture/risk-dollars.js +2 -2
  78. package/src/posture/router.js +5 -4
  79. package/src/posture/ruleset-version.js +2 -2
  80. package/src/posture/runtime-correlation.js +2 -1
  81. package/src/posture/sbom-diff.js +12 -3
  82. package/src/posture/sca-policy.js +7 -4
  83. package/src/posture/scan-checkpoint.js +15 -0
  84. package/src/posture/state-dir.js +34 -0
  85. package/src/posture/telemetry-ingest.js +4 -3
  86. package/src/posture/threat-model-auto.js +4 -1
  87. package/src/posture/threat-model-grounding.js +11 -1
  88. package/src/posture/time-to-fix.js +3 -2
  89. package/src/posture/triage-memory.js +3 -2
  90. package/src/posture/waf-ingest.js +6 -5
  91. package/src/posture/watch-mode.js +4 -3
  92. package/src/sast/code-injection-multilang.js +29 -0
  93. package/src/sca/dep-confusion.js +2 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,201 @@
1
1
  # Changelog
2
2
 
3
+ > **A note on `docs/*_PRD.md` references below.** Product requirement documents in
4
+ > this project are removed once their workstreams land — the durable parts move
5
+ > into the code and tests that enforce them, where they cannot drift out of date.
6
+ > Entries below cite PRD files that were live at the time of that release and no
7
+ > longer exist. They are left as written: a changelog records what was true when
8
+ > it was written, and rewriting past entries to hide a since-deleted file would
9
+ > make the history less accurate, not more.
10
+
11
+
12
+ ## 0.136.2 — authenticate the gate on the path that actually runs it
13
+
14
+ 0.136.1 removed the self-deadlock and the release workflow got further: every
15
+ gate check passed, then `npm publish` failed with *"the forge CLI is not
16
+ authenticated"*.
17
+
18
+ The gate runs **twice** in that job. Once as an explicit `Release gate` step,
19
+ which sets `GH_TOKEN` and passed. Then again inside `npm publish`, which
20
+ triggers `prepublishOnly` — and that step set only `NODE_AUTH_TOKEN`. The second
21
+ run had no token, could not read hosted CI, and refused.
22
+
23
+ The gate was right to refuse: unverifiable is not green, and the remedy it
24
+ suggests — `--allow-unverified-ci` — would have published without proving the
25
+ commit was green at all. So the fix is to authenticate it, not to relax it.
26
+ `checks: read` is now declared explicitly too; the gate passed without it on the
27
+ default token, but depending on an undeclared default is how a tightened default
28
+ becomes a mystery failure a year later.
29
+
30
+ The same shape as the deadlock it follows: a control that works on the path
31
+ that was tested, and fails on the path that actually ships.
32
+
33
+ ## 0.136.1 — the release gate stops deadlocking on itself
34
+
35
+ `npm publish` had been impossible since 0.135.0, and the cause was not the one I
36
+ reported. Not a missing npm token: **the release gate was waiting for itself.**
37
+
38
+ The release workflow's job is named `publish`. The gate runs inside that job,
39
+ queries hosted CI for HEAD, sees a check run named `publish` that is
40
+ `in_progress` — itself — and requires it to finish before allowing the release.
41
+ It never can. Every tag push failed this way with all nine real checks green, and
42
+ the resulting `publish: failure` then blocked local publishes too, which is the
43
+ error that finally surfaced it.
44
+
45
+ `.github/required-checks.json` gains a third category, **self**, beside blocking
46
+ and informational. A self check is EXCLUDED, not trusted: it cannot report a
47
+ conclusion until the gate it contains has already passed, so requiring it is a
48
+ deadlock and believing it would be believing a check that has not run. The file
49
+ already insisted every check be classified deliberately — this is the category
50
+ that was missing, and its absence meant `publish` fell through to the safe
51
+ default of blocking, which was exactly wrong here.
52
+
53
+ Proven in three directions, because excluding a check must not weaken a gate:
54
+ a pending self check no longer blocks, a FAILED self check no longer blocks, and
55
+ a genuinely red blocking check still does. Verified against live CI state:
56
+ `PASS Hosted CI is green for HEAD`.
57
+
58
+ v0.135.0 and v0.136.0 were tagged but never reached npm for this reason. The
59
+ tags stay where they are — a public tag is not moved — so this ships as 0.136.1.
60
+
61
+ ## 0.136.0 — NIST Privacy Framework 1.1, and the frameworks that never shipped
62
+
63
+ ### A privacy compliance scan that says what it did not check
64
+
65
+ All 104 PF 1.1 controls, assessed on every scan, artifacts at
66
+ `.agentic-security/privacy-framework.{json,md}`. Each gap is emitted as an
67
+ ordinary finding (`family: privacy-compliance`, `CWE-359`) carrying an actionable
68
+ remediation, so `/fix` handles it like anything else.
69
+
70
+ The design turns on one column in NIST's own workbook. PF 1.1 rates each control
71
+ for code-testability — **23 yes, 33 partial, 48 no** — and that rating says a
72
+ control *could* be assessed from source, not that this engine assesses it.
73
+ Collapsing the two is how a privacy report marks "the organizational mission is
74
+ communicated" as PASSED because no rule fired against it, and someone hands that
75
+ to an auditor. So every control lands in exactly one stated bucket:
76
+
77
+ | Bucket | Meaning |
78
+ |---|---|
79
+ | gap | mapped to an engine signal, and that signal is failing — the only bucket that emits a finding |
80
+ | not assessed | NIST rates it code-testable, this engine has no signal — named, never a pass |
81
+ | manual | NIST rates it not code-testable — governance, outside any scanner's reach |
82
+ | satisfied | mapped, and the signal is clean |
83
+
84
+ Measured on a live fixture: 9 gaps, 20 satisfied, 27 not assessed, 48 manual.
85
+ The satisfied rate is reported over the **29 assessed** controls, never over 104.
86
+
87
+ A **vacuous-satisfaction guard** was added after the module's own test caught it:
88
+ a `family:`-mapped control clears when no findings of that family are open, which
89
+ is equally true of a scan that read zero files. Pointing the tool at an empty
90
+ directory was reporting privacy controls as satisfied on the strength of having
91
+ looked at nothing. Now every mapped control degrades to *not assessed* and the
92
+ summary says so.
93
+
94
+ Findings are opt-in (`AGENTIC_SECURITY_PRIVACY_FRAMEWORK=1`). The assessment
95
+ always runs and persists; appending findings by default would change every
96
+ severity count and gate verdict downstream, and a compliance opinion should not
97
+ silently become someone's build failure.
98
+
99
+ ### A real `compliance` subcommand
100
+
101
+ ```
102
+ agentic-security compliance [--gap] [--list] [--walkthrough <id>]
103
+ [--format cli|json|md] [--fail-on gap]
104
+ ```
105
+
106
+ `/compliance --privacy` was documented as a mode with no binary behind it. It
107
+ reads `last-scan.json` rather than re-scanning — a compliance answer is a
108
+ statement about a scan that happened. With no scan to read it exits **2** rather
109
+ than assessing an empty project. Exit codes: 0 report produced, 1 only with
110
+ `--fail-on gap` and a failing control, 2 nothing to assess.
111
+
112
+ ### Every bundled framework had been invisible from the published artifact
113
+
114
+ Running the new subcommand from the shipped bundle printed nothing and exited 0.
115
+ `auditor-walkthrough` resolves its data directory from `import.meta.url` — inside
116
+ the bundle that is `dist/`, and `dist/compliance-frameworks/` never existed
117
+ because the build only emitted the `.mjs`. `listFrameworks` catches the readdir
118
+ failure and returns `[]`.
119
+
120
+ So GDPR, ASVS, NIST AI 600-1 — all nine — worked perfectly from source and were
121
+ **silently absent from the npm package**. Nothing caught it because every test
122
+ ran against `src/`. The build now copies the data next to the bundle, the data is
123
+ tracked in git for the same reason the bundle is, and a test drives the BUNDLE so
124
+ it cannot regress.
125
+
126
+ Twice in this release, testing the shipped artifact rather than the source found
127
+ something the whole suite was blind to.
128
+
129
+ ### Also
130
+
131
+ Removed all remaining PRD documents, repairing 18 files that referenced them.
132
+ `bench/proof-corpus`'s section-level citations (parse-coverage rule, acceptance
133
+ criterion 2, criterion 4, the disclosure boundary) are now stated inline and
134
+ owned by the files that depend on them — the rationale outlived the document.
135
+
136
+ ## 0.135.0 — a scan stops modifying what it scans, and the benchmark stops flattering
137
+
138
+ Two findings, one of which reverses something this project previously reported.
139
+
140
+ ### `--no-state`: a scan is an observation again
141
+
142
+ Pointing the engine at a directory used to write **10 files** into it. For a user
143
+ that means CI asserting a clean tree fails after a scan, and scanning a
144
+ dependency or a customer's code leaves artifacts in a tree they own. Worse, our
145
+ own output contains CWE identifiers, so a second scan could read the first
146
+ scan's conclusions as source.
147
+
148
+ `AGENTIC_SECURITY_NO_STATE=1` (and `--no-state`) now adds **zero paths** while
149
+ reporting byte-identical findings. Both halves are asserted, and the test was
150
+ proven to FAIL with the switch off — the switch must change what is written,
151
+ never what is found. The engine also skips `.agentic-security/` when walking, so
152
+ our output can never become our input.
153
+
154
+ Three defects were found in the guard meant to enforce this, each worth more
155
+ than the line that fixed it:
156
+
157
+ - **`git status` is not sufficient evidence.** Git does not track empty
158
+ directories, so an earlier revision reported a CLEAN tree while still creating
159
+ `sbom-history/` and `fix-history/`. Directory creation is mutation: it fails on
160
+ a read-only mount and is litter in someone else's repo. The acceptance test
161
+ compares full path listings.
162
+ - **The guard was blind to `bin/`** — where the three largest artifacts
163
+ (`findings.json`, `last-scan.json`, `.sig`) are written. A seam guard that
164
+ cannot see the CLI entry point misses the primary writer.
165
+ - **The detector counted documentation as a violation**, then over-corrected: a
166
+ glob inside `// .agentic-security/rules/*.yml` opened a block comment that
167
+ consumed 12,198 characters and hid a real violation. Comment-strip order is
168
+ now load-bearing and asserted in both directions.
169
+
170
+ Stated plainly: 55 modules still build state paths by hand and remain on a
171
+ migration ledger. What changed permanently is that a 56th cannot be added.
172
+
173
+ ### The independent recall figure was wrong, and the correction is downward
174
+
175
+ Benchmark trees had been contaminated by the engine's own state files (220
176
+ polluted trees, 544 carrying `CWE-` strings). Fixing that coincided with a second
177
+ change — restricting matches to the files the advisory's fix commit touched — and
178
+ recall fell from a previously reported 33.6% to 12.7%.
179
+
180
+ Attributing that fall to the wrong cause would have been the same reasoning error
181
+ as the contamination, pointed the other way, so the runner now scores both ways
182
+ in one pass:
183
+
184
+ | | advisory-local (**the claim**) | wide (diagnostic) |
185
+ |---|---|---|
186
+ | recall | **12.7%** (14/110) | 33.6% (37/110) |
187
+ | precision | **50.0%** (14/28) | 50.0% (37/74) |
188
+ | F1 | **0.203** | 0.402 |
189
+
190
+ The wide figure is identical to the pre-purge number. **The contamination was
191
+ real and had to be fixed, but it was not inflating the measurement** — every one
192
+ of the 20.9 points comes from the benchmark becoming honest about *where* a
193
+ finding has to be. 12.7% is the true recall, and it always was.
194
+
195
+ It is published as a low number rather than quietly requalified, because the
196
+ point of owning the instrument is to be able to trust it when it disagrees.
197
+
198
+
3
199
  ## 0.134.0 — the loop closes, and the logic tier learns how to be wrong
4
200
 
5
201
  The remaining PRD epics. Two of them are new capability; the other two are the
@@ -23,6 +23,7 @@ import * as fs from 'node:fs';
23
23
  import * as path from 'node:path';
24
24
  import { verifyAuditLog } from '../src/mcp/audit.js';
25
25
 
26
+ import { statePath } from '../src/posture/state-dir.js';
26
27
  function args() {
27
28
  const a = process.argv.slice(2);
28
29
  const sub = a[0] || 'review';
@@ -48,7 +49,7 @@ function _parseDuration(s) {
48
49
  return n * u;
49
50
  }
50
51
 
51
- function _logPath(root) { return path.join(root, '.agentic-security', 'mcp-audit.log'); }
52
+ function _logPath(root) { return statePath(root, 'mcp-audit.log'); }
52
53
 
53
54
  function _readEntries(root) {
54
55
  const fp = _logPath(root);
@@ -26,6 +26,7 @@ import * as fs from 'node:fs';
26
26
  import * as path from 'node:path';
27
27
  import { measureConsistency, summarize } from '../src/llm-validator/consistency.js';
28
28
 
29
+ import { statePath } from '../src/posture/state-dir.js';
29
30
  function args() {
30
31
  const a = process.argv.slice(2);
31
32
  const out = { trials: 5, top: 5, json: false, root: process.cwd() };
@@ -40,7 +41,7 @@ function args() {
40
41
 
41
42
  async function main() {
42
43
  const opts = args();
43
- const scanFile = path.join(opts.root, '.agentic-security', 'last-scan.json');
44
+ const scanFile = statePath(opts.root, 'last-scan.json');
44
45
  if (!fs.existsSync(scanFile)) {
45
46
  console.error(`no last-scan.json at ${scanFile} — run a scan first`);
46
47
  process.exit(2);