@clear-capabilities/agentic-security-scanner 0.128.1 → 0.132.0
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 +223 -0
- package/bin/agentic-security.js +52 -2
- package/dist/11.index.js +2 -2
- package/dist/113.index.js +498 -7
- package/dist/178.index.js +1 -1
- package/dist/207.index.js +220 -0
- package/dist/238.index.js +218 -0
- package/dist/259.index.js +975 -0
- package/dist/384.index.js +1 -1
- package/dist/415.index.js +1 -1
- package/dist/435.index.js +4 -4
- package/dist/526.index.js +844 -0
- package/dist/637.index.js +1 -1
- package/dist/830.index.js +1 -1
- package/dist/agentic-security.mjs +106 -194
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/package.json +33 -17
- package/src/dataflow/CLAUDE.md +4 -1
- package/src/dataflow/async-sequencing.js +8 -3
- package/src/dataflow/catalog.js +278 -11
- package/src/dataflow/cross-repo.js +1 -1
- package/src/dataflow/cross-service-taint.js +1 -1
- package/src/dataflow/engine.js +182 -61
- package/src/dataflow/ifds.js +10 -5
- package/src/dataflow/index.js +15 -3
- package/src/dataflow/points-to.js +8 -2
- package/src/dataflow/proof-gate.js +7 -0
- package/src/dataflow/sanitizer-gate.js +89 -0
- package/src/dataflow/tabulation.js +14 -3
- package/src/engine.js +170 -7
- package/src/integrations/index.js +1 -1
- package/src/ir/CLAUDE.md +49 -4
- package/src/ir/call-sites.js +66 -0
- package/src/ir/callgraph.js +174 -7
- package/src/ir/class-hierarchy.js +22 -2
- package/src/ir/index.js +138 -51
- package/src/ir/ir-stats.js +126 -0
- package/src/ir/parser-cpp.js +829 -0
- package/src/ir/parser-cs.js +4 -1
- package/src/ir/parser-go.js +4 -1
- package/src/ir/parser-js.js +13 -1
- package/src/ir/parser-kt.js +4 -1
- package/src/ir/parser-php.js +10 -3
- package/src/ir/parser-py-cst.js +62 -10
- package/src/ir/tree-sitter-loader.js +13 -1
- package/src/llm-validator/index.js +9 -2
- package/src/llm-validator/redact.js +157 -0
- package/src/mcp/tools.js +2 -2
- package/src/posture/CLAUDE.md +193 -1
- package/src/posture/accuracy-scorecard.js +317 -0
- package/src/posture/api-contract.js +1 -1
- package/src/posture/attestation.js +202 -0
- package/src/posture/auditor-walkthrough.js +12 -3
- package/src/posture/compliance-policy.js +1 -1
- package/src/posture/corpus-enroll.js +303 -0
- package/src/posture/corpus-match.js +52 -0
- package/src/posture/cross-lang-openapi.js +1 -1
- package/src/posture/custom-rules.js +3 -3
- package/src/posture/execution-proof.js +92 -0
- package/src/posture/exploitability-probability.js +1 -1
- package/src/posture/falsification.js +45 -1
- package/src/posture/fix-metrics.js +197 -0
- package/src/posture/fix-verify.js +129 -2
- package/src/posture/license-policy.js +1 -1
- package/src/posture/profile.js +1 -1
- package/src/posture/proof-tier.js +33 -0
- package/src/posture/relevance.js +379 -0
- package/src/posture/root-cause-sweep.js +0 -0
- package/src/posture/rule-overrides.js +1 -1
- package/src/posture/sca-policy.js +1 -1
- package/src/posture/scan-checkpoint.js +277 -0
- package/src/posture/suppressions.js +1 -1
- package/src/posture/test-runner.js +147 -0
- package/src/posture/verification-separation.js +131 -0
- package/src/report/index.js +11 -0
- package/src/runScan.js +5 -7
- package/src/sandbox/CLAUDE.md +340 -0
- package/src/sandbox/backend-disabled.js +14 -0
- package/src/sandbox/backend-namespace.js +335 -0
- package/src/sandbox/backend-userspace.js +83 -0
- package/src/sandbox/capabilities.js +181 -0
- package/src/sandbox/index.js +30 -0
- package/src/sandbox/limits.js +63 -0
- package/src/sandbox/result.js +104 -0
- package/src/sca/dep-confusion.js +1 -1
- package/src/util/glob.js +173 -0
- package/src/util/yaml.js +24 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,228 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.132.0 — a proven exploit becomes a permanent regression test
|
|
4
|
+
|
|
5
|
+
The corpus stops being only a regression net and starts being fed by the engine
|
|
6
|
+
itself. Every claim below was verified by a command in the session that made it.
|
|
7
|
+
|
|
8
|
+
- **Execution-proven findings auto-enrol as corpus entries.** This was R2's
|
|
9
|
+
differentiator and the last missing piece of it. A finding whose
|
|
10
|
+
proof-of-concept RAN in the sandbox and produced the predicted effect can now
|
|
11
|
+
be turned into a permanent `pre:TP post:TN` corpus entry, so every exploit
|
|
12
|
+
proved once is defended against its own regression forever. Demonstrated end
|
|
13
|
+
to end rather than in a unit test: a real command-injection finding was proved
|
|
14
|
+
in the sandbox, its fix supplied the `post/` tree, and the entry was enrolled
|
|
15
|
+
and scored by the real gate — **the baseline moved 199 → 200**. Proven in the
|
|
16
|
+
other direction too: with the entry's `pre/` neutered the gate reports
|
|
17
|
+
`REGRESSED (1)` and exits non-zero.
|
|
18
|
+
- **Nothing reaches the corpus unscored.** The entry is built in a temporary
|
|
19
|
+
directory, `pre/` and `post/` are scanned, and it is moved into the corpus
|
|
20
|
+
only on `pre:TP post:TN`. There is no force flag, and the scoring function is
|
|
21
|
+
unexported so no caller can score by one route and write by another — the
|
|
22
|
+
v0.106.0 mistake (fixtures committed without verifying they score) is the one
|
|
23
|
+
thing an automated writer must never industrialise. Scoring itself now lives
|
|
24
|
+
in one module shared by the enroller and the corpus runner, so the two cannot
|
|
25
|
+
drift apart; the refactor was proven behaviour-preserving at 199/199, no drift.
|
|
26
|
+
New entries land in `capability/`, never the CI-gated `regression/` tier — a
|
|
27
|
+
machine must not decide what blocks everyone's build.
|
|
28
|
+
- **`verify_fix` now runs the proof-of-concept against the candidate patch.** A
|
|
29
|
+
re-scan only proves the DETECTOR stopped firing, which a cosmetic edit
|
|
30
|
+
achieves; re-running the exploit proves the hole is shut. Still-exploitable
|
|
31
|
+
after the patch is a hard failure. Deliberately asymmetric: a PoC that could
|
|
32
|
+
not run is recorded `inconclusive` and excluded from the verdict, because
|
|
33
|
+
reading "could not prove it" as "fixed" is exactly the false confidence this
|
|
34
|
+
leg exists to prevent. Both directions were executed against the real sandbox.
|
|
35
|
+
- **Time-to-validated-fix is now measured and reported**, closing R5. Every
|
|
36
|
+
verification stage is timed and appended to a per-project log, and the
|
|
37
|
+
reported distribution is deliberately hard to flatter: failed attempts never
|
|
38
|
+
enter the validated median (they short-circuit, so blending them makes a worse
|
|
39
|
+
pipeline look faster), "no test suite to run" is bucketed apart from "tests
|
|
40
|
+
passed", and per-stage timings come from validated runs only. Percentiles are
|
|
41
|
+
nearest-rank — every figure shown is a duration some run actually took — and
|
|
42
|
+
are flagged unreliable below n=10 rather than quoted as settled.
|
|
43
|
+
- **Cross-machine determinism now has a gate behind it.** A dependency-free
|
|
44
|
+
fixture is scanned on two operating systems and the run-attestation digests
|
|
45
|
+
must match. The comparator refuses every route to a meaningless pass: fewer
|
|
46
|
+
than two attestations, two runs from the same platform, a zero-finding digest,
|
|
47
|
+
mismatched canonicalisations, unparseable input. Each refusal was fired
|
|
48
|
+
deliberately and confirmed to exit non-zero.
|
|
49
|
+
|
|
50
|
+
Honest limits, stated rather than implied:
|
|
51
|
+
|
|
52
|
+
- The enrolment loop is **not yet automatic end to end**. Nothing in the scan
|
|
53
|
+
pipeline attaches a proof-of-concept to a finding or promotes proof tiers, so
|
|
54
|
+
a scan never produces an `execution-proven` finding on its own; PoCs come from
|
|
55
|
+
the generator and are proved at enrol time. Automatic attachment during a scan
|
|
56
|
+
is the remaining work.
|
|
57
|
+
- The cross-machine determinism jobs have **not yet run in CI**, so no second
|
|
58
|
+
machine has actually been compared. Same-machine repeatability is verified.
|
|
59
|
+
The attestation's own "does not prove" statement is unchanged and stays
|
|
60
|
+
correct either way: one attestation is one run on one machine.
|
|
61
|
+
- The sandbox wall-clock timeout stops the **direct child, not the process
|
|
62
|
+
tree**, on both backends. No test asserts a backgrounded grandchild dies.
|
|
63
|
+
|
|
64
|
+
Also in this release:
|
|
65
|
+
|
|
66
|
+
- **Documentation corrected against evidence.** The roadmap and the sandbox and
|
|
67
|
+
posture guides still described the kernel-namespace backend as "implemented,
|
|
68
|
+
unverified" after CI had already proved otherwise; they now cite the run
|
|
69
|
+
(Ubuntu 24.04, kernel 6.17.0-1020-azure, 41 assertions, 0 failures, all eight
|
|
70
|
+
escape cases). The corpus entry count was three tiers and 14 entries out of
|
|
71
|
+
date.
|
|
72
|
+
- **Dependency advisories cleared** in the extension tree and `@types/node`
|
|
73
|
+
brought current in both trees.
|
|
74
|
+
|
|
75
|
+
## 0.131.0 — decorated files stop vanishing; the Linux sandbox is verified
|
|
76
|
+
|
|
77
|
+
Two correctness fixes, both found by pushing measurement further than the last
|
|
78
|
+
release did.
|
|
79
|
+
|
|
80
|
+
- **Decorator syntax is now accepted by the IR frontend.** The parser was told
|
|
81
|
+
about TypeScript and JSX but not decorators, so it rejected the ENTIRE file on
|
|
82
|
+
the first `@tracked` it met — no error, no warning, just findings that never
|
|
83
|
+
existed. Measured on a live third-party target: **4,023 of 4,271 JavaScript
|
|
84
|
+
files parsed before, 4,262 after** — 239 files, roughly 6% of that project,
|
|
85
|
+
were invisible to the scanner and are now analysed. `decorators-legacy` plus
|
|
86
|
+
`decoratorAutoAccessors` was chosen over the modern `decorators` variant
|
|
87
|
+
because the modern one cannot parse TypeScript parameter decorators; it would
|
|
88
|
+
have swapped one blind spot for another. No new dependency. Guarded by a
|
|
89
|
+
regression test proven in both directions — 5/5 pass with the fix, 4 fail
|
|
90
|
+
without it.
|
|
91
|
+
- **The kernel-namespace sandbox now confines writes, and is verified.** It
|
|
92
|
+
previously confined network only and had never been executed. It now enters a
|
|
93
|
+
private mount namespace, rebinds every mount read-only except the sandbox
|
|
94
|
+
root, and drops the entire capability set before exec so a payload cannot
|
|
95
|
+
rebind the tree writable. A CI job relaxes the host restriction on
|
|
96
|
+
unprivileged user namespaces and runs the real escape suite: a write outside
|
|
97
|
+
the root is blocked with no file created, and outbound egress is blocked. The
|
|
98
|
+
verifier exits non-zero unless that suite actually RAN, so a skip can never be
|
|
99
|
+
read as a pass. Execution-proof on Linux no longer rests on an unexercised
|
|
100
|
+
backend.
|
|
101
|
+
|
|
102
|
+
Also in this release:
|
|
103
|
+
|
|
104
|
+
- **A quadratic blowup in the root-cause sweep**, which ran on every full scan:
|
|
105
|
+
it re-split the whole corpus into lines once per finding and kept a record per
|
|
106
|
+
finding x matching line. The largest benchmark corpus went from dying after
|
|
107
|
+
55+ minutes to **exit 0, 933 MB peak, 330s** — smaller and roughly ten times
|
|
108
|
+
faster, with metrics identical before and after.
|
|
109
|
+
- **Benchmark corpora are fetched by pinned commit** instead of guessing a clone
|
|
110
|
+
depth; six corpora had never been scoreable because their pin sat more than
|
|
111
|
+
100 commits back.
|
|
112
|
+
- **A pre-push gate** runs bundle integrity, the full suite, the corpus baseline
|
|
113
|
+
and the precision baseline before anything leaves the machine, plus branch
|
|
114
|
+
protection requiring green checks to merge.
|
|
115
|
+
- **A dependency-currency release gate**: any advisory at moderate or above
|
|
116
|
+
fails with no opt-out; anything behind latest fails unless explicitly held
|
|
117
|
+
with a stated reason and a review date that expires.
|
|
118
|
+
- **The glob dependency was replaced by the platform built-in** — 92 to 73
|
|
119
|
+
production packages — after a differential over 320 trees and 1,419,229 paths
|
|
120
|
+
showed zero differences.
|
|
121
|
+
|
|
122
|
+
`npm test` 2072/0; cve-replay 199/199; self-scan no drift; proof corpus
|
|
123
|
+
ghost/superset/godot all 100% parse coverage.
|
|
124
|
+
|
|
125
|
+
## 0.130.0 — the roadmap's first ten: provable security over orchestration parity
|
|
126
|
+
|
|
127
|
+
A capability roadmap (`docs/ROADMAP.md`) plus its first ten items, derived from a
|
|
128
|
+
survey of the current agentic security-review field. The strategic call: that
|
|
129
|
+
field cannot state a false-positive rate or prove it did not regress, because its
|
|
130
|
+
core is a model call. This project's core is a deterministic engine behind three
|
|
131
|
+
gates, so the work doubles down on provable, measurable, reproducible.
|
|
132
|
+
|
|
133
|
+
- **Execution sandbox** (`src/sandbox/`) — fail-closed confined execution. Verified
|
|
134
|
+
by execution: writes outside the sandbox root blocked, network blocked, wall-clock
|
|
135
|
+
overrun terminated, benign work still succeeds. With no confinement primitive
|
|
136
|
+
available, execution is REFUSED, never run unconfined.
|
|
137
|
+
- **Execution-verified findings** (`posture/execution-proof.js`) — a finding can be
|
|
138
|
+
promoted to `execution-proven` by running its proof-of-concept in the sandbox.
|
|
139
|
+
Proof is a marker file, never an exit code, because the sandbox cannot reliably
|
|
140
|
+
distinguish a denied run from a clean exit. `proof-failed` is a triage signal,
|
|
141
|
+
NOT a false-positive verdict.
|
|
142
|
+
- **Published accuracy scorecard** (`npm run scorecard`) — detection and correct-
|
|
143
|
+
silence rates sliced by language and CWE, every figure with its denominator,
|
|
144
|
+
regeneration-stable. F1 deliberately omitted: no labelled real-world population
|
|
145
|
+
exists to measure precision over, and the document says so.
|
|
146
|
+
- **Determinism attestation** (`posture/attestation.js`) — an order-independent
|
|
147
|
+
signed digest. Cross-machine reproducibility is explicitly NOT claimed.
|
|
148
|
+
- **Fixes must pass your tests** (`posture/test-runner.js`) — `verifyFix` previously
|
|
149
|
+
proved only that the finding disappeared, which deleting the feature also
|
|
150
|
+
achieves. It now runs the project's own suite, and says so when the suite ran
|
|
151
|
+
against unpatched code.
|
|
152
|
+
- **Relevance ranking** (`posture/relevance.js`) — re-ranks by entry-point
|
|
153
|
+
reachability. Recall-preserving: nothing deleted, severity never touched,
|
|
154
|
+
`unreachable` only on positive evidence.
|
|
155
|
+
- **Enforced verification separation** (`posture/verification-separation.js`) — a
|
|
156
|
+
verifier cannot rubber-stamp its own finding.
|
|
157
|
+
- **Resumable scans** (`posture/scan-checkpoint.js`) — opt-in via
|
|
158
|
+
`AGENTIC_SECURITY_RESUME=1`, crash-safe, conservatively invalidated.
|
|
159
|
+
- **Secret redaction** (`llm-validator/redact.js`) — credentials removed before any
|
|
160
|
+
code leaves the machine; ordinary code passes through byte-unchanged.
|
|
161
|
+
|
|
162
|
+
Also: dependencies updated to latest (Babel 7→8 with the removed preset options
|
|
163
|
+
migrated, js-yaml 4→5 with an empty-input shim). One dependency deliberately held
|
|
164
|
+
back: bumping the grammar runtime silently drops all six long-tail language
|
|
165
|
+
grammars, so it stays pinned and the reason is documented.
|
|
166
|
+
|
|
167
|
+
`npm test` 1989/0; cve-replay 199/199; self-scan no drift.
|
|
168
|
+
|
|
169
|
+
## 0.129.0 — closing two taint-engine recall gaps
|
|
170
|
+
|
|
171
|
+
Two defects were found by execution on the merged tree, each silencing real findings across
|
|
172
|
+
every supported language. Both are now fixed, measured, and gated.
|
|
173
|
+
|
|
174
|
+
- **Sinks are matched on assignment right-hand sides** (`dataflow/engine.js`). The engine only
|
|
175
|
+
ever sink-matched in statement position, so `db.query(tainted)` was reported while
|
|
176
|
+
`const rows = db.query(tainted)` was silent — in every language. The sink-matching logic is
|
|
177
|
+
now extracted into shared helpers called from both `case 'call'` and `case 'assign'` rather
|
|
178
|
+
than duplicated, and the pre-existing statement-position path is unchanged (measured control:
|
|
179
|
+
`1/1` before and after; assignment position `0/0 -> 1/1`).
|
|
180
|
+
- **`match.type:'global'` catalog entries are indexed and reachable** (`dataflow/catalog.js`).
|
|
181
|
+
All 10 global entries were unreachable from `matchSource()` — including `$_GET`/`$_POST`/
|
|
182
|
+
`$_REQUEST`, the canonical PHP taint sources, in a language that already had interprocedural
|
|
183
|
+
analysis. A new `GLOBAL_INDEX`, plus lookup-side sigil normalization (`_globalKey()`) so PHP's
|
|
184
|
+
`$` prefix matches sigil-free catalog keys, takes catalog reachability `0/10 -> 10/10` with
|
|
185
|
+
language scoping preserved.
|
|
186
|
+
- **Seven self-scan false positives eliminated at source.** Raising sink recall exposed
|
|
187
|
+
pre-existing catalog imprecision: `py-yaml-load`/`py-pickle-load` matched bare callee `load`
|
|
188
|
+
with no receiver constraint, so ordinary `json.load(fh)` was flagged as unsafe deserialization.
|
|
189
|
+
Each finding was inspected individually and all seven were false positives; the entries are now
|
|
190
|
+
pinned to their receiver. **Nothing was baselined** — `bench/self-scan/BASELINE.json` is
|
|
191
|
+
unchanged and the gate is green on the source fix.
|
|
192
|
+
- **New**: `bench/engine-recall` before/after harness (`npm run bench:engine-recall`) and
|
|
193
|
+
`bench/engine-recall/RESULTS.md`, the full measurement record including what the fixes cost.
|
|
194
|
+
- **Corpus 197 -> 199**: two deep-tier entries, each verified missed-before / found-after against
|
|
195
|
+
its specific fix.
|
|
196
|
+
|
|
197
|
+
Known trade, recorded rather than papered over: pinning the receiver drops `import yaml as y;
|
|
198
|
+
y.load(f)` and `from yaml import load; load(x)`, which are now covered at no layer. A corpus guard
|
|
199
|
+
was attempted and deliberately withheld because it would score `pre:TN`; closing it needs
|
|
200
|
+
import-alias resolution in the Python IR. Separately, `10/10` is catalog reachability, not
|
|
201
|
+
end-to-end recall — only PHP is proven end to end; Ruby's deep engine does not complete those
|
|
202
|
+
flows (pre-existing). Both are documented in `RESULTS.md` §3 and §8.
|
|
203
|
+
|
|
204
|
+
`npm test` 1854/0; cve-replay 199/199; self-scan no drift.
|
|
205
|
+
|
|
206
|
+
## 0.128.2 — compliance attestation accuracy + quieter self-scans
|
|
207
|
+
|
|
208
|
+
Two fixes surfaced while dogfooding the compliance flow on this repo:
|
|
209
|
+
|
|
210
|
+
- **Fixed a compliance-attestation path bug** (`posture/auditor-walkthrough.js`). Three
|
|
211
|
+
evidence checks (`mcp-tools`, `security-fixer`, `pre-edit-bodyguard`) carried a literal
|
|
212
|
+
`.../` placeholder path that `path.join(scanRoot, STATE, '.../x')` could never resolve, so
|
|
213
|
+
they read **"not present" for every project** — falsely dragging OWASP LLM08/LLM09 (and any
|
|
214
|
+
framework mapping to those modules) to "manual/not-present". A `.../` sentinel now resolves
|
|
215
|
+
against the scan root itself. On a self-attestation this flips LLM09 → satisfied and makes
|
|
216
|
+
LLM08 honestly partial.
|
|
217
|
+
- **Repo `ignorePaths` for meaningful self-scans** (`.agentic-security/rules.yml`). Added
|
|
218
|
+
`bench/**` and `scanner/test/fixtures/**` so a repo-root `/scan` no longer counts the ~600
|
|
219
|
+
intentionally-vulnerable benchmark corpora and test fixtures as findings. Safe: `rules.yml`
|
|
220
|
+
is loaded from the exact scan root only, so the cve-replay runner (which scans each
|
|
221
|
+
pre/post fixture as its own root) and the unit tests are unaffected — corpus gate stays
|
|
222
|
+
185/185.
|
|
223
|
+
|
|
224
|
+
`npm test` 1695/0; cve-replay 185/185.
|
|
225
|
+
|
|
3
226
|
## 0.128.1 — patch dependency vulnerabilities (11 Dependabot alerts → 0)
|
|
4
227
|
|
|
5
228
|
Security maintenance. Cleared all 11 open Dependabot alerts by updating the two lockfiles to
|
package/bin/agentic-security.js
CHANGED
|
@@ -28,7 +28,7 @@ import { syncTickets } from '../src/integrations/tickets.js';
|
|
|
28
28
|
import { decide as decideNextAction, explain as explainDecision } from '../src/posture/router.js';
|
|
29
29
|
import * as triage from '../src/posture/triage.js';
|
|
30
30
|
import { buildSlackDigest, buildDiscordDigest, postWebhook, buildJiraIssue, buildPrComment, buildSiemEvent, loadIntegrationConfig } from '../src/integrations/index.js';
|
|
31
|
-
import
|
|
31
|
+
import { globFiles } from '../src/util/glob.js';
|
|
32
32
|
|
|
33
33
|
// last-scan.json integrity helpers — implementation in posture/integrity.js
|
|
34
34
|
// so the MCP server tools can share verification.
|
|
@@ -274,6 +274,21 @@ function renderV3Blocks(scan, flags) {
|
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
// Always-on machine output (R2). Vibecoder gets JSON only; pro gets JSON+SARIF+CSV.
|
|
277
|
+
// SHA-256 of the running bundle, read from the sidecar `npm run build` emits
|
|
278
|
+
// NEXT TO the bundle. Running from source (bin/ + src/) yields 'unavailable'
|
|
279
|
+
// rather than the checkout's dist hash: src and a previously-built dist can
|
|
280
|
+
// disagree, and attesting a bundle that did not produce this run would be a
|
|
281
|
+
// false claim.
|
|
282
|
+
function _bundleSha() {
|
|
283
|
+
const here = path.dirname(new URL(import.meta.url).pathname);
|
|
284
|
+
try {
|
|
285
|
+
const raw = fs.readFileSync(path.join(here, 'agentic-security.mjs.sha256'), 'utf8').trim();
|
|
286
|
+
const m = /^([0-9a-f]{64})\b/.exec(raw);
|
|
287
|
+
if (m) return m[1];
|
|
288
|
+
} catch { /* not running from the bundle */ }
|
|
289
|
+
return 'unavailable';
|
|
290
|
+
}
|
|
291
|
+
|
|
277
292
|
async function writeMachineOutput(targetAbs, scan, meta, profile) {
|
|
278
293
|
const stateDir = path.join(targetAbs, '.agentic-security');
|
|
279
294
|
const { isSafeStateDir: _isSafe } = await import('../src/posture/state-dir.js');
|
|
@@ -468,7 +483,7 @@ async function cmdScan(args) {
|
|
|
468
483
|
// fingerprint and tracking provenance via sources[].
|
|
469
484
|
if (args.flags['ingest-sarif']) {
|
|
470
485
|
const glob = args.flags['ingest-sarif'];
|
|
471
|
-
const paths = await
|
|
486
|
+
const paths = await globFiles(glob);
|
|
472
487
|
if (paths.length) {
|
|
473
488
|
const r = ingestAndMerge(scan, paths);
|
|
474
489
|
if (process.stderr.isTTY) process.stderr.write(`[ingest] merged ${r.merged} / added ${r.added} findings from ${paths.length} SARIF file(s)\n`);
|
|
@@ -553,6 +568,24 @@ async function cmdScan(args) {
|
|
|
553
568
|
// Deterministic post-process: stable-sort findings + zero out timing.
|
|
554
569
|
if (isDeterministic()) makeDeterministic(scan, meta);
|
|
555
570
|
|
|
571
|
+
// R4 — determinism as a contract. Bind the PUBLISHED finding set (the same
|
|
572
|
+
// normalization every report format emits) to the engine version, ruleset
|
|
573
|
+
// version and bundle hash that produced it, via an order-independent digest.
|
|
574
|
+
// Runs after every filter above so it attests what actually ships. Metadata
|
|
575
|
+
// only — a failure here must never fail a scan.
|
|
576
|
+
try {
|
|
577
|
+
const { computeRunAttestation } = await import('../src/posture/attestation.js');
|
|
578
|
+
const { effectiveVersion } = await import('../src/posture/ruleset-version.js');
|
|
579
|
+
scan.attestation = computeRunAttestation({
|
|
580
|
+
findings: normalizeFindings(scan),
|
|
581
|
+
engineVersion: PKG_VERSION,
|
|
582
|
+
rulesetVersion: effectiveVersion(targetAbs).version,
|
|
583
|
+
bundleSha: _bundleSha(),
|
|
584
|
+
root: targetAbs,
|
|
585
|
+
sign: true,
|
|
586
|
+
});
|
|
587
|
+
} catch { /* attestation is metadata; never fail a scan over it */ }
|
|
588
|
+
|
|
556
589
|
// R2: Always emit machine-readable artifacts to .agentic-security/.
|
|
557
590
|
await writeMachineOutput(targetAbs, scan, meta, profile);
|
|
558
591
|
|
|
@@ -622,6 +655,23 @@ async function cmdScan(args) {
|
|
|
622
655
|
if (sla) process.stderr.write(`⏰ agentic-security: ${sla}\n`);
|
|
623
656
|
}
|
|
624
657
|
} catch { /* MTTR is best-effort — never block a scan write */ }
|
|
658
|
+
|
|
659
|
+
// R5 — report the observed time-to-validated-fix distribution from the
|
|
660
|
+
// fix attempts recorded by `verifyFix`. This is measurement, not
|
|
661
|
+
// estimation: it says nothing until fixes have actually been verified in
|
|
662
|
+
// this project, and it prints nothing when there is nothing measured.
|
|
663
|
+
// Skipped under --deterministic for the same reason MTTR is: the
|
|
664
|
+
// durations are wall-clock and would break byte-identical state.
|
|
665
|
+
try {
|
|
666
|
+
const { fixDurationReport, renderFixDurationSummary } = await import('../src/posture/fix-metrics.js');
|
|
667
|
+
const fixMetrics = fixDurationReport(path.resolve(target));
|
|
668
|
+
if (fixMetrics.attempts > 0) {
|
|
669
|
+
persistedScan.fixMetrics = fixMetrics;
|
|
670
|
+
const isJsonFmt = format === 'json' || format === 'sarif' || format === 'cyclonedx' || format === 'sbom' || format === 'spdx' || format === 'vex' || format === 'openvex' || format === 'pbom' || format === 'aibom';
|
|
671
|
+
const line = renderFixDurationSummary(fixMetrics);
|
|
672
|
+
if (!isJsonFmt && line) process.stderr.write(`🔧 agentic-security: ${line}\n`);
|
|
673
|
+
}
|
|
674
|
+
} catch { /* fix metrics are best-effort — never block a scan write */ }
|
|
625
675
|
}
|
|
626
676
|
// #22 — live-secret validation (opt-in, offline-degrading). Label each
|
|
627
677
|
// detected secret live | dead | unknown via a read-only provider "whoami".
|
package/dist/11.index.js
CHANGED
|
@@ -21,8 +21,8 @@ var external_node_child_process_ = __webpack_require__(1421);
|
|
|
21
21
|
var external_node_fs_ = __webpack_require__(3024);
|
|
22
22
|
// EXTERNAL MODULE: external "node:path"
|
|
23
23
|
var external_node_path_ = __webpack_require__(6760);
|
|
24
|
-
// EXTERNAL MODULE: ./src/engine.js +
|
|
25
|
-
var engine = __webpack_require__(
|
|
24
|
+
// EXTERNAL MODULE: ./src/engine.js + 592 modules
|
|
25
|
+
var engine = __webpack_require__(9408);
|
|
26
26
|
;// CONCATENATED MODULE: ./src/posture/fix-honesty-gate.js
|
|
27
27
|
// Deterministic honesty gates on fix / finding output (#7).
|
|
28
28
|
//
|