@clear-capabilities/agentic-security-scanner 0.185.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.
Files changed (167) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/bin/agentic-security.js +24 -1
  3. package/dist/1128.index.js +25 -21
  4. package/dist/2584.index.js +1 -1
  5. package/dist/4171.index.js +2 -2
  6. package/dist/5752.index.js +466 -0
  7. package/dist/7405.index.js +1 -1
  8. package/dist/8158.index.js +1713 -0
  9. package/dist/8798.index.js +1 -1
  10. package/dist/9382.index.js +2 -2
  11. package/dist/9579.index.js +1 -1
  12. package/dist/agentic-security.mjs +29 -29
  13. package/dist/agentic-security.mjs.sha256 +1 -1
  14. package/dist/{0b301d45b231f144a1b4.json → ca62ee93079be4c24eee.json} +7 -7
  15. package/package.json +7 -7
  16. package/src/dataflow/CLAUDE.md +6 -3
  17. package/src/dataflow/access-paths.js +12 -2
  18. package/src/dataflow/demand-query.js +279 -40
  19. package/src/dataflow/dep-fingerprint.js +1 -1
  20. package/src/dataflow/exploit-prover.js +6 -2
  21. package/src/dataflow/ir-cache.js +268 -0
  22. package/src/dataflow/privacy-taint.js +7 -0
  23. package/src/engine.js +62 -5
  24. package/src/ir/CLAUDE.md +4 -0
  25. package/src/ir/callgraph.js +36 -8
  26. package/src/ir/index.js +18 -4
  27. package/src/ir/parser-js.js +10 -0
  28. package/src/language/CLAUDE.md +13 -0
  29. package/src/language/nix-option-types.js +170 -0
  30. package/src/language/nix-script-taint.js +46 -3
  31. package/src/language/nix-secrets.js +20 -2
  32. package/src/lineage/CLAUDE.md +2 -1
  33. package/src/lineage/smt-orchestrator.js +47 -11
  34. package/src/lineage/smt-z3-backend.js +294 -0
  35. package/src/posture/CLAUDE.md +5 -1
  36. package/src/posture/business-logic.js +16 -7
  37. package/src/posture/dag-commands.js +442 -0
  38. package/src/posture/finding-defaults.js +19 -0
  39. package/src/posture/git-history.js +91 -1
  40. package/src/sast/CLAUDE.md +2 -0
  41. package/src/sast/_call-args.js +70 -0
  42. package/src/sast/init-scope.js +87 -0
  43. package/src/sast/listen-bind-address.js +49 -0
  44. package/src/sast/redirect-target.js +106 -0
  45. package/src/sast/reflected-input.js +76 -0
  46. package/src/sast/send-file-root.js +27 -0
  47. package/src/sast/sensitive-data-logging.js +38 -1
  48. package/dist/119.index.js +0 -197
  49. package/dist/128.index.js +0 -129
  50. package/dist/1293.index.js +0 -5689
  51. package/dist/1336.index.js +0 -16331
  52. package/dist/1351.index.js +0 -133
  53. package/dist/1387.index.js +0 -271
  54. package/dist/1437.index.js +0 -23
  55. package/dist/1445.index.js +0 -110
  56. package/dist/1505.index.js +0 -151
  57. package/dist/1557.index.js +0 -293
  58. package/dist/1621.index.js +0 -311
  59. package/dist/1751.index.js +0 -160
  60. package/dist/1765.index.js +0 -727
  61. package/dist/1861.index.js +0 -690
  62. package/dist/1906.index.js +0 -433
  63. package/dist/208.index.js +0 -1631
  64. package/dist/2223.index.js +0 -159
  65. package/dist/2238.index.js +0 -295
  66. package/dist/2530.index.js +0 -152
  67. package/dist/2565.index.js +0 -116
  68. package/dist/2574.index.js +0 -127
  69. package/dist/2620.index.js +0 -345
  70. package/dist/2773.index.js +0 -98
  71. package/dist/2775.index.js +0 -32
  72. package/dist/2966.index.js +0 -171
  73. package/dist/2972.index.js +0 -87
  74. package/dist/2991.index.js +0 -131
  75. package/dist/3063.index.js +0 -833
  76. package/dist/3075.index.js +0 -227
  77. package/dist/3121.index.js +0 -297
  78. package/dist/3314.index.js +0 -263
  79. package/dist/3480.index.js +0 -1822
  80. package/dist/3481.index.js +0 -24
  81. package/dist/3506.index.js +0 -268
  82. package/dist/3533.index.js +0 -591
  83. package/dist/3653.index.js +0 -450
  84. package/dist/3740.index.js +0 -722
  85. package/dist/3822.index.js +0 -131
  86. package/dist/3868.index.js +0 -292
  87. package/dist/3918.index.js +0 -192
  88. package/dist/4090.index.js +0 -288
  89. package/dist/4113.index.js +0 -1286
  90. package/dist/4121.index.js +0 -1190
  91. package/dist/4142.index.js +0 -690
  92. package/dist/4168.index.js +0 -509
  93. package/dist/4281.index.js +0 -993
  94. package/dist/4336.index.js +0 -436
  95. package/dist/4387.index.js +0 -532
  96. package/dist/4413.index.js +0 -302
  97. package/dist/4476.index.js +0 -4510
  98. package/dist/4491.index.js +0 -133
  99. package/dist/4538.index.js +0 -185
  100. package/dist/4684.index.js +0 -319
  101. package/dist/4740.index.js +0 -165
  102. package/dist/4780.index.js +0 -209
  103. package/dist/4802.index.js +0 -225
  104. package/dist/4812.index.js +0 -379
  105. package/dist/4835.index.js +0 -126
  106. package/dist/494.index.js +0 -226
  107. package/dist/4991.index.js +0 -549
  108. package/dist/5199.index.js +0 -305
  109. package/dist/5216.index.js +0 -109
  110. package/dist/5439.index.js +0 -301
  111. package/dist/5443.index.js +0 -97
  112. package/dist/5496.index.js +0 -377
  113. package/dist/5738.index.js +0 -240
  114. package/dist/5801.index.js +0 -588
  115. package/dist/5817.index.js +0 -714
  116. package/dist/5858.index.js +0 -223
  117. package/dist/5933.index.js +0 -143
  118. package/dist/601.index.js +0 -23
  119. package/dist/6173.index.js +0 -273
  120. package/dist/6291.index.js +0 -1871
  121. package/dist/6312.index.js +0 -666
  122. package/dist/634.index.js +0 -16182
  123. package/dist/644.index.js +0 -87
  124. package/dist/6508.index.js +0 -283
  125. package/dist/6560.index.js +0 -645
  126. package/dist/6576.index.js +0 -422
  127. package/dist/6579.index.js +0 -271
  128. package/dist/6655.index.js +0 -3221
  129. package/dist/6745.index.js +0 -305
  130. package/dist/6818.index.js +0 -21642
  131. package/dist/6932.index.js +0 -1190
  132. package/dist/6972.index.js +0 -167
  133. package/dist/6995.index.js +0 -163
  134. package/dist/7249.index.js +0 -191
  135. package/dist/7372.index.js +0 -1197
  136. package/dist/7546.index.js +0 -541
  137. package/dist/76.index.js +0 -448
  138. package/dist/7879.index.js +0 -163
  139. package/dist/8025.index.js +0 -130
  140. package/dist/8054.index.js +0 -248
  141. package/dist/808.index.js +0 -1330
  142. package/dist/8097.index.js +0 -201
  143. package/dist/8126.index.js +0 -191
  144. package/dist/8178.index.js +0 -78
  145. package/dist/8382.index.js +0 -93
  146. package/dist/8408.index.js +0 -261
  147. package/dist/8540.index.js +0 -174
  148. package/dist/8635.index.js +0 -802
  149. package/dist/8688.index.js +0 -410
  150. package/dist/8699.index.js +0 -779
  151. package/dist/8891.index.js +0 -457
  152. package/dist/8916.index.js +0 -885
  153. package/dist/9146.index.js +0 -170
  154. package/dist/9168.index.js +0 -841
  155. package/dist/9292.index.js +0 -369
  156. package/dist/9422.index.js +0 -866
  157. package/dist/9440.index.js +0 -453
  158. package/dist/9479.index.js +0 -152
  159. package/dist/9481.index.js +0 -220
  160. package/dist/9539.index.js +0 -100
  161. package/dist/9547.index.js +0 -103
  162. package/dist/9692.index.js +0 -90
  163. package/dist/9800.index.js +0 -462
  164. package/dist/9899.index.js +0 -138
  165. package/dist/9981.index.js +0 -206
  166. package/dist/999.index.js +0 -117
  167. package/dist/9999.index.js +0 -1698
