@clear-capabilities/agentic-security-scanner 0.133.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.
- package/CHANGELOG.md +299 -0
- package/bin/agentic-security-audit.js +2 -1
- package/bin/agentic-security-consistency.js +2 -1
- package/bin/agentic-security.js +348 -40
- package/dist/113.index.js +6 -6
- package/dist/117.index.js +3 -1
- package/dist/178.index.js +1 -1
- package/dist/220.index.js +5 -3
- package/dist/238.index.js +4 -4
- package/dist/317.index.js +270 -0
- package/dist/384.index.js +1 -1
- package/dist/435.index.js +13 -7
- package/dist/499.index.js +86 -0
- package/dist/513.index.js +7 -3
- package/dist/526.index.js +6 -6
- package/dist/609.index.js +741 -0
- package/dist/637.index.js +1 -1
- package/dist/675.index.js +7 -5
- package/dist/839.index.js +4 -3
- package/dist/905.index.js +1173 -0
- package/dist/agentic-security.mjs +56 -56
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/dist/compliance-frameworks/ccpa.json +32 -0
- package/dist/compliance-frameworks/eu-ai-act.json +51 -0
- package/dist/compliance-frameworks/gdpr.json +45 -0
- package/dist/compliance-frameworks/hipaa-security-rule.json +56 -0
- package/dist/compliance-frameworks/nist-ai-600-1.json +51 -0
- package/dist/compliance-frameworks/nist-csf-2.json +73 -0
- package/dist/compliance-frameworks/nist-privacy-1-1.json +846 -0
- package/dist/compliance-frameworks/owasp-asvs-5.json +79 -0
- package/dist/compliance-frameworks/owasp-llm-top-10.json +69 -0
- package/package.json +14 -6
- package/src/badge.js +2 -1
- package/src/dataflow/cross-service-taint.js +2 -1
- package/src/dataflow/ifds-precise.js +6 -4
- package/src/dataflow/incremental.js +7 -5
- package/src/discovery/CLAUDE.md +48 -0
- package/src/discovery/confirm.js +47 -0
- package/src/discovery/disprove.js +79 -0
- package/src/discovery/hunter.js +116 -0
- package/src/discovery/index.js +331 -0
- package/src/discovery/judge.js +97 -0
- package/src/discovery/lenses.js +69 -0
- package/src/discovery/llm-invoke.js +120 -0
- package/src/discovery/memory.js +163 -0
- package/src/discovery/partition.js +92 -0
- package/src/engine.js +163 -7
- package/src/integrations/tickets.js +7 -6
- package/src/ir/ir-stats.js +1 -1
- package/src/ir/parser-cpp.js +1 -1
- package/src/leaderboard.js +3 -2
- package/src/llm-validator/consistency.js +6 -2
- package/src/llm-validator/index.js +30 -41
- package/src/llm-validator/providers.js +227 -0
- package/src/mcp/audit.js +2 -1
- package/src/mcp/tools.js +4 -3
- package/src/posture/CLAUDE.md +102 -1
- package/src/posture/accuracy-scorecard.js +59 -1
- package/src/posture/agents-memory.js +5 -3
- package/src/posture/auditor-walkthrough.js +9 -7
- package/src/posture/auth-posture-import.js +5 -4
- package/src/posture/autopilot.js +225 -0
- package/src/posture/comparison.js +181 -0
- package/src/posture/compliance-frameworks/nist-privacy-1-1.json +846 -0
- package/src/posture/compliance-policy.js +7 -9
- package/src/posture/custom-rules.js +7 -5
- package/src/posture/cve-alert-daemon.js +6 -5
- package/src/posture/dep-add-guard.js +2 -1
- package/src/posture/deterministic.js +3 -2
- package/src/posture/evidence-bundle.js +246 -0
- package/src/posture/execution-proof.js +25 -1
- package/src/posture/exploitability-probability.js +2 -1
- package/src/posture/feature-flags.js +3 -2
- package/src/posture/findings-memory.js +3 -3
- package/src/posture/fix-history.js +5 -2
- package/src/posture/fix-metrics.js +5 -5
- package/src/posture/fix-plan.js +2 -1
- package/src/posture/fleet.js +0 -0
- package/src/posture/grader-calibration.js +3 -4
- package/src/posture/intent-context.js +2 -1
- package/src/posture/learning.js +4 -3
- package/src/posture/license-attributions.js +5 -7
- package/src/posture/license-graph.js +2 -1
- package/src/posture/license-policy.js +2 -1
- package/src/posture/logic-claims.js +266 -0
- package/src/posture/model-rescan.js +4 -3
- package/src/posture/network-policy-import.js +3 -2
- package/src/posture/poc-inprocess.js +404 -2
- package/src/posture/pqc-migration-plan.js +7 -5
- package/src/posture/pr-augment.js +8 -5
- package/src/posture/privacy-framework.js +262 -0
- package/src/posture/proof-artifact.js +101 -0
- package/src/posture/prove-findings.js +28 -4
- package/src/posture/risk-dollars.js +2 -2
- package/src/posture/router.js +5 -4
- package/src/posture/ruleset-version.js +2 -2
- package/src/posture/runtime-correlation.js +2 -1
- package/src/posture/sbom-diff.js +12 -3
- package/src/posture/sca-policy.js +7 -4
- package/src/posture/scan-checkpoint.js +15 -0
- package/src/posture/state-dir.js +34 -0
- package/src/posture/telemetry-ingest.js +4 -3
- package/src/posture/threat-model-auto.js +4 -1
- package/src/posture/threat-model-grounding.js +11 -1
- package/src/posture/time-to-fix.js +3 -2
- package/src/posture/triage-memory.js +3 -2
- package/src/posture/waf-ingest.js +6 -5
- package/src/posture/watch-mode.js +4 -3
- package/src/report/index.js +9 -0
- package/src/sast/code-injection-multilang.js +29 -0
- package/src/sca/dep-confusion.js +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,304 @@
|
|
|
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
|
+
|
|
199
|
+
## 0.134.0 — the loop closes, and the logic tier learns how to be wrong
|
|
200
|
+
|
|
201
|
+
The remaining PRD epics. Two of them are new capability; the other two are the
|
|
202
|
+
same idea applied twice — a claim nobody can disagree with is the weakest thing
|
|
203
|
+
this engine emits, so both new tiers ship with the machinery to refute
|
|
204
|
+
themselves.
|
|
205
|
+
|
|
206
|
+
### Two more proof classes, and neither needed a running application
|
|
207
|
+
|
|
208
|
+
`sql-injection` and `path-traversal` join the three existing classes. The PRD
|
|
209
|
+
assumed both would wait on a running-app harness; they did not, because the
|
|
210
|
+
harness was never where the proof lived.
|
|
211
|
+
|
|
212
|
+
- **SQL injection is settled at the driver boundary.** Either the payload
|
|
213
|
+
arrives inside the query TEXT or it arrives as a bound parameter — the first
|
|
214
|
+
is the vulnerability by definition and the second is the fix by definition,
|
|
215
|
+
and no schema, rows or live server are needed to tell them apart. The PoC
|
|
216
|
+
stubs the driver with a recorder and writes the marker only when the payload
|
|
217
|
+
shows up inside something recognisably SQL. A parameterised query reaches
|
|
218
|
+
`proof-failed` by **execution**, not by reading the source.
|
|
219
|
+
- **Path traversal is settled by what comes back.** A sentinel is planted
|
|
220
|
+
outside the served directory and the marker is written only if its content —
|
|
221
|
+
or, for `sendFile`, its resolved path — comes back out of the handler. A
|
|
222
|
+
`basename` guard is refuted by running it.
|
|
223
|
+
|
|
224
|
+
Both directions are pinned by tests that execute in the real sandbox. Classes
|
|
225
|
+
still absent (IDOR, SSRF, XSS) are now documented as decisions with reasons
|
|
226
|
+
rather than gaps: a PoC built on invented application state proves something
|
|
227
|
+
about the invention.
|
|
228
|
+
|
|
229
|
+
Fixed along the way: the webhook PoC wrote its marker after a top-level `await`
|
|
230
|
+
guarded by an **unref'd** timer, so a handler that never replied let Node exit
|
|
231
|
+
with the promise pending and the marker check never ran. The test asserting
|
|
232
|
+
"no decision writes no marker" had been passing without reaching the line it
|
|
233
|
+
was testing. Timer is now ref'd and cleared, with a positive control asserting
|
|
234
|
+
the process reaches exit 0.
|
|
235
|
+
|
|
236
|
+
### The autonomous loop, wired to real stages
|
|
237
|
+
|
|
238
|
+
`scripts/autopilot.mjs` connects the loop to a real scan, a real sandboxed
|
|
239
|
+
exploit, a deterministic-then-model fix, and the real gate. End-to-end tests run
|
|
240
|
+
the whole thing against a live HTTP endpoint, including the one that matters: a
|
|
241
|
+
patch that changes the file, reads like a fix, and would satisfy any "did the
|
|
242
|
+
scanner go quiet?" check is **refused and never written**, because the exploit
|
|
243
|
+
still fires against it.
|
|
244
|
+
|
|
245
|
+
The CLI refuses to start without a confinement backend (the verdict requires
|
|
246
|
+
executing something) and refuses a dirty git tree by default (the test leg
|
|
247
|
+
writes the candidate patch to disk and restores it in a `finally`; a clean tree
|
|
248
|
+
is what makes a crash recoverable). A verified fix reached with no test runner
|
|
249
|
+
detected is counted and reported separately — the exploit stopped firing, but
|
|
250
|
+
nothing checked the application still works.
|
|
251
|
+
|
|
252
|
+
### The business-logic tier learns how to be wrong
|
|
253
|
+
|
|
254
|
+
The deterministic half already existed. The reviewing agent's half was prose:
|
|
255
|
+
it asserted that a handler lets one user act on another's resource, and nothing
|
|
256
|
+
in the finding gave a second party anything to disagree with. It was the only
|
|
257
|
+
tier in this engine with no way to be wrong.
|
|
258
|
+
|
|
259
|
+
`posture/logic-claims.js` adds three offline lenses that can refute one —
|
|
260
|
+
citation (the file exists and the line is inside it), quotation (the quoted
|
|
261
|
+
snippet is at the cited line), corroboration (a "no authentication" claim
|
|
262
|
+
against a handler that plainly authenticates). Verdicts go through the existing
|
|
263
|
+
producer/verifier separation, so a lens can never vote on a claim it produced;
|
|
264
|
+
that is why the lenses are deterministic code and not another prompt. Refuted
|
|
265
|
+
claims are quarantined, never deleted and never severity-touched.
|
|
266
|
+
|
|
267
|
+
### A comparison harness that ships no opinion about who the competition is
|
|
268
|
+
|
|
269
|
+
`posture/comparison.js` + `scripts/comparison.mjs` score this engine
|
|
270
|
+
head-to-head against participants **the operator supplies**. The repository
|
|
271
|
+
ships the harness and the answer key and names no tool — a test enforces that.
|
|
272
|
+
|
|
273
|
+
Two properties are the entire module. Every rate is computed over the
|
|
274
|
+
**intersection** of corpus entries *all* participants completed, because a tool
|
|
275
|
+
that crashed on the forty hardest entries and was scored over the remaining
|
|
276
|
+
hundred and seventy looks like it beat one that completed everything, and the
|
|
277
|
+
difference is invisible in the output. And an entry a participant could not run
|
|
278
|
+
is **unscored**, never counted as a miss: counting a crash as a false negative
|
|
279
|
+
penalises a tool for a harness problem, counting it as a pass rewards it for
|
|
280
|
+
one. Matching is CWE-only so nobody is scored on this engine's vocabulary.
|
|
281
|
+
|
|
282
|
+
No comparison figures are published in this repository. Running other vendors'
|
|
283
|
+
tools and publishing the numbers is the operator's call, not the harness's.
|
|
284
|
+
|
|
285
|
+
### The suppression pragma never worked
|
|
286
|
+
|
|
287
|
+
Found while suppressing a false positive in this release's own new code.
|
|
288
|
+
`// agentic-security-ignore: <rule-id>` is documented in `CLAUDE.md` and
|
|
289
|
+
`pr-comment.js` tells every reviewer to use it — and **nothing implemented it**.
|
|
290
|
+
It has been advertised and inert. A dead suppression mechanism is worse than an
|
|
291
|
+
absent one: the developer writes the pragma, sees the finding again, and
|
|
292
|
+
concludes the scanner is noisy rather than that the pragma is dead.
|
|
293
|
+
|
|
294
|
+
Now implemented in `engine.js`, applied after dedupe and after every cross-file
|
|
295
|
+
pass so it covers a finding whichever analysis produced it. Line-scoped, matched
|
|
296
|
+
against the finding's id / vuln / CWE / family, and **logged** to the same
|
|
297
|
+
ledger custom rules use so `--include-suppressed` can show it. Every test
|
|
298
|
+
carries a positive control — the same file without the pragma must still
|
|
299
|
+
produce the finding, or "0 findings" would prove the suppression works and
|
|
300
|
+
equally prove the detector stopped firing.
|
|
301
|
+
|
|
3
302
|
## 0.133.0 — two ways findings could be silently deleted, both closed
|
|
4
303
|
|
|
5
304
|
Four rounds of adversarial premortem against this repository's own artifacts.
|
|
@@ -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
|
|
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 =
|
|
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);
|