@clear-capabilities/agentic-security-scanner 0.184.0 → 0.186.1
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 +37 -0
- package/bin/agentic-security.js +111 -1
- package/dist/1128.index.js +912 -0
- package/dist/1665.index.js +828 -0
- package/dist/2584.index.js +1 -1
- package/dist/4171.index.js +2 -2
- package/dist/5752.index.js +466 -0
- package/dist/7405.index.js +1 -1
- package/dist/8158.index.js +1713 -0
- package/dist/8798.index.js +1 -1
- package/dist/9382.index.js +2 -2
- package/dist/9579.index.js +1 -1
- package/dist/agentic-security.mjs +29 -29
- package/dist/agentic-security.mjs.sha256 +1 -1
- package/dist/{dad58d1113a12c23a142.json → ca62ee93079be4c24eee.json} +29 -12
- package/dist/frontend/src/shell.js +12 -2
- package/package.json +29 -12
- package/src/dataflow/CLAUDE.md +46 -1
- package/src/dataflow/access-paths.js +12 -2
- package/src/dataflow/backward.js +42 -7
- package/src/dataflow/cross-service-taint.js +83 -1
- package/src/dataflow/demand-query.js +1168 -0
- package/src/dataflow/dep-fingerprint.js +718 -0
- package/src/dataflow/engine.js +48 -6
- package/src/dataflow/exploit-prover.js +6 -2
- package/src/dataflow/ifds-precise.js +22 -0
- package/src/dataflow/incremental-replay.js +118 -0
- package/src/dataflow/incremental-session.js +257 -0
- package/src/dataflow/incremental.js +237 -35
- package/src/dataflow/index.js +25 -94
- package/src/dataflow/ir-cache.js +268 -0
- package/src/dataflow/privacy-taint.js +7 -0
- package/src/dataflow/summaries.js +72 -6
- package/src/dataflow/tabulation.js +80 -24
- package/src/engine.js +113 -8
- package/src/graph/CLAUDE.md +31 -0
- package/src/graph/adapters/artifact.js +81 -0
- package/src/graph/adapters/crossrepo.js +75 -0
- package/src/graph/adapters/dataflow.js +76 -0
- package/src/graph/adapters/provenance.js +92 -0
- package/src/graph/adapters/semantic.js +89 -0
- package/src/graph/crosswalk-builders.js +152 -0
- package/src/graph/crosswalk.js +146 -0
- package/src/graph/index.js +15 -0
- package/src/graph/observation.js +57 -0
- package/src/graph/refs.js +105 -0
- package/src/graph/registry.js +129 -0
- package/src/graph/relations.js +81 -0
- package/src/graph/scc.js +241 -0
- package/src/graph/view.js +254 -0
- package/src/ir/CLAUDE.md +4 -0
- package/src/ir/callgraph.js +36 -8
- package/src/ir/index.js +18 -4
- package/src/ir/parser-js.js +10 -0
- package/src/language/CLAUDE.md +13 -0
- package/src/language/nix-option-types.js +170 -0
- package/src/language/nix-script-taint.js +46 -3
- package/src/language/nix-secrets.js +20 -2
- package/src/lineage/CLAUDE.md +72 -8
- package/src/lineage/DESIGN_GRAPH_BUILDER.md +1 -1
- package/src/lineage/DESIGN_PATH_PROVENANCE.md +6 -0
- package/src/lineage/boundary-contract.js +273 -0
- package/src/lineage/completion.js +205 -0
- package/src/lineage/continuation.js +140 -0
- package/src/lineage/control-credit.js +313 -0
- package/src/lineage/correlation.js +199 -0
- package/src/lineage/coverage.js +8 -1
- package/src/lineage/cross-repo-link.js +22 -0
- package/src/lineage/cut-planner.js +548 -0
- package/src/lineage/driver.js +219 -66
- package/src/lineage/engine.js +166 -21
- package/src/lineage/export-json.js +5 -0
- package/src/lineage/graph-builder.js +84 -7
- package/src/lineage/haskell-view.js +59 -0
- package/src/lineage/index.js +3 -0
- package/src/lineage/nix-view.js +35 -0
- package/src/lineage/owned-build.js +77 -0
- package/src/lineage/owned-worker-entry.js +22 -0
- package/src/lineage/owned-worker.js +95 -0
- package/src/lineage/path-evidence.js +123 -0
- package/src/lineage/path-predicates.js +529 -0
- package/src/lineage/path-query.js +189 -20
- package/src/lineage/path-store.js +191 -17
- package/src/lineage/scc-solver.js +186 -0
- package/src/lineage/smt-orchestrator.js +174 -0
- package/src/lineage/smt-z3-backend.js +294 -0
- package/src/lineage/source-seeding.js +36 -2
- package/src/lineage/summaries.js +66 -4
- package/src/lineage/value-versions.js +130 -0
- package/src/lineage/witness-selection.js +95 -0
- package/src/pipeline/scan-health.js +18 -0
- package/src/posture/CLAUDE.md +17 -0
- package/src/posture/business-logic.js +16 -7
- package/src/posture/dag-commands.js +442 -0
- package/src/posture/evidence-bundle.js +1 -1
- package/src/posture/finding-defaults.js +19 -0
- package/src/posture/git-history.js +91 -1
- package/src/posture/witness-bundle.js +573 -0
- package/src/posture/witness-runtime.js +276 -0
- package/src/report/index.js +2 -0
- package/src/sast/CLAUDE.md +2 -0
- package/src/sast/_call-args.js +70 -0
- package/src/sast/init-scope.js +87 -0
- package/src/sast/listen-bind-address.js +49 -0
- package/src/sast/redirect-target.js +106 -0
- package/src/sast/reflected-input.js +76 -0
- package/src/sast/send-file-root.js +27 -0
- package/src/sast/sensitive-data-logging.js +38 -1
- package/dist/119.index.js +0 -197
- package/dist/128.index.js +0 -129
- package/dist/1293.index.js +0 -5689
- package/dist/1336.index.js +0 -16182
- package/dist/1351.index.js +0 -133
- package/dist/1387.index.js +0 -271
- package/dist/1437.index.js +0 -23
- package/dist/1445.index.js +0 -110
- package/dist/1505.index.js +0 -151
- package/dist/1557.index.js +0 -293
- package/dist/1621.index.js +0 -311
- package/dist/1751.index.js +0 -160
- package/dist/1765.index.js +0 -727
- package/dist/1861.index.js +0 -690
- package/dist/1906.index.js +0 -433
- package/dist/208.index.js +0 -1631
- package/dist/2223.index.js +0 -159
- package/dist/2238.index.js +0 -295
- package/dist/2530.index.js +0 -152
- package/dist/2565.index.js +0 -116
- package/dist/2574.index.js +0 -127
- package/dist/2620.index.js +0 -345
- package/dist/2773.index.js +0 -98
- package/dist/2775.index.js +0 -32
- package/dist/2966.index.js +0 -171
- package/dist/2972.index.js +0 -87
- package/dist/2991.index.js +0 -131
- package/dist/3063.index.js +0 -833
- package/dist/3075.index.js +0 -227
- package/dist/3121.index.js +0 -297
- package/dist/3314.index.js +0 -263
- package/dist/3480.index.js +0 -1822
- package/dist/3481.index.js +0 -24
- package/dist/3506.index.js +0 -268
- package/dist/3533.index.js +0 -591
- package/dist/3653.index.js +0 -450
- package/dist/3740.index.js +0 -722
- package/dist/3822.index.js +0 -131
- package/dist/3868.index.js +0 -292
- package/dist/3918.index.js +0 -192
- package/dist/4090.index.js +0 -288
- package/dist/4113.index.js +0 -1286
- package/dist/4121.index.js +0 -1190
- package/dist/4142.index.js +0 -690
- package/dist/4168.index.js +0 -509
- package/dist/4281.index.js +0 -993
- package/dist/4336.index.js +0 -436
- package/dist/4387.index.js +0 -532
- package/dist/4413.index.js +0 -302
- package/dist/4476.index.js +0 -4510
- package/dist/4491.index.js +0 -133
- package/dist/4538.index.js +0 -185
- package/dist/4684.index.js +0 -319
- package/dist/4740.index.js +0 -165
- package/dist/4780.index.js +0 -209
- package/dist/4802.index.js +0 -225
- package/dist/4812.index.js +0 -379
- package/dist/4835.index.js +0 -126
- package/dist/494.index.js +0 -226
- package/dist/4991.index.js +0 -549
- package/dist/5199.index.js +0 -305
- package/dist/5216.index.js +0 -109
- package/dist/5439.index.js +0 -301
- package/dist/5443.index.js +0 -97
- package/dist/5496.index.js +0 -377
- package/dist/5738.index.js +0 -240
- package/dist/5801.index.js +0 -588
- package/dist/5817.index.js +0 -714
- package/dist/5858.index.js +0 -223
- package/dist/5933.index.js +0 -143
- package/dist/601.index.js +0 -23
- package/dist/6173.index.js +0 -273
- package/dist/6291.index.js +0 -1871
- package/dist/6312.index.js +0 -666
- package/dist/634.index.js +0 -16182
- package/dist/644.index.js +0 -87
- package/dist/6508.index.js +0 -283
- package/dist/6560.index.js +0 -645
- package/dist/6576.index.js +0 -422
- package/dist/6579.index.js +0 -271
- package/dist/6655.index.js +0 -3221
- package/dist/6745.index.js +0 -305
- package/dist/6818.index.js +0 -21642
- package/dist/6932.index.js +0 -1190
- package/dist/6972.index.js +0 -167
- package/dist/6995.index.js +0 -163
- package/dist/7249.index.js +0 -191
- package/dist/7372.index.js +0 -1197
- package/dist/7546.index.js +0 -541
- package/dist/76.index.js +0 -448
- package/dist/7879.index.js +0 -163
- package/dist/8025.index.js +0 -130
- package/dist/8054.index.js +0 -248
- package/dist/808.index.js +0 -1330
- package/dist/8097.index.js +0 -201
- package/dist/8126.index.js +0 -191
- package/dist/8178.index.js +0 -78
- package/dist/8382.index.js +0 -93
- package/dist/8408.index.js +0 -261
- package/dist/8540.index.js +0 -174
- package/dist/8635.index.js +0 -802
- package/dist/8688.index.js +0 -410
- package/dist/8699.index.js +0 -779
- package/dist/8891.index.js +0 -457
- package/dist/8916.index.js +0 -885
- package/dist/9146.index.js +0 -170
- package/dist/9168.index.js +0 -841
- package/dist/9292.index.js +0 -369
- package/dist/9422.index.js +0 -866
- package/dist/9440.index.js +0 -453
- package/dist/9479.index.js +0 -152
- package/dist/9481.index.js +0 -220
- package/dist/9539.index.js +0 -100
- package/dist/9547.index.js +0 -103
- package/dist/9692.index.js +0 -90
- package/dist/9800.index.js +0 -462
- package/dist/9899.index.js +0 -138
- package/dist/9981.index.js +0 -206
- package/dist/999.index.js +0 -117
- package/dist/9999.index.js +0 -1698
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,43 @@
|
|
|
9
9
|
> make the history less accurate, not more.
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
## 0.186.1 - Git history annotation blames once per file, which restores scan speed (fix)
|
|
13
|
+
|
|
14
|
+
0.186.0 was tagged but never published: its release workflow stopped at the time-to-first-finding gate (15 to 18 s against a 13 s limit), so the first npm release carrying that work is 0.186.1. Nothing in 0.186.0's content changed apart from the fix below.
|
|
15
|
+
|
|
16
|
+
- **Why it got slower.** Findings that used to carry no line number now carry one, so the git history step, which asks git who introduced each finding, ran for about twice as many findings. It ran one `git blame` per finding and one `git show` per commit, about 14 ms each. Measured on this repository's own source: 7.9 s on 0.185.0, 10.9 s on 0.186.0.
|
|
17
|
+
- **The fix.** One `git blame` per file for all of its lines and one `git log --no-walk` for all commit messages. A file whose batch fails is blamed line by line, so a bad line number cannot lose the others. The annotations are the same; `test/git-history-batch.test.js` checks the batched answers against per-line `git blame`. Measured after the change: 8.3 s locally.
|
|
18
|
+
- **Also in this release.** Two test fixes found by hosted CI: the artifact CLI tests now warm the OSV cache once, because a cold runner has none, and `owned-build/6` accepts a watchdog stop on a slow runner.
|
|
19
|
+
|
|
20
|
+
## 0.186.0 - The graph-precision capabilities are reachable from the CLI, a z3 backend checks path feasibility, and demand queries and incremental analysis got faster (Preview)
|
|
21
|
+
|
|
22
|
+
Everything new is Preview, opt-in and off by default. A default scan's output is unchanged by the new commands and by the solver. No GA capability changed its contract.
|
|
23
|
+
|
|
24
|
+
- **Reachable from the CLI.** `dataflow view`, `dataflow contracts`, `dataflow sink-query`, `dataflow plan-cuts`, `witness build` and `witness verify` expose the typed graph views, boundary contracts, demand reverse taint, min-cut control planner and signed witness bundles. Every report carries a completion status (`complete`, `partial`, `cancelled`, `unavailable`, `invalid`) and `negativeClaimAllowed`; "no modeled path" is never reported as "no vulnerability". `witness build` exits 1 and writes nothing when no modeled path exists. The graph views other than the dataflow view and a tool in the MCP server remain library-only.
|
|
25
|
+
- **A real z3 feasibility backend (`AGENTIC_SECURITY_LINEAGE_SMT=z3`).** Path conditions are translated to SMT-LIB and checked with a per-query timeout and memory cap. Only a proven `unsat` over a verified encoding can reduce a path's exploitability; a timeout, an unknown result, a missing solver or a condition the encoding cannot express changes nothing. The synchronous graph build does not call it: it is reached through the async path-evidence entry points. NaN and loose-equality coercion are not modeled.
|
|
26
|
+
- **Demand queries are faster.** A query with the index built per query is 23.4x faster than full analysis on this repository (was 1.9x), and 3.7x averaged over a 30-query sample that includes fallbacks. Instance-field reads in JS and TS no longer force a full fallback; 75 of 982 sink sites still use the full path, all at the caller-depth limit. Findings equal full analysis on all 982 sites. Call-graph name resolution got a name index that also speeds up the cold engine.
|
|
27
|
+
- **Incremental analysis is faster.** A per-file IR cache (JS and TS, under `AGENTIC_SECURITY_INCREMENTAL`) removes re-parsing. End to end it is now 4.3x on `scanner/src`; on the two synthetic projects it is 2.5x and 2.4x, still short of the 3x target. Projects above about 5,000 functions still run cold at the default summary budget.
|
|
28
|
+
- **Fewer false positives on the labeled samples.** Flagged sites that were true rose from 5 of 25 to 5 of 8 for JavaScript and from 6 of 17 to 6 of 8 for Nix, with recall unchanged (5 of 5 and 6 of 6). Part of the JavaScript gain is a measurement correction: line-less findings were credited to every same-class label in their file. The rules are general (a literal origin prefix on a redirect, request-input-only reflection, module-load blocking calls, confined file serving, bounded Nix option types) and each has a metamorphic test; the held-out split was not independent, which the benchmark summary now says. The multi-service fixture still finds 3 of 8: the misses need the cross-service join in a normal scan and a queue-consumer source model.
|
|
29
|
+
- **The state-machine heuristic is narrowed.** A `.status` write is compared only with status sets declared in the same file, and test files are skipped. Before, the first set found anywhere in a project was applied to every write.
|
|
30
|
+
- **Fixed.** The lineage deadline tests no longer assume which of two valid cancellations wins on a slow runner, and the regression report no longer embeds the exact bundle size, so a rebuild cannot make it stale. `@babel/core` and `z3-solver` moved to their latest releases and `@azure/msal-node` is pinned past an advisory in the extension's dev tooling.
|
|
31
|
+
- **Self-scan baseline.** The scanner's own source falls to 688 findings; the only increases are three low advisories in the two new files that read files from disk.
|
|
32
|
+
- **Not claimed.** Reachability, satisfiability and runtime observation are not exploit proof. A signature on a witness bundle proves integrity, not that the transfer models are right.
|
|
33
|
+
|
|
34
|
+
## 0.185.0 - Graph analysis says when it did not finish, and gains recursion handling, call-site correlation, demand queries, incremental invalidation, boundary contracts, control planning and replayable witnesses (Preview)
|
|
35
|
+
|
|
36
|
+
All new behavior is Preview and is off by default or purely additive. No GA capability changed its contract.
|
|
37
|
+
|
|
38
|
+
- **A graph analysis now says whether it finished.** The field analysis, the call-graph driver, source seeding and reverse selection return a completion record (`complete`, `partial` or `cancelled`) with reasons. A stopped analysis, a coarser fallback context, a construction limit or a cancellation shows in `graph.scanHealth`, `coverage.analysisCompletion`, the limitations list, the signed export and scan health, even when zero flows were projected. A query that did not finish never says "no path" (`negativeClaimAllowed: false`). Invalid query budgets (NaN, Infinity, negative, fractional, over the bound) return `status: "invalid"`. `AGENTIC_SECURITY_LINEAGE_DEADLINE_MS` and the `signal` and `deadline` options cancel a build; from the bundle the cooperative deadline applies and the watchdog for a worker that never stops is source-only.
|
|
39
|
+
- **Recursion.** The call graph is condensed into strongly connected components, callees are solved first, and recursive groups iterate to a fixed point or report `partial`. Reconstruction walks one shared path, so a 20,000-hop chain went from 8.9 s to 22 ms and a 400,000-hop chain completes without a stack overflow.
|
|
40
|
+
- **Call-site correlation (`correlationVersion: 2`).** Value versions and invocation identity keep two calls to one helper, sibling fields and overwritten values apart, with balanced call and return witnesses. Version 1 output is unchanged.
|
|
41
|
+
- **Demand-driven reverse queries (`AGENTIC_SECURITY_DEMAND=1`).** A selector narrows analysis to the functions that can feed one sink argument and the forward engine corroborates. Parity with full analysis holds on 21 programs. The 2x target was missed on this repository's own source when the index is built per query (1.8x); with a warm index a query is about 277x faster. 10 of 30 own-tree queries fell back to full analysis.
|
|
42
|
+
- **Path predicates and control credit (`pathEvidence: true`).** Branch conditions travel with each witness. A control earns credit only for its own sink family and value version, and an all-path claim is refused on any truncated sample.
|
|
43
|
+
- **Dependency-complete incremental analysis (`AGENTIC_SECURITY_INCREMENTAL=1`).** Cached summaries are invalidated through the reverse-dependency closure, including deleted and renamed functions. Corrupt or interrupted caches fall back to a cold scan and say why. A key bug is fixed: summaries for qualified names (those containing `::`) were persisted under the file path, so reuse was effectively inert. The deep stage meets 3x on every project measured; end to end the median is 1.8x to 2.9x, and projects of about 5,000 functions or more run cold at the default summary budget.
|
|
44
|
+
- **Boundary contracts, graph views, control planning and witness bundles.** HTTP, queue and storage contracts seed a consumer's parameter or record field (library entry point). Read-only graph views join findings, call sites, witnesses and flows. `dataflow plan-cuts` computes minimum cuts over the modeled flow network. Signed witness bundles replay offline, with four evidence statuses that are never merged. Witness replay succeeded on 64 of 64 bundles built from real hop streams, and all four tamper kinds tried were rejected 64 of 64.
|
|
45
|
+
- **Measured, with the limits stated.** `bench/dag/` pins six real projects and a labeled multi-service fixture. Precision on the labeled JS and Nix samples is low (5 of 25 and 6 of 17 flagged sites were true), and the fixture finds 3 of 8 labeled positives. Impossible call and return witnesses: 6 of 65 under version 1 and 0 of 64 under version 2 on the fixtures. Labels were written after seeing scanner output and are not independent.
|
|
46
|
+
- **Not claimed.** Reachability, satisfiability and runtime observation are not exploit proof. A signature on a witness bundle proves integrity, not that the transfer models are right. No real SMT solver ships. Boundary contracts, witness building, graph views and demand queries are not wired into a normal scan.
|
|
47
|
+
- **Self-scan baseline.** The scanner's own source gained 18 findings from existing heuristics (14 from the state-machine heuristic, which applies the first declared status set in a project to every `.status` write). The baseline was re-recorded with that reason. Narrowing that heuristic is a follow-up.
|
|
48
|
+
|
|
12
49
|
## 0.184.0 - Runtime corroboration reaches GA on real exports, dependency and taint findings get contracts, and a scan says when it could not consult advisories
|
|
13
50
|
|
|
14
51
|
Runtime corroboration is the first Observe capability to earn GA. The other promotions of the programme's remaining items are contracts and corrections, not new maturity labels, and the ones that stay held say why.
|
package/bin/agentic-security.js
CHANGED
|
@@ -203,6 +203,25 @@ Commands:
|
|
|
203
203
|
--filter <path.json> {nodeIds,edgeIds} to scope the export (no-op + warning for csv/coverage)
|
|
204
204
|
--audience board|ciso|privacy|compliance|regulator|technical
|
|
205
205
|
(default: technical; briefing only — wording/verbosity, never facts)
|
|
206
|
+
dataflow plan-cuts [path] --candidates <file.json> --output <file> [--query <file.json>] [--format json|markdown]
|
|
207
|
+
Plan a minimum-cost, hypothetical control placement (graph cut) over the
|
|
208
|
+
already-scanned lineage graph. Never verifies a remediation.
|
|
209
|
+
The plan carries a completion contract (never a negative claim).
|
|
210
|
+
dataflow view [path] [--node <id> [--direction in|out|both] [--relations a,b] [--origins a,b]] [--output <file>]
|
|
211
|
+
Opt-in typed graph view over the already-scanned lineage graph: identity,
|
|
212
|
+
completion, SCC condensation of the data-flow relation, or one node's neighbours.
|
|
213
|
+
dataflow contracts [path] [--contracts <file>] [--output <file>]
|
|
214
|
+
Opt-in boundary contracts: bind declared HTTP/queue/storage edges to parsed
|
|
215
|
+
consumer sites and seed taint there. A declaration is a modeled input only.
|
|
216
|
+
dataflow sink-query [path] (--list | --file <rel> --line <n>) [--mode demand|full|compare] [--arg-index <n>]
|
|
217
|
+
[--sink-id <id>] [--access-path <p>] [--rule-family <f>] [--max-work <n>]
|
|
218
|
+
[--max-caller-depth <n>] [--on-limit full|partial] [--output <file>]
|
|
219
|
+
Opt-in demand-driven reverse taint for one sink site. Verdicts: flow-found,
|
|
220
|
+
no-flow-in-modeled-scope, unknown. There is no "safe".
|
|
221
|
+
witness build [path] --out <dir> [--sink <id>] | --list-sinks
|
|
222
|
+
witness verify <bundle.json> [--public-key <path>] [--artifacts <dir> --root <project>] [--json]
|
|
223
|
+
Opt-in signed, replayable path witness bundles (Ed25519, same key as attest).
|
|
224
|
+
A valid signature proves integrity only, never that the finding is real.
|
|
206
225
|
dataflow scenario apply [path] --operations <file.json> --output <file> [--format json|markdown]
|
|
207
226
|
Simulate a hypothetical architecture change against the
|
|
208
227
|
already-scanned lineage graph — never mutates the real scan.
|
|
@@ -3385,6 +3404,34 @@ async function cmdVerifyAttestation(args) {
|
|
|
3385
3404
|
return 0;
|
|
3386
3405
|
}
|
|
3387
3406
|
|
|
3407
|
+
// DAG-10: a replayable path witness (schema: agentic-security/path-witness@1). The signature alone proves
|
|
3408
|
+
// integrity; `--witness-artifacts <file.json>` ({graph, provenance, sources?, config?}) additionally replays
|
|
3409
|
+
// the recorded path offline against the artifacts it names. Building a witness is a module API
|
|
3410
|
+
// (posture/witness-bundle.js buildWitnessBundle): the hop stream it needs is not persisted by a scan.
|
|
3411
|
+
const { WITNESS_SCHEMA, verifyWitness } = await import('../src/posture/witness-bundle.js');
|
|
3412
|
+
if (bundle.schema === WITNESS_SCHEMA) {
|
|
3413
|
+
let artifacts = null;
|
|
3414
|
+
if (args.flags['witness-artifacts']) {
|
|
3415
|
+
try { artifacts = JSON.parse(fs.readFileSync(path.resolve(args.flags['witness-artifacts']), 'utf8')); }
|
|
3416
|
+
catch (e) { console.error(`Could not read witness artifacts: ${e.message}`); return 2; }
|
|
3417
|
+
}
|
|
3418
|
+
const w = verifyWitness(bundle, publicKeyPem, artifacts);
|
|
3419
|
+
if (!w.signature.ok) { console.error(`✗ INVALID — ${w.signature.reason}`); return 1; }
|
|
3420
|
+
if (w.replay.performed && !w.replay.ok) {
|
|
3421
|
+
console.error('✗ INVALID — the signature holds but replay against the supplied artifacts failed:');
|
|
3422
|
+
for (const x of w.replay.failures) console.error(` - ${x.code}: ${x.detail}`);
|
|
3423
|
+
return 1;
|
|
3424
|
+
}
|
|
3425
|
+
console.log(w.replay.performed
|
|
3426
|
+
? '✓ VALID — signature holds and the recorded path replays against the supplied artifacts.'
|
|
3427
|
+
: '✓ SIGNATURE VALID (integrity only) — replay was not attempted; pass --witness-artifacts <file.json> to replay.');
|
|
3428
|
+
console.log(` completion: ${bundle.completion?.overall} evidence: ${Object.entries(bundle.evidence || {}).map(([k, v]) => `${k}=${v.status}`).join(' ')}`);
|
|
3429
|
+
console.log(` proves: ${w.statement.proves}`);
|
|
3430
|
+
console.log(` does NOT prove: ${w.statement.doesNotProve}`);
|
|
3431
|
+
console.log(` trust basis: ${w.signature.trustBasis}`);
|
|
3432
|
+
return 0;
|
|
3433
|
+
}
|
|
3434
|
+
|
|
3388
3435
|
const r = verifyEvidenceBundle(bundle, publicKeyPem);
|
|
3389
3436
|
if (!r.ok) {
|
|
3390
3437
|
console.error(`✗ INVALID — ${r.reason}`);
|
|
@@ -4083,6 +4130,16 @@ async function cmdBoundaries(args) {
|
|
|
4083
4130
|
// itself closes (idle-timeout auto-stop, or an external kill) — matching
|
|
4084
4131
|
// every other cmdX(args) function's "resolves to an exit code" contract
|
|
4085
4132
|
// while keeping the process alive for as long as the server is listening.
|
|
4133
|
+
// DAG-07 R07.2: show the graph's completion state wherever a graph is
|
|
4134
|
+
// loaded for display or export. Silent for a complete graph; for anything
|
|
4135
|
+
// else it says so on stderr, including when zero flows were projected.
|
|
4136
|
+
async function _printGraphCompletion(graph, who) {
|
|
4137
|
+
try {
|
|
4138
|
+
const { completionOfGraph, describeCompletion } = await import('../src/lineage/completion.js');
|
|
4139
|
+
for (const line of describeCompletion(completionOfGraph(graph))) process.stderr.write(`${who}: ${line}\n`);
|
|
4140
|
+
} catch { /* disclosure must never break the command */ }
|
|
4141
|
+
}
|
|
4142
|
+
|
|
4086
4143
|
async function cmdExplore(args) {
|
|
4087
4144
|
// `explore . --runtime`: the runtime views are one static, script-free page, not a route on the hardened local server.
|
|
4088
4145
|
if (args.flags.runtime) return cmdRuntimeExplorer(args, args._[1] || '.');
|
|
@@ -4095,6 +4152,7 @@ async function cmdExplore(args) {
|
|
|
4095
4152
|
process.stderr.write(`agentic-security explore: ${loaded.message}\n`);
|
|
4096
4153
|
return 1;
|
|
4097
4154
|
}
|
|
4155
|
+
await _printGraphCompletion(loaded.graph, 'agentic-security explore');
|
|
4098
4156
|
|
|
4099
4157
|
let port = 0;
|
|
4100
4158
|
if (args.flags.port !== undefined) {
|
|
@@ -4355,6 +4413,7 @@ async function cmdDataflowExport(args) {
|
|
|
4355
4413
|
return 1;
|
|
4356
4414
|
}
|
|
4357
4415
|
const graph = loaded.graph;
|
|
4416
|
+
await _printGraphCompletion(graph, 'agentic-security dataflow export');
|
|
4358
4417
|
// Task-3 review finding (non-blocking, fixed): emitGraphDpiaArtifact's
|
|
4359
4418
|
// own opts.generatedAt fallback is `new Date()` — omitting it here made
|
|
4360
4419
|
// dpia/ropa the only two formats whose content reflects EXPORT-time
|
|
@@ -5094,6 +5153,52 @@ async function cmdDataflowScenarioApply(args) {
|
|
|
5094
5153
|
return 0;
|
|
5095
5154
|
}
|
|
5096
5155
|
|
|
5156
|
+
// agentic-security dataflow plan-cuts [path] --candidates <file.json>
|
|
5157
|
+
// --output <file> [--query <file.json>] [--format json|markdown] — DAG-09.
|
|
5158
|
+
// Plans a minimum-cost control placement over the already-scanned,
|
|
5159
|
+
// already-signed graph. HYPOTHETICAL: never writes remediation state or
|
|
5160
|
+
// the scan artifact. Exit codes: 0 success, 1 graph-load failure, 2
|
|
5161
|
+
// argument problem.
|
|
5162
|
+
async function cmdDataflowPlanCuts(args) {
|
|
5163
|
+
const targetAbs = path.resolve(args._[2] || '.');
|
|
5164
|
+
const candFlag = args.flags.candidates;
|
|
5165
|
+
const outputPath = args.flags.output;
|
|
5166
|
+
const format = args.flags.format || 'json';
|
|
5167
|
+
if (!candFlag || typeof candFlag !== 'string') { process.stderr.write('agentic-security dataflow plan-cuts: --candidates <file> is required.\n'); return 2; }
|
|
5168
|
+
if (!outputPath || typeof outputPath !== 'string') { process.stderr.write('agentic-security dataflow plan-cuts: --output <file> is required.\n'); return 2; }
|
|
5169
|
+
if (format !== 'json' && format !== 'markdown') { process.stderr.write('agentic-security dataflow plan-cuts: --format must be one of json|markdown.\n'); return 2; }
|
|
5170
|
+
let candidates; let query = {};
|
|
5171
|
+
try {
|
|
5172
|
+
candidates = JSON.parse(await fsp.readFile(path.resolve(candFlag), 'utf8'));
|
|
5173
|
+
if (typeof args.flags.query === 'string') query = JSON.parse(await fsp.readFile(path.resolve(args.flags.query), 'utf8'));
|
|
5174
|
+
} catch (e) { process.stderr.write(`agentic-security dataflow plan-cuts: could not read/parse input: ${e.message}\n`); return 2; }
|
|
5175
|
+
if (!Array.isArray(candidates)) { process.stderr.write('agentic-security dataflow plan-cuts: --candidates must be a JSON array.\n'); return 2; }
|
|
5176
|
+
const { loadSignedGraph } = await import('../src/server/graph-loader.js');
|
|
5177
|
+
const loaded = loadSignedGraph(targetAbs);
|
|
5178
|
+
if (!loaded.ok) { process.stderr.write(`agentic-security dataflow plan-cuts: ${loaded.message}\n`); return 1; }
|
|
5179
|
+
const { planCuts } = await import('../src/lineage/cut-planner.js');
|
|
5180
|
+
const plan = planCuts(loaded.graph, query, candidates);
|
|
5181
|
+
// Completion contract: a plan is `complete` only for a finite cut over a graph known complete; never a negative claim.
|
|
5182
|
+
plan.contract = (await import('../src/posture/dag-commands.js')).planContract(plan);
|
|
5183
|
+
let data;
|
|
5184
|
+
if (format === 'json') data = JSON.stringify(plan, null, 2);
|
|
5185
|
+
else {
|
|
5186
|
+
const lines = ['# Control placement plan (hypothetical)', '', `Status: ${plan.status}. Total coverage: ${plan.completeness.totalCoverage}. Not verified.`, '', '## Proposed controls', ''];
|
|
5187
|
+
for (const c of plan.cut.controls) lines.push(`- ${c.location.type} \`${c.location.id}\` (${c.semantics}, owner ${c.owner}, cost ${c.cost})`);
|
|
5188
|
+
lines.push('', '## Residual bypass paths', '');
|
|
5189
|
+
for (const r of plan.validation?.residualPaths ?? []) lines.push(`- to \`${r.sink}\` via ${r.nodes.map((n) => `\`${n}\``).join(' -> ')}`);
|
|
5190
|
+
lines.push('', '## Limitations', '');
|
|
5191
|
+
for (const l of plan.limitations) lines.push(`- ${l}`);
|
|
5192
|
+
lines.push('', `Completion: ${plan.contract.status}. A negative claim is never allowed for a hypothetical plan.`);
|
|
5193
|
+
data = lines.join('\n') + '\n';
|
|
5194
|
+
}
|
|
5195
|
+
try {
|
|
5196
|
+
await fsp.mkdir(path.dirname(path.resolve(outputPath)), { recursive: true });
|
|
5197
|
+
await fsp.writeFile(path.resolve(outputPath), data);
|
|
5198
|
+
} catch (e) { process.stderr.write(`agentic-security dataflow plan-cuts: could not write --output: ${e.message}\n`); return 2; }
|
|
5199
|
+
return 0;
|
|
5200
|
+
}
|
|
5201
|
+
|
|
5097
5202
|
// agentic-security dataflow impact assess [path] --target <canonical-id>
|
|
5098
5203
|
// --output <file> [--format json|markdown] — M5 deliverable #4
|
|
5099
5204
|
// (FR-507). Loads the already-scanned, already-signed graph via
|
|
@@ -7926,6 +8031,7 @@ async function main() {
|
|
|
7926
8031
|
}
|
|
7927
8032
|
case 'boundaries': process.exit(await cmdBoundaries(args));
|
|
7928
8033
|
case 'explore': process.exit(await cmdExplore(args));
|
|
8034
|
+
case 'witness': process.exit(await (await import('../src/posture/dag-commands.js')).cmdWitness(args));
|
|
7929
8035
|
case 'dataflow': {
|
|
7930
8036
|
const sub = args._[1];
|
|
7931
8037
|
if (sub === 'export') { process.exit(await cmdDataflowExport(args)); }
|
|
@@ -7966,6 +8072,10 @@ async function main() {
|
|
|
7966
8072
|
}
|
|
7967
8073
|
break;
|
|
7968
8074
|
}
|
|
8075
|
+
else if (sub === 'plan-cuts') { process.exit(await cmdDataflowPlanCuts(args)); }
|
|
8076
|
+
else if (sub === 'view') { process.exit(await (await import('../src/posture/dag-commands.js')).cmdDataflowView(args)); }
|
|
8077
|
+
else if (sub === 'contracts') { process.exit(await (await import('../src/posture/dag-commands.js')).cmdDataflowContracts(args)); }
|
|
8078
|
+
else if (sub === 'sink-query') { process.exit(await (await import('../src/posture/dag-commands.js')).cmdDataflowSinkQuery(args)); }
|
|
7969
8079
|
else if (sub === 'impact') {
|
|
7970
8080
|
const impactSub = args._[2];
|
|
7971
8081
|
if (impactSub === 'assess') { process.exit(await cmdDataflowImpactAssess(args)); }
|
|
@@ -7986,7 +8096,7 @@ async function main() {
|
|
|
7986
8096
|
break;
|
|
7987
8097
|
}
|
|
7988
8098
|
else if (sub === 'twin') { process.exit(await cmdDataflowTwin(args)); }
|
|
7989
|
-
process.stderr.write(`agentic-security dataflow: unknown subcommand "${sub}" — only "export", "diff", "watch", "scenario", "impact", "observations", and "twin" are supported.\n`);
|
|
8099
|
+
process.stderr.write(`agentic-security dataflow: unknown subcommand "${sub}" — only "export", "diff", "watch", "scenario", "plan-cuts", "view", "contracts", "sink-query", "impact", "observations", and "twin" are supported.\n`);
|
|
7990
8100
|
process.exit(2);
|
|
7991
8101
|
}
|
|
7992
8102
|
case 'governance': {
|