package/CHANGELOG.md CHANGED
@@ -9,6 +9,28 @@
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
+
12
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)
13
35
 
14
36
  All new behavior is Preview and is off by default or purely additive. No GA capability changed its contract.
@@ -206,6 +206,22 @@ Commands:
206
206
  dataflow plan-cuts [path] --candidates <file.json> --output <file> [--query <file.json>] [--format json|markdown]
207
207
  Plan a minimum-cost, hypothetical control placement (graph cut) over the
208
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.
209
225
  dataflow scenario apply [path] --operations <file.json> --output <file> [--format json|markdown]
210
226
  Simulate a hypothetical architecture change against the
211
227
  already-scanned lineage graph — never mutates the real scan.
@@ -5162,6 +5178,8 @@ async function cmdDataflowPlanCuts(args) {
5162
5178
  if (!loaded.ok) { process.stderr.write(`agentic-security dataflow plan-cuts: ${loaded.message}\n`); return 1; }
5163
5179
  const { planCuts } = await import('../src/lineage/cut-planner.js');
5164
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);
5165
5183
  let data;
5166
5184
  if (format === 'json') data = JSON.stringify(plan, null, 2);
5167
5185
  else {
@@ -5171,6 +5189,7 @@ async function cmdDataflowPlanCuts(args) {
5171
5189
  for (const r of plan.validation?.residualPaths ?? []) lines.push(`- to \`${r.sink}\` via ${r.nodes.map((n) => `\`${n}\``).join(' -> ')}`);
5172
5190
  lines.push('', '## Limitations', '');
5173
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.`);
5174
5193
  data = lines.join('\n') + '\n';
5175
5194
  }
5176
5195
  try {
@@ -8012,6 +8031,7 @@ async function main() {
8012
8031
  }
8013
8032
  case 'boundaries': process.exit(await cmdBoundaries(args));
8014
8033
  case 'explore': process.exit(await cmdExplore(args));
8034
+ case 'witness': process.exit(await (await import('../src/posture/dag-commands.js')).cmdWitness(args));
8015
8035
  case 'dataflow': {
8016
8036
  const sub = args._[1];
8017
8037
  if (sub === 'export') { process.exit(await cmdDataflowExport(args)); }
@@ -8053,6 +8073,9 @@ async function main() {
8053
8073
  break;
8054
8074
  }
8055
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)); }
8056
8079
  else if (sub === 'impact') {
8057
8080
  const impactSub = args._[2];
8058
8081
  if (impactSub === 'assess') { process.exit(await cmdDataflowImpactAssess(args)); }
@@ -8073,7 +8096,7 @@ async function main() {
8073
8096
  break;
8074
8097
  }
8075
8098
  else if (sub === 'twin') { process.exit(await cmdDataflowTwin(args)); }
8076
- 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`);
8077
8100
  process.exit(2);
8078
8101
  }
8079
8102
  case 'governance': {
@@ -31,10 +31,14 @@ function makeIdentityTokenizer() {
31
31
  // EXPORTS
32
32
  __webpack_require__.d(__webpack_exports__, {
33
33
  WITNESS_SCHEMA: () => (/* binding */ WITNESS_SCHEMA),
34
+ buildWitnessBundle: () => (/* binding */ buildWitnessBundle),
35
+ exportProvenanceArtifact: () => (/* binding */ exportProvenanceArtifact),
36
+ parseWitnessBundle: () => (/* binding */ parseWitnessBundle),
37
+ signWitnessBundle: () => (/* binding */ signWitnessBundle),
34
38
  verifyWitness: () => (/* binding */ verifyWitness)
35
39
  });
36
40
 
37
- // UNUSED EXPORTS: buildWitnessBundle, exportProvenanceArtifact, parseWitnessBundle, replayWitness, signWitnessBundle, transferModelFingerprints, verifyWitnessSignature
41
+ // UNUSED EXPORTS: replayWitness, transferModelFingerprints, verifyWitnessSignature
38
42
 
39
43
  // EXTERNAL MODULE: external "node:crypto"
40
44
  var external_node_crypto_ = __webpack_require__(77598);
@@ -118,7 +122,7 @@ function iso(v) {
118
122
  * dropped and only COUNTED (their names could themselves carry a value). Identities are tokenized with the
119
123
  * existing keyed-hash convention; a raw identity with no tokenizer is dropped, never kept.
120
124
  */
121
- function witness_runtime_sanitizeCausalGroups(groups, { tokenize = (0,identity_token.makeIdentityTokenizer)() } = {}) {
125
+ function sanitizeCausalGroups(groups, { tokenize = (0,identity_token.makeIdentityTokenizer)() } = {}) {
122
126
  const out = []; const rejected = []; let droppedFields = 0;
123
127
  const GROUP_KEYS = new Set(['id', 'sessionRef', 'sessionId', 'traceId', 'environment', 'deploymentRef', 'deploymentConfigDigest',
124
128
  'identity', 'identityToken', 'firstObservedAt', 'lastObservedAt', 'edgeObservations']);
@@ -218,7 +222,7 @@ function assessRuntimeSupport({ pathEdgeIds, edgeStates, causalGroups = [], expe
218
222
  if (!ids.length || !isObj(edgeStates) || !edgeStates.evaluated) {
219
223
  return { ...base, status: 'not_consulted', statements: ['No telemetry was consulted for this path. That is not an observation of absence.'] };
220
224
  }
221
- const sanitized = witness_runtime_sanitizeCausalGroups(causalGroups, { tokenize });
225
+ const sanitized = sanitizeCausalGroups(causalGroups, { tokenize });
222
226
  base.groupsConsidered = sanitized.groups.length; base.rejectedGroups = sanitized.rejected.length; base.droppedFields = sanitized.droppedFields;
223
227
 
224
228
  for (const id of ids) {
@@ -278,7 +282,7 @@ function assessRuntimeSupport({ pathEdgeIds, edgeStates, causalGroups = [], expe
278
282
  * R10.6: external proof references are bound to the sink, the value and the configuration. A reference that is
279
283
  * stale or mismatched is REJECTED with its reasons; it is never re-attached to the changed graph.
280
284
  */
281
- function witness_runtime_bindExploitProofs(proofs, expected) {
285
+ function bindExploitProofs(proofs, expected) {
282
286
  const bound = []; const rejected = [];
283
287
  for (const [i, p] of arr(proofs).entries()) {
284
288
  const id = isObj(p) && typeof p.id === 'string' && REF_RE.test(p.id) ? p.id : `proof-${i}`;
@@ -302,7 +306,7 @@ function witness_runtime_bindExploitProofs(proofs, expected) {
302
306
  }
303
307
 
304
308
  /** Validate the nullable DAG-04 slot. Returns { ok, errors }. */
305
- function witness_runtime_validateControlEvidence(ce, pathEdgeIds) {
309
+ function validateControlEvidence(ce, pathEdgeIds) {
306
310
  if (ce === null) return { ok: true, errors: [] };
307
311
  const errors = [];
308
312
  if (!isObj(ce)) return { ok: false, errors: ['control evidence must be an object or null'] };
@@ -321,7 +325,7 @@ function witness_runtime_validateControlEvidence(ce, pathEdgeIds) {
321
325
  * The four statuses, side by side. Returns exactly the four keys in EVIDENCE_STATUS_KEYS and nothing that
322
326
  * ranks, scores or combines them.
323
327
  */
324
- function witness_runtime_buildEvidenceStatuses({ pathComplete, controlEvidence, runtime, exploit }) {
328
+ function buildEvidenceStatuses({ pathComplete, controlEvidence, runtime, exploit }) {
325
329
  return {
326
330
  modeledPath: { status: pathComplete ? 'connected_complete' : 'connected_partial' },
327
331
  feasibility: { status: controlEvidence ? controlEvidence.status : 'not_assessed' },
@@ -420,14 +424,14 @@ const fileOfScope = (scope) => (typeof scope === 'string' ? scope.split('::')[0]
420
424
  function exportProvenanceArtifact({ hops, incomplete = [] }) {
421
425
  const seen = new Map();
422
426
  for (const h of witness_bundle_arr(hops)) {
423
- const norm = Object.fromEntries(HOP_FIELDS.map((f) => [f, h?.[f] ?? null]));
424
- seen.set(canonicalJson(norm), norm);
427
+ const norm = Object.fromEntries(path_store/* HOP_FIELDS */.KO.map((f) => [f, h?.[f] ?? null]));
428
+ seen.set((0,evidence_bundle/* canonicalJson */.dj)(norm), norm);
425
429
  }
426
430
  const hopList = [...seen.keys()].sort().map((k) => seen.get(k));
427
431
  const truncations = witness_bundle_arr(incomplete)
428
432
  .map((u) => ({ scope: u.qid ?? u.scope ?? null, context: u.context ?? null, reason: u.reasons?.[0] ?? u.reason ?? 'analysis-incomplete' }))
429
- .sort((a, b) => (canonicalJson(a) < canonicalJson(b) ? -1 : 1));
430
- return { schema: PROVENANCE_ARTIFACT_SCHEMA, hops: hopList, truncations, digest: witness_bundle_sha(canonicalJson({ hops: hopList, truncations })) };
433
+ .sort((a, b) => ((0,evidence_bundle/* canonicalJson */.dj)(a) < (0,evidence_bundle/* canonicalJson */.dj)(b) ? -1 : 1));
434
+ return { schema: PROVENANCE_ARTIFACT_SCHEMA, hops: hopList, truncations, digest: witness_bundle_sha((0,evidence_bundle/* canonicalJson */.dj)({ hops: hopList, truncations })) };
431
435
  }
432
436
 
433
437
  function artifactDigest(a) {
@@ -568,8 +572,8 @@ function buildWitnessBundle(o = {}) {
568
572
  }
569
573
  const budget = { ...result.budget }; delete budget.expansionsUsed;
570
574
  const config = { pathQuery: budget, storeLimits: storeLimits.limits, ...(witness_bundle_isObj(o.analysisConfig) ? { extra: o.analysisConfig } : {}) };
571
- const analysisConfigDigest = witness_bundle_sha(canonicalJson(config));
572
- const graphDigest = computeGraphDigest(graph);
575
+ const analysisConfigDigest = witness_bundle_sha((0,evidence_bundle/* canonicalJson */.dj)(config));
576
+ const graphDigest = (0,export_json.computeGraphDigest)(graph);
573
577
 
574
578
  const files = [...new Set([srcNode, sinkNode, ...edges.flatMap((e) => [store.getNode(e.fromNodeId), store.getNode(e.toNodeId)])].map((n) => fileOfScope(n?.scope)).filter(Boolean))].sort();
575
579
  const sources = []; const unboundSourceFiles = [];
@@ -608,7 +612,7 @@ function buildWitnessBundle(o = {}) {
608
612
  const ceCheck = validateControlEvidence(ce, edges.map((e) => e.edgeId));
609
613
  if (!ceCheck.ok) return { ok: false, reason: `control evidence: ${ceCheck.errors.join('; ')}` };
610
614
 
611
- const tokenize = o.tokenize ?? makeIdentityTokenizer();
615
+ const tokenize = o.tokenize ?? (0,identity_token.makeIdentityTokenizer)();
612
616
  let runtime;
613
617
  if (o.runtime) {
614
618
  const groups = sanitizeCausalGroups(o.runtime.causalGroups, { tokenize });
@@ -622,7 +626,7 @@ function buildWitnessBundle(o = {}) {
622
626
  const exploitInputs = witness_bundle_arr(o.exploitProofs).map((p) => sanitizeProof(p));
623
627
  const exploit = { inputs: exploitInputs, ...bindExploitProofs(exploitInputs, { sinkRef, dataElementId: edges[0].dataElementId, graphDigest, analysisConfigDigest }) };
624
628
 
625
- const sourcePath = redactString(srcNode.path ?? ''); const sinkPath = redactString(sinkNode.path ?? '');
629
+ const sourcePath = (0,redact/* redactString */.rd)(srcNode.path ?? ''); const sinkPath = (0,redact/* redactString */.rd)(sinkNode.path ?? '');
626
630
  const redactions = (srcNode.path && sourcePath !== srcNode.path ? 1 : 0) + (sinkNode.path && sinkPath !== sinkNode.path ? 1 : 0);
627
631
 
628
632
  const bundle = jsonSafe({
@@ -669,7 +673,7 @@ function sanitizeProof(p) {
669
673
  const ref = (v) => (typeof v === 'string' && /^[A-Za-z0-9._:@/-]{1,160}$/.test(v) ? v : null);
670
674
  return {
671
675
  id: ref(p.id), kind: ['proof', 'dnr-hunt', 'incident'].includes(p.kind) ? p.kind : null, sinkRef: ref(p.sinkRef), dataElementId: ref(p.dataElementId),
672
- graphDigest: ref(p.graphDigest), analysisConfigDigest: ref(p.analysisConfigDigest), note: typeof p.note === 'string' ? redactString(p.note).slice(0, 200) : null,
676
+ graphDigest: ref(p.graphDigest), analysisConfigDigest: ref(p.analysisConfigDigest), note: typeof p.note === 'string' ? (0,redact/* redactString */.rd)(p.note).slice(0, 200) : null,
673
677
  };
674
678
  }
675
679
 
@@ -693,9 +697,9 @@ function signedBytes(bundle) {
693
697
 
694
698
  /** Sign with the same Ed25519 key and self-issued trust basis as finding evidence bundles. */
695
699
  function signWitnessBundle(bundle, privateKeyPem) {
696
- const publicKeyPem = crypto.createPublicKey(privateKeyPem).export({ type: 'spki', format: 'pem' });
697
- const withIssuance = { ...bundle, issuance: buildLocalIssuance(publicKeyPem) };
698
- const sig = crypto.sign(null, signedBytes(withIssuance), privateKeyPem);
700
+ const publicKeyPem = external_node_crypto_.createPublicKey(privateKeyPem).export({ type: 'spki', format: 'pem' });
701
+ const withIssuance = { ...bundle, issuance: (0,evidence_bundle/* buildLocalIssuance */.hc)(publicKeyPem) };
702
+ const sig = external_node_crypto_.sign(null, signedBytes(withIssuance), privateKeyPem);
699
703
  return { ...withIssuance, signature: { algorithm: 'ed25519', canonicalisation: WITNESS_SCHEMA, value: sig.toString('base64') } };
700
704
  }
701
705
 
@@ -874,16 +878,16 @@ function replayWitness(bundle, artifacts, opts = {}) {
874
878
  const gEdges = new Set(witness_bundle_arr(artifacts.graph.edges).map((e) => e.id));
875
879
  if (gb.edgeIds.some((id) => !gEdges.has(id))) fail('graph-binding', 'a bound graph edge is not in the graph artifact');
876
880
  }
877
- const ceCheck = witness_runtime_validateControlEvidence(bundle.controlEvidence, edges.map((e) => e.edgeId));
881
+ const ceCheck = validateControlEvidence(bundle.controlEvidence, edges.map((e) => e.edgeId));
878
882
  if (!ceCheck.ok) fail('control-evidence', ceCheck.errors.join('; '));
879
883
  const rt = bundle.runtime;
880
884
  const reAssessed = witness_bundle_isObj(rt) ? runtimeAssessment(rt, gb ? gb.edgeIds : []) : null;
881
885
  if (!reAssessed || !same(reAssessed, rt.assessment)) fail('runtime-inconsistent', 'the recorded runtime assessment does not follow from its recorded inputs');
882
886
  if (rt?.inputs && !gb) fail('runtime-inconsistent', 'runtime inputs without a graph binding');
883
- const reBound = witness_runtime_bindExploitProofs(witness_bundle_arr(bundle.exploit?.inputs), { sinkRef: last, dataElementId: edges[0]?.dataElementId, graphDigest: bundle.graphRef.digest, analysisConfigDigest: bundle.graphRef.analysisConfigDigest });
887
+ const reBound = bindExploitProofs(witness_bundle_arr(bundle.exploit?.inputs), { sinkRef: last, dataElementId: edges[0]?.dataElementId, graphDigest: bundle.graphRef.digest, analysisConfigDigest: bundle.graphRef.analysisConfigDigest });
884
888
  if (!same({ status: reBound.status, bound: reBound.bound, rejected: reBound.rejected }, { status: bundle.exploit?.status, bound: bundle.exploit?.bound, rejected: bundle.exploit?.rejected })) fail('proof-binding', 'the recorded proof binding does not follow from the sink, value and configuration it is bound to');
885
889
  if (reAssessed && bundle.completion) {
886
- const ev = witness_runtime_buildEvidenceStatuses({ pathComplete: bundle.completion.path.complete && derived.overall === 'complete', controlEvidence: bundle.controlEvidence, runtime: reAssessed, exploit: reBound });
890
+ const ev = buildEvidenceStatuses({ pathComplete: bundle.completion.path.complete && derived.overall === 'complete', controlEvidence: bundle.controlEvidence, runtime: reAssessed, exploit: reBound });
887
891
  if (!same(ev, bundle.evidence)) fail('evidence-statuses', 'the four evidence statuses do not follow from their inputs');
888
892
  }
889
893
  return { ok: failures.length === 0, failures, warnings, derived };
@@ -1430,7 +1430,7 @@ async function diagnosticsOf(scan) {
1430
1430
  try { wanted = JSON.parse(process.env.AGENTIC_SECURITY_DIAG_FILES || '[]'); } catch { wanted = []; }
1431
1431
  if (wanted.length) {
1432
1432
  try {
1433
- const irMod = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 28149));
1433
+ const irMod = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, 98297));
1434
1434
  irMod._resetIrParseFailures();
1435
1435
  const ir = await irMod.buildProjectIRAsync(Object.fromEntries(wanted.filter((f) => scan.fc && typeof scan.fc[f] === 'string').map((f) => [f, scan.fc[f]])));
1436
1436
  for (const f of wanted) {
@@ -22,8 +22,8 @@ var nix_build_trust = __webpack_require__(36668);
22
22
  var discovery = __webpack_require__(30951);
23
23
  // EXTERNAL MODULE: ./src/language/haskell-supply.js
24
24
  var haskell_supply = __webpack_require__(86349);
25
- // EXTERNAL MODULE: ./src/language/nix-script-taint.js
26
- var nix_script_taint = __webpack_require__(11793);
25
+ // EXTERNAL MODULE: ./src/language/nix-script-taint.js + 1 modules
26
+ var nix_script_taint = __webpack_require__(98078);
27
27
  // EXTERNAL MODULE: ./src/language/nix-secrets.js
28
28
  var nix_secrets = __webpack_require__(69466);
29
29
  // EXTERNAL MODULE: ./src/language/nix-parser.js