@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.
Files changed (228) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/bin/agentic-security.js +111 -1
  3. package/dist/1128.index.js +912 -0
  4. package/dist/1665.index.js +828 -0
  5. package/dist/2584.index.js +1 -1
  6. package/dist/4171.index.js +2 -2
  7. package/dist/5752.index.js +466 -0
  8. package/dist/7405.index.js +1 -1
  9. package/dist/8158.index.js +1713 -0
  10. package/dist/8798.index.js +1 -1
  11. package/dist/9382.index.js +2 -2
  12. package/dist/9579.index.js +1 -1
  13. package/dist/agentic-security.mjs +29 -29
  14. package/dist/agentic-security.mjs.sha256 +1 -1
  15. package/dist/{dad58d1113a12c23a142.json → ca62ee93079be4c24eee.json} +29 -12
  16. package/dist/frontend/src/shell.js +12 -2
  17. package/package.json +29 -12
  18. package/src/dataflow/CLAUDE.md +46 -1
  19. package/src/dataflow/access-paths.js +12 -2
  20. package/src/dataflow/backward.js +42 -7
  21. package/src/dataflow/cross-service-taint.js +83 -1
  22. package/src/dataflow/demand-query.js +1168 -0
  23. package/src/dataflow/dep-fingerprint.js +718 -0
  24. package/src/dataflow/engine.js +48 -6
  25. package/src/dataflow/exploit-prover.js +6 -2
  26. package/src/dataflow/ifds-precise.js +22 -0
  27. package/src/dataflow/incremental-replay.js +118 -0
  28. package/src/dataflow/incremental-session.js +257 -0
  29. package/src/dataflow/incremental.js +237 -35
  30. package/src/dataflow/index.js +25 -94
  31. package/src/dataflow/ir-cache.js +268 -0
  32. package/src/dataflow/privacy-taint.js +7 -0
  33. package/src/dataflow/summaries.js +72 -6
  34. package/src/dataflow/tabulation.js +80 -24
  35. package/src/engine.js +113 -8
  36. package/src/graph/CLAUDE.md +31 -0
  37. package/src/graph/adapters/artifact.js +81 -0
  38. package/src/graph/adapters/crossrepo.js +75 -0
  39. package/src/graph/adapters/dataflow.js +76 -0
  40. package/src/graph/adapters/provenance.js +92 -0
  41. package/src/graph/adapters/semantic.js +89 -0
  42. package/src/graph/crosswalk-builders.js +152 -0
  43. package/src/graph/crosswalk.js +146 -0
  44. package/src/graph/index.js +15 -0
  45. package/src/graph/observation.js +57 -0
  46. package/src/graph/refs.js +105 -0
  47. package/src/graph/registry.js +129 -0
  48. package/src/graph/relations.js +81 -0
  49. package/src/graph/scc.js +241 -0
  50. package/src/graph/view.js +254 -0
  51. package/src/ir/CLAUDE.md +4 -0
  52. package/src/ir/callgraph.js +36 -8
  53. package/src/ir/index.js +18 -4
  54. package/src/ir/parser-js.js +10 -0
  55. package/src/language/CLAUDE.md +13 -0
  56. package/src/language/nix-option-types.js +170 -0
  57. package/src/language/nix-script-taint.js +46 -3
  58. package/src/language/nix-secrets.js +20 -2
  59. package/src/lineage/CLAUDE.md +72 -8
  60. package/src/lineage/DESIGN_GRAPH_BUILDER.md +1 -1
  61. package/src/lineage/DESIGN_PATH_PROVENANCE.md +6 -0
  62. package/src/lineage/boundary-contract.js +273 -0
  63. package/src/lineage/completion.js +205 -0
  64. package/src/lineage/continuation.js +140 -0
  65. package/src/lineage/control-credit.js +313 -0
  66. package/src/lineage/correlation.js +199 -0
  67. package/src/lineage/coverage.js +8 -1
  68. package/src/lineage/cross-repo-link.js +22 -0
  69. package/src/lineage/cut-planner.js +548 -0
  70. package/src/lineage/driver.js +219 -66
  71. package/src/lineage/engine.js +166 -21
  72. package/src/lineage/export-json.js +5 -0
  73. package/src/lineage/graph-builder.js +84 -7
  74. package/src/lineage/haskell-view.js +59 -0
  75. package/src/lineage/index.js +3 -0
  76. package/src/lineage/nix-view.js +35 -0
  77. package/src/lineage/owned-build.js +77 -0
  78. package/src/lineage/owned-worker-entry.js +22 -0
  79. package/src/lineage/owned-worker.js +95 -0
  80. package/src/lineage/path-evidence.js +123 -0
  81. package/src/lineage/path-predicates.js +529 -0
  82. package/src/lineage/path-query.js +189 -20
  83. package/src/lineage/path-store.js +191 -17
  84. package/src/lineage/scc-solver.js +186 -0
  85. package/src/lineage/smt-orchestrator.js +174 -0
  86. package/src/lineage/smt-z3-backend.js +294 -0
  87. package/src/lineage/source-seeding.js +36 -2
  88. package/src/lineage/summaries.js +66 -4
  89. package/src/lineage/value-versions.js +130 -0
  90. package/src/lineage/witness-selection.js +95 -0
  91. package/src/pipeline/scan-health.js +18 -0
  92. package/src/posture/CLAUDE.md +17 -0
  93. package/src/posture/business-logic.js +16 -7
  94. package/src/posture/dag-commands.js +442 -0
  95. package/src/posture/evidence-bundle.js +1 -1
  96. package/src/posture/finding-defaults.js +19 -0
  97. package/src/posture/git-history.js +91 -1
  98. package/src/posture/witness-bundle.js +573 -0
  99. package/src/posture/witness-runtime.js +276 -0
  100. package/src/report/index.js +2 -0
  101. package/src/sast/CLAUDE.md +2 -0
  102. package/src/sast/_call-args.js +70 -0
  103. package/src/sast/init-scope.js +87 -0
  104. package/src/sast/listen-bind-address.js +49 -0
  105. package/src/sast/redirect-target.js +106 -0
  106. package/src/sast/reflected-input.js +76 -0
  107. package/src/sast/send-file-root.js +27 -0
  108. package/src/sast/sensitive-data-logging.js +38 -1
  109. package/dist/119.index.js +0 -197
  110. package/dist/128.index.js +0 -129
  111. package/dist/1293.index.js +0 -5689
  112. package/dist/1336.index.js +0 -16182
  113. package/dist/1351.index.js +0 -133
  114. package/dist/1387.index.js +0 -271
  115. package/dist/1437.index.js +0 -23
  116. package/dist/1445.index.js +0 -110
  117. package/dist/1505.index.js +0 -151
  118. package/dist/1557.index.js +0 -293
  119. package/dist/1621.index.js +0 -311
  120. package/dist/1751.index.js +0 -160
  121. package/dist/1765.index.js +0 -727
  122. package/dist/1861.index.js +0 -690
  123. package/dist/1906.index.js +0 -433
  124. package/dist/208.index.js +0 -1631
  125. package/dist/2223.index.js +0 -159
  126. package/dist/2238.index.js +0 -295
  127. package/dist/2530.index.js +0 -152
  128. package/dist/2565.index.js +0 -116
  129. package/dist/2574.index.js +0 -127
  130. package/dist/2620.index.js +0 -345
  131. package/dist/2773.index.js +0 -98
  132. package/dist/2775.index.js +0 -32
  133. package/dist/2966.index.js +0 -171
  134. package/dist/2972.index.js +0 -87
  135. package/dist/2991.index.js +0 -131
  136. package/dist/3063.index.js +0 -833
  137. package/dist/3075.index.js +0 -227
  138. package/dist/3121.index.js +0 -297
  139. package/dist/3314.index.js +0 -263
  140. package/dist/3480.index.js +0 -1822
  141. package/dist/3481.index.js +0 -24
  142. package/dist/3506.index.js +0 -268
  143. package/dist/3533.index.js +0 -591
  144. package/dist/3653.index.js +0 -450
  145. package/dist/3740.index.js +0 -722
  146. package/dist/3822.index.js +0 -131
  147. package/dist/3868.index.js +0 -292
  148. package/dist/3918.index.js +0 -192
  149. package/dist/4090.index.js +0 -288
  150. package/dist/4113.index.js +0 -1286
  151. package/dist/4121.index.js +0 -1190
  152. package/dist/4142.index.js +0 -690
  153. package/dist/4168.index.js +0 -509
  154. package/dist/4281.index.js +0 -993
  155. package/dist/4336.index.js +0 -436
  156. package/dist/4387.index.js +0 -532
  157. package/dist/4413.index.js +0 -302
  158. package/dist/4476.index.js +0 -4510
  159. package/dist/4491.index.js +0 -133
  160. package/dist/4538.index.js +0 -185
  161. package/dist/4684.index.js +0 -319
  162. package/dist/4740.index.js +0 -165
  163. package/dist/4780.index.js +0 -209
  164. package/dist/4802.index.js +0 -225
  165. package/dist/4812.index.js +0 -379
  166. package/dist/4835.index.js +0 -126
  167. package/dist/494.index.js +0 -226
  168. package/dist/4991.index.js +0 -549
  169. package/dist/5199.index.js +0 -305
  170. package/dist/5216.index.js +0 -109
  171. package/dist/5439.index.js +0 -301
  172. package/dist/5443.index.js +0 -97
  173. package/dist/5496.index.js +0 -377
  174. package/dist/5738.index.js +0 -240
  175. package/dist/5801.index.js +0 -588
  176. package/dist/5817.index.js +0 -714
  177. package/dist/5858.index.js +0 -223
  178. package/dist/5933.index.js +0 -143
  179. package/dist/601.index.js +0 -23
  180. package/dist/6173.index.js +0 -273
  181. package/dist/6291.index.js +0 -1871
  182. package/dist/6312.index.js +0 -666
  183. package/dist/634.index.js +0 -16182
  184. package/dist/644.index.js +0 -87
  185. package/dist/6508.index.js +0 -283
  186. package/dist/6560.index.js +0 -645
  187. package/dist/6576.index.js +0 -422
  188. package/dist/6579.index.js +0 -271
  189. package/dist/6655.index.js +0 -3221
  190. package/dist/6745.index.js +0 -305
  191. package/dist/6818.index.js +0 -21642
  192. package/dist/6932.index.js +0 -1190
  193. package/dist/6972.index.js +0 -167
  194. package/dist/6995.index.js +0 -163
  195. package/dist/7249.index.js +0 -191
  196. package/dist/7372.index.js +0 -1197
  197. package/dist/7546.index.js +0 -541
  198. package/dist/76.index.js +0 -448
  199. package/dist/7879.index.js +0 -163
  200. package/dist/8025.index.js +0 -130
  201. package/dist/8054.index.js +0 -248
  202. package/dist/808.index.js +0 -1330
  203. package/dist/8097.index.js +0 -201
  204. package/dist/8126.index.js +0 -191
  205. package/dist/8178.index.js +0 -78
  206. package/dist/8382.index.js +0 -93
  207. package/dist/8408.index.js +0 -261
  208. package/dist/8540.index.js +0 -174
  209. package/dist/8635.index.js +0 -802
  210. package/dist/8688.index.js +0 -410
  211. package/dist/8699.index.js +0 -779
  212. package/dist/8891.index.js +0 -457
  213. package/dist/8916.index.js +0 -885
  214. package/dist/9146.index.js +0 -170
  215. package/dist/9168.index.js +0 -841
  216. package/dist/9292.index.js +0 -369
  217. package/dist/9422.index.js +0 -866
  218. package/dist/9440.index.js +0 -453
  219. package/dist/9479.index.js +0 -152
  220. package/dist/9481.index.js +0 -220
  221. package/dist/9539.index.js +0 -100
  222. package/dist/9547.index.js +0 -103
  223. package/dist/9692.index.js +0 -90
  224. package/dist/9800.index.js +0 -462
  225. package/dist/9899.index.js +0 -138
  226. package/dist/9981.index.js +0 -206
  227. package/dist/999.index.js +0 -117
  228. package/dist/9999.index.js +0 -1698
@@ -1,165 +0,0 @@
1
- export const id = 4740;
2
- export const ids = [4740];
3
- export const modules = {
4
-
5
- /***/ 4740:
6
- /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
7
-
8
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9
- /* harmony export */ PROVENANCE_BUNDLE_SCHEMA: () => (/* binding */ PROVENANCE_BUNDLE_SCHEMA),
10
- /* harmony export */ buildProvenanceEvidenceBundle: () => (/* binding */ buildProvenanceEvidenceBundle),
11
- /* harmony export */ ensureKeyPair: () => (/* reexport safe */ _evidence_bundle_js__WEBPACK_IMPORTED_MODULE_1__.ensureKeyPair),
12
- /* harmony export */ signProvenanceEvidenceBundle: () => (/* binding */ signProvenanceEvidenceBundle),
13
- /* harmony export */ verifyProvenanceEvidenceBundle: () => (/* binding */ verifyProvenanceEvidenceBundle)
14
- /* harmony export */ });
15
- /* harmony import */ var node_crypto__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(77598);
16
- /* harmony import */ var _evidence_bundle_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(32876);
17
- // Signed provenance evidence bundles (Finding Provenance PRD, M4 §4.1).
18
- //
19
- // Sibling to posture/evidence-bundle.js, not a generalization of it — that
20
- // module's bundle shape (proofTier, taintPath, exploitability...) answers
21
- // "is this finding real"; this one answers "who/when introduced it, how
22
- // sure are we." Forcing one shape to cover both would leave half of every
23
- // bundle null. Same four-function pattern (build/sign/verify + a schema
24
- // string + a top-level-key allowlist), same Ed25519 key material — reused,
25
- // not reimplemented.
26
- //
27
- // The allowlisted fields mirror provenance/coordinator.js's own
28
- // computeDigest() material EXACTLY (stableId, findingOrigin.commit,
29
- // branchIntroduction.commit, evidenceAttribution role:path:line:commit
30
- // strings, method, confidence.reasons, limitations) plus repo/HEAD identity
31
- // (not in computeDigest's material, since that digest never leaves the repo
32
- // it was computed in, but a bundle does). Everything here is copied from
33
- // what findingProvenance already computed. Nothing is inferred.
34
- //
35
- // canonicalJson vs canonicalBytes — resolved before writing this file
36
- // -------------------------------------------------------------------
37
- // evidence-bundle.js exports TWO canonicalisation helpers, not one, and they
38
- // are not interchangeable. `canonicalJson(value)` is a pure, shape-agnostic
39
- // deterministic serialiser (sorted keys at every level, order-preserving
40
- // arrays) — safe to reuse for any bundle shape. `canonicalBytes(bundle)`
41
- // is NOT generic: it hardcodes evidence-bundle.js's own six top-level keys
42
- // (`schema, finding, evidence, engine, proves, doesNotProve`) when building
43
- // the object it signs. Reusing `canonicalBytes` here would silently sign a
44
- // filtered object missing this module's `repo` and `provenance` fields
45
- // (and would read `bundle.evidence`, which a provenance bundle never has) —
46
- // exactly the EA-03 failure mode the allowlist check below exists to catch,
47
- // just introduced from the signing side instead of the verifying side. So
48
- // this module imports the generic `canonicalJson` (as the brief's Step 1
49
- // sketch already did) and signs `canonicalJson(bundle)` directly, since
50
- // `buildProvenanceEvidenceBundle` never puts anything on the unsigned
51
- // object beyond this module's own allowlisted fields.
52
-
53
-
54
-
55
-
56
- const PROVENANCE_BUNDLE_SCHEMA = 'agentic-security/provenance-evidence@1';
57
-
58
- const PROVES = 'This bundle\'s contents are exactly what was signed at attestation time.';
59
- const DOES_NOT_PROVE = 'This bundle does NOT prove the origin commit is correctly identified — read confidence.level and limitations for that. It proves the RECORD is unmodified, not that the record is right.';
60
-
61
- /**
62
- * Build an unsigned provenance bundle from one finding's findingProvenance.
63
- * Returns null for a finding with no findingProvenance at all (nothing to
64
- * attest) — this is a caller error (attest a scan before its provenance
65
- * pass ran), not a case to paper over with an empty bundle.
66
- */
67
- function buildProvenanceEvidenceBundle(finding, { engineVersion, repoIdentity, head } = {}) {
68
- if (!finding || typeof finding !== 'object') return null;
69
- const fp = finding.findingProvenance;
70
- if (!fp || typeof fp !== 'object') return null;
71
- return {
72
- schema: PROVENANCE_BUNDLE_SCHEMA,
73
- finding: {
74
- id: finding.id ?? null,
75
- stableId: finding.stableId ?? null,
76
- },
77
- repo: {
78
- identity: repoIdentity ?? null,
79
- head: head ?? fp.analysisBasis?.head ?? null,
80
- },
81
- provenance: {
82
- status: fp.status ?? null,
83
- findingOrigin: fp.findingOrigin
84
- ? {
85
- commit: fp.findingOrigin.commit ?? null,
86
- authorName: fp.findingOrigin.authorName ?? null,
87
- authorDate: fp.findingOrigin.authorDate ?? null,
88
- summary: fp.findingOrigin.summary ?? null,
89
- }
90
- : null,
91
- branchIntroduction: fp.branchIntroduction
92
- ? { commit: fp.branchIntroduction.commit ?? null, branch: fp.branchIntroduction.branch ?? null }
93
- : null,
94
- evidenceAttribution: (fp.evidenceAttribution || []).map((n) => ({
95
- role: n.role ?? null, path: n.path ?? null, line: n.line ?? null, commit: n.commit ?? null,
96
- })),
97
- method: fp.method ?? null,
98
- confidence: fp.confidence
99
- ? { level: fp.confidence.level ?? null, score: fp.confidence.score ?? null, reasons: fp.confidence.reasons || [] }
100
- : null,
101
- limitations: fp.limitations || [],
102
- // M4 §4.2 final-review fix: this module was written (Task 1) before
103
- // the cross-repo lineage feature existed (Task 5), so a
104
- // cross-repo-resolved origin's boundary-crossing marker was dropped
105
- // from the signed bundle — a foreign repository's commit SHA and a
106
- // real author name, with no MACHINE-READABLE signal that the origin
107
- // crossed a repository boundary (only the prose in `limitations`
108
- // said so, and a programmatic verifier does not read prose). Nested
109
- // under `provenance` alongside the fields above, so it is part of
110
- // what gets signed — never a new top-level key.
111
- historyCoverage: { crossRepoLineage: fp.historyCoverage?.crossRepoLineage ?? false },
112
- },
113
- engine: { engineVersion: engineVersion ?? null },
114
- proves: PROVES,
115
- doesNotProve: DOES_NOT_PROVE,
116
- };
117
- }
118
-
119
- function signProvenanceEvidenceBundle(bundle, privateKeyPem) {
120
- const sig = node_crypto__WEBPACK_IMPORTED_MODULE_0__.sign(null, Buffer.from((0,_evidence_bundle_js__WEBPACK_IMPORTED_MODULE_1__/* .canonicalJson */ .dj)(bundle), 'utf8'), privateKeyPem);
121
- return {
122
- ...bundle,
123
- signature: { algorithm: 'ed25519', canonicalisation: PROVENANCE_BUNDLE_SCHEMA, value: sig.toString('base64') },
124
- };
125
- }
126
-
127
- const PROVENANCE_BUNDLE_TOP_LEVEL_KEYS = new Set([
128
- 'schema', 'finding', 'repo', 'provenance', 'engine', 'proves', 'doesNotProve', 'signature',
129
- ]);
130
-
131
- /**
132
- * Verify with a PUBLIC key only. Rejects any top-level key outside the
133
- * allowlist BEFORE checking the signature — same EA-03 fix evidence-bundle.js
134
- * carries: a signature only covers the bytes it was computed over, so an
135
- * unknown key stapled on after signing would otherwise verify as authentic.
136
- */
137
- function verifyProvenanceEvidenceBundle(bundle, publicKeyPem) {
138
- if (!bundle || typeof bundle !== 'object') return { ok: false, reason: 'bundle is not an object' };
139
- if (bundle.schema !== PROVENANCE_BUNDLE_SCHEMA) return { ok: false, reason: `unrecognised schema: ${bundle.schema}` };
140
- const unknownKeys = Object.keys(bundle).filter((k) => !PROVENANCE_BUNDLE_TOP_LEVEL_KEYS.has(k));
141
- if (unknownKeys.length) {
142
- return { ok: false, reason: `unrecognised top-level key(s) not covered by the signature: ${unknownKeys.join(', ')}` };
143
- }
144
- const sig = bundle.signature;
145
- if (!sig?.value) return { ok: false, reason: 'bundle is unsigned' };
146
- if (sig.algorithm !== 'ed25519') return { ok: false, reason: `unsupported algorithm: ${sig.algorithm}` };
147
- if (!publicKeyPem) return { ok: false, reason: 'no public key supplied' };
148
- const { signature, ...unsigned } = bundle;
149
- let ok = false;
150
- try {
151
- ok = node_crypto__WEBPACK_IMPORTED_MODULE_0__.verify(null, Buffer.from((0,_evidence_bundle_js__WEBPACK_IMPORTED_MODULE_1__/* .canonicalJson */ .dj)(unsigned), 'utf8'), publicKeyPem, Buffer.from(sig.value, 'base64'));
152
- } catch (e) {
153
- return { ok: false, reason: `verification error: ${e.message}` };
154
- }
155
- return ok
156
- ? { ok: true, reason: null }
157
- : { ok: false, reason: 'signature does not match the bundle contents — it was modified after signing' };
158
- }
159
-
160
-
161
-
162
-
163
- /***/ })
164
-
165
- };
@@ -1,209 +0,0 @@
1
- export const id = 4780;
2
- export const ids = [4780];
3
- export const modules = {
4
-
5
- /***/ 44780:
6
- /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
7
-
8
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9
- /* harmony export */ iI: () => (/* binding */ summarizeForBadge),
10
- /* harmony export */ renderBadge: () => (/* binding */ renderBadge)
11
- /* harmony export */ });
12
- /* unused harmony exports badgeFromScanRoot, renderSvg, _internal */
13
- /* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(73024);
14
- /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(76760);
15
- /* harmony import */ var _posture_state_dir_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(69259);
16
- // Live SVG badge generator (v0.72).
17
- //
18
- // Every repo can drop a badge in its README pulling from the latest scan:
19
- //
20
- // ![agentic-security](https://agentic-security.dev/badge?repo=<slug>)
21
- //
22
- // or self-hosted via the CLI subcommand emitting an inline <img> URL or
23
- // a static SVG. The badge format borrows from shields.io for visual
24
- // consistency. Reads from .agentic-security/last-scan.json or accepts a
25
- // scan object directly.
26
- //
27
- // Output formats:
28
- // - 'svg' — inline SVG string (default; the bytes you'd serve)
29
- // - 'json' — { label, count, color, severity } for a frontend renderer
30
- //
31
- // Style variants:
32
- // - 'flat' — shields.io flat
33
- // - 'for-the-badge' — caps + thicker
34
- //
35
- // Color is driven by the highest non-zero severity:
36
- // critical → red
37
- // high → orange
38
- // medium → yellow
39
- // low → blue
40
- // info → lightgrey
41
- // none → brightgreen
42
-
43
-
44
-
45
-
46
-
47
- const COLORS = {
48
- critical: '#e05d44', // red
49
- high: '#fe7d37', // orange
50
- medium: '#dfb317', // yellow
51
- low: '#007ec6', // blue
52
- info: '#9f9f9f', // grey
53
- none: '#4c1', // brightgreen
54
- label: '#555',
55
- };
56
-
57
- const SEVERITIES = ['critical', 'high', 'medium', 'low', 'info'];
58
-
59
- function _readLastScan(scanRoot) {
60
- if (!scanRoot) return null;
61
- const fp = (0,_posture_state_dir_js__WEBPACK_IMPORTED_MODULE_2__.statePath)(scanRoot, 'last-scan.json');
62
- if (!node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(fp)) return null;
63
- try { return JSON.parse(node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(fp, 'utf8')); }
64
- catch { return null; }
65
- }
66
-
67
- function _ageString(ts) {
68
- if (!ts) return null;
69
- const ageMs = Date.now() - new Date(ts).getTime();
70
- if (isNaN(ageMs) || ageMs < 0) return null;
71
- const min = Math.floor(ageMs / 60_000);
72
- if (min < 60) return `${min}m ago`;
73
- const hr = Math.floor(min / 60);
74
- if (hr < 24) return `${hr}h ago`;
75
- const day = Math.floor(hr / 24);
76
- return `${day}d ago`;
77
- }
78
-
79
- /**
80
- * Compute the badge value from a scan object.
81
- *
82
- * Returns:
83
- * {
84
- * label: 'agentic-security',
85
- * summary: 'critical 0 · high 2 · medium 5' | 'passing' | 'no scan',
86
- * color: '#fe7d37',
87
- * highest: 'high' | 'none' | 'unknown',
88
- * ageStr: '4h ago' | null,
89
- * counts: { critical, high, medium, low, info },
90
- * total: 7,
91
- * }
92
- */
93
- function summarizeForBadge(scan) {
94
- if (!scan || !Array.isArray(scan.findings)) {
95
- return {
96
- label: 'agentic-security',
97
- summary: 'no scan',
98
- color: COLORS.info,
99
- highest: 'unknown',
100
- ageStr: null,
101
- counts: { critical: 0, high: 0, medium: 0, low: 0, info: 0 },
102
- total: 0,
103
- };
104
- }
105
- const counts = { critical: 0, high: 0, medium: 0, low: 0, info: 0 };
106
- for (const f of scan.findings) {
107
- const s = f.severity || 'info';
108
- if (counts[s] !== undefined) counts[s]++;
109
- }
110
- let highest = 'none';
111
- for (const s of SEVERITIES) { if (counts[s] > 0) { highest = s; break; } }
112
- const color = COLORS[highest] || COLORS.info;
113
- const summary = highest === 'none'
114
- ? 'passing'
115
- : `crit ${counts.critical} · high ${counts.high} · med ${counts.medium}`;
116
- const total = SEVERITIES.reduce((a, s) => a + counts[s], 0);
117
- return {
118
- label: 'agentic-security',
119
- summary,
120
- color,
121
- highest,
122
- ageStr: _ageString(scan.timestamp || scan.when || scan.lastScan),
123
- counts,
124
- total,
125
- };
126
- }
127
-
128
- /**
129
- * Compute the badge from .agentic-security/last-scan.json under `scanRoot`.
130
- */
131
- function badgeFromScanRoot(scanRoot) {
132
- return summarizeForBadge(_readLastScan(scanRoot));
133
- }
134
-
135
- function _xmlEscape(s) {
136
- return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
137
- }
138
-
139
- function _textWidth(s) {
140
- // Rough character width — works fine for the small badge label range.
141
- return s.length * 7 + 10;
142
- }
143
-
144
- /**
145
- * Render an inline SVG matching shields.io's flat style. Self-contained
146
- * (no external font references) so the badge works in any README.
147
- */
148
- function renderSvg(b, opts = {}) {
149
- if (!b) b = summarizeForBadge(null);
150
- const style = opts.style || 'flat';
151
- const labelText = b.label;
152
- const valueText = b.ageStr ? `${b.summary} · ${b.ageStr}` : b.summary;
153
- const lblW = _textWidth(labelText);
154
- const valW = _textWidth(valueText);
155
- const totalW = lblW + valW;
156
- const h = style === 'for-the-badge' ? 28 : 20;
157
- const fontSize = style === 'for-the-badge' ? 12 : 11;
158
- return `<svg xmlns="http://www.w3.org/2000/svg" width="${totalW}" height="${h}" role="img" aria-label="${_xmlEscape(labelText)}: ${_xmlEscape(valueText)}">
159
- <title>${_xmlEscape(labelText)}: ${_xmlEscape(valueText)}</title>
160
- <linearGradient id="s" x2="0" y2="100%">
161
- <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
162
- <stop offset="1" stop-opacity=".1"/>
163
- </linearGradient>
164
- <clipPath id="r"><rect width="${totalW}" height="${h}" rx="3" fill="#fff"/></clipPath>
165
- <g clip-path="url(#r)">
166
- <rect width="${lblW}" height="${h}" fill="${COLORS.label}"/>
167
- <rect x="${lblW}" width="${valW}" height="${h}" fill="${b.color}"/>
168
- <rect width="${totalW}" height="${h}" fill="url(#s)"/>
169
- </g>
170
- <g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="${fontSize}">
171
- <text aria-hidden="true" x="${lblW / 2}" y="${h - 6}" fill="#010101" fill-opacity=".3">${_xmlEscape(labelText)}</text>
172
- <text x="${lblW / 2}" y="${h - 7}">${_xmlEscape(labelText)}</text>
173
- <text aria-hidden="true" x="${lblW + valW / 2}" y="${h - 6}" fill="#010101" fill-opacity=".3">${_xmlEscape(valueText)}</text>
174
- <text x="${lblW + valW / 2}" y="${h - 7}">${_xmlEscape(valueText)}</text>
175
- </g>
176
- </svg>`;
177
- }
178
-
179
- /**
180
- * Public entry: produce the badge in the requested format.
181
- *
182
- * format: 'svg' (default) | 'json'
183
- * style: 'flat' (default) | 'for-the-badge'
184
- * scanRoot: directory containing .agentic-security/last-scan.json
185
- * scan: pre-loaded scan object (skips disk read)
186
- */
187
- function renderBadge({ format = 'svg', style = 'flat', scanRoot, scan } = {}) {
188
- const summary = summarizeForBadge(scan || _readLastScan(scanRoot));
189
- if (format === 'json') {
190
- return JSON.stringify({
191
- schemaVersion: 1,
192
- label: summary.label,
193
- message: summary.summary,
194
- color: summary.color,
195
- highest: summary.highest,
196
- ageStr: summary.ageStr,
197
- counts: summary.counts,
198
- total: summary.total,
199
- });
200
- }
201
- return renderSvg(summary, { style });
202
- }
203
-
204
- const _internal = { COLORS, _ageString, _readLastScan };
205
-
206
-
207
- /***/ })
208
-
209
- };
@@ -1,225 +0,0 @@
1
- export const id = 4802;
2
- export const ids = [4802];
3
- export const modules = {
4
-
5
- /***/ 94802:
6
- /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
7
-
8
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9
- /* harmony export */ OBLIGATION_EVIDENCE_PACK_SCHEMA: () => (/* binding */ OBLIGATION_EVIDENCE_PACK_SCHEMA),
10
- /* harmony export */ buildObligationEvidencePack: () => (/* binding */ buildObligationEvidencePack),
11
- /* harmony export */ ensureKeyPair: () => (/* reexport safe */ _evidence_bundle_js__WEBPACK_IMPORTED_MODULE_0__.ensureKeyPair),
12
- /* harmony export */ signObligationEvidencePack: () => (/* binding */ signObligationEvidencePack),
13
- /* harmony export */ verifyObligationEvidencePack: () => (/* binding */ verifyObligationEvidencePack)
14
- /* harmony export */ });
15
- /* harmony import */ var _evidence_bundle_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(32876);
16
- /* harmony import */ var _evidence_grade_wording_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(51128);
17
- /* harmony import */ var _lineage_export_json_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(63406);
18
- /* harmony import */ var node_crypto__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(77598);
19
- // obligation-evidence-pack.js — Milestone 4 sub-project 6c: signed,
20
- // versioned evidence packs for a Regulatory Obligation Overlay framework
21
- // evaluation (FR-504, PRD §10.10). Fourth sibling in the
22
- // evidence-bundle.js family — see that module's own header for the
23
- // shared-key rationale, and provenance-evidence-bundle.js for the most
24
- // directly mirrored precedent (same reused ensureKeyPair/keyPaths/
25
- // canonicalJson, own schema string, own build/sign/verify trio, own
26
- // top-level-key allowlist).
27
- //
28
- // WHAT THIS IS NOT
29
- // -----------------
30
- // Not a reuse of evidence-bundle.js's own bundle shape (that's a single
31
- // FINDING's evidence — proofTier/taintPath/etc; this artifact has no
32
- // finding at all). Not compliance-evidence-signing.js's ComplianceEvidence
33
- // manifest either — that signs the pre-existing family:/module:/rule:-
34
- // driven present/partial/absent/manual walkthrough status; this signs the
35
- // newer, additive graph: mapping type's own real ObligationMapping
36
- // records (evaluateGraphFlowPredicate/buildObligationMappingFromGraphPredicate,
37
- // sub-project 6b) — a distinct FR-504 artifact with its own field list
38
- // (scope, framework versions, facts, evidence index, unknown/manual
39
- // items, accepted exceptions, scan health, limitations, graph digest,
40
- // reproducibility metadata).
41
- //
42
- // THE EVIDENCE-INDEX DESIGN DECISION (see the scoping doc for the full
43
- // writeup)
44
- // -----------------------------------------------------------------
45
- // A real ObligationMapping record's own `evidence[]` is structurally
46
- // always empty today (graph-builder.js hardcodes `edge.evidenceRefs: []`
47
- // on every minted edge, disclosed in obligation-predicates.js's own
48
- // header). Rather than ship an evidence pack whose "evidence index" is
49
- // honestly, permanently empty, this module builds a REAL evidence index
50
- // from each fact's own `contributingGraphIds` (real flow ids
51
- // evaluateGraphFlowPredicate already returns) — resolving each flow id
52
- // back into a small, real summary (source/sink kind, dataElement
53
- // dataClasses, the edge's own transit/atRest/handling verdicts) by
54
- // joining against the graph's own entity arrays. `record.evidence` is
55
- // still carried through verbatim on each fact (honest, even though
56
- // empty) — the evidence index is an ADDITIONAL section built from data
57
- // that genuinely IS populated, not a replacement that hides the gap.
58
-
59
-
60
-
61
-
62
-
63
-
64
- const OBLIGATION_EVIDENCE_PACK_SCHEMA = 'agentic-security/obligation-evidence-pack@1';
65
-
66
- function _asArray(v) {
67
- return Array.isArray(v) ? v : [];
68
- }
69
-
70
- /**
71
- * Resolve one ObligationMapping fact's contributingGraphIds (real flow
72
- * ids) into a small, real, human-and-machine-readable summary per flow —
73
- * the evidence index's own per-fact contribution. Never throws: a flow id
74
- * that no longer resolves against this graph (a stale pack re-evaluated
75
- * against a newer graph, or a malformed fact) is simply skipped, not
76
- * fabricated.
77
- */
78
- function _resolveEvidenceForFact(fact, joins) {
79
- const { flowsById, nodesById, edgesById, dataElementsById } = joins;
80
- const flowIds = _asArray(fact?.contributingGraphIds);
81
- const resolved = [];
82
- for (const flowId of flowIds) {
83
- const flow = flowsById.get(flowId);
84
- if (!flow) continue;
85
- const sourceNode = nodesById.get(flow.source);
86
- const sinkNode = nodesById.get(flow.sink);
87
- // Reads only edgeIds[0] — mirrors obligation-predicates.js's own
88
- // disclosed, currently-unreachable limitation (graph-builder.js always
89
- // mints single-edge flows; see that module's own header) rather than
90
- // inventing a different convention for the same case here.
91
- const edge = edgesById.get(_asArray(flow.edgeIds)[0]);
92
- const dataClasses = _asArray(flow.dataElementIds)
93
- .map((id) => dataElementsById.get(id))
94
- .filter(Boolean)
95
- .flatMap((d) => _asArray(d.dataClasses));
96
- resolved.push({
97
- flowId,
98
- source: sourceNode ? { kind: sourceNode.kind ?? null, subtype: sourceNode.subtype ?? null } : null,
99
- sink: sinkNode ? { kind: sinkNode.kind ?? null, subtype: sinkNode.subtype ?? null } : null,
100
- dataClasses: [...new Set(dataClasses)],
101
- transitVerdict: edge?.protection?.transit?.verdict ?? null,
102
- atRestVerdict: edge?.protection?.atRest?.verdict ?? null,
103
- handlingVerdict: edge?.protection?.handling?.verdict ?? null,
104
- });
105
- }
106
- return resolved;
107
- }
108
-
109
- function _buildJoins(graph) {
110
- return {
111
- flowsById: new Map(_asArray(graph?.flows).filter(Boolean).map((f) => [f.id, f])),
112
- nodesById: new Map(_asArray(graph?.nodes).filter(Boolean).map((n) => [n.id, n])),
113
- edgesById: new Map(_asArray(graph?.edges).filter(Boolean).map((e) => [e.id, e])),
114
- dataElementsById: new Map(_asArray(graph?.dataElements).filter(Boolean).map((d) => [d.id, d])),
115
- };
116
- }
117
-
118
- /**
119
- * Build an unsigned evidence pack from a framework evaluation. Never
120
- * throws: every field degrades honestly on missing input rather than
121
- * fabricating a value — a null/absent graph yields empty facts/
122
- * evidenceIndex and a null graphDigest/scope, never a guess.
123
- *
124
- * @param {object} args
125
- * @param {object|null} args.graph - the scan's DataFlowGraph v1 document (scan.lineageGraph), or null
126
- * @param {object} args.framework - the loaded framework object (auditor-walkthrough.js#loadFramework's return)
127
- * @param {Array} args.evaluation - auditor-walkthrough.js#evaluateFramework's return (per-control entries, each carrying .obligationMappings)
128
- * @param {object|null} [args.scanHealth] - scan.scanHealth, passed through verbatim; null if not supplied, never fabricated
129
- * @param {string|null} [args.engineVersion]
130
- * @param {string|null} [args.rulesetVersion]
131
- * @param {string|null} [args.bundleSha]
132
- * @param {string} [args.generatedAt] - defaults to new Date().toISOString()
133
- */
134
- function buildObligationEvidencePack({
135
- graph, framework, evaluation, scanHealth, engineVersion, rulesetVersion, bundleSha, generatedAt,
136
- } = {}) {
137
- const facts = _asArray(evaluation).flatMap((e) => _asArray(e?.obligationMappings));
138
- const joins = _buildJoins(graph);
139
- const evidenceIndex = facts.map((fact) => ({
140
- obligationId: fact?.id ?? null,
141
- requirementId: fact?.requirementId ?? null,
142
- evidence: _resolveEvidenceForFact(fact, joins),
143
- }));
144
-
145
- return {
146
- schema: OBLIGATION_EVIDENCE_PACK_SCHEMA,
147
- framework: {
148
- id: framework?.id ?? null,
149
- name: framework?.name ?? null,
150
- version: framework?.controlsDigest ?? null,
151
- publisher: framework?.publisher ?? null,
152
- url: framework?.url ?? null,
153
- },
154
- scope: graph?.scope ?? null,
155
- facts,
156
- evidenceIndex,
157
- unknownItems: facts.filter((f) => f?.state === 'unknown'),
158
- manualItems: facts.filter((f) => f?.state === 'manual_required'),
159
- acceptedExceptions: facts.filter((f) => f?.state === 'accepted_exception'),
160
- scanHealth: scanHealth ?? null,
161
- limitations: _asArray(graph?.limitations),
162
- graphDigest: graph ? (0,_lineage_export_json_js__WEBPACK_IMPORTED_MODULE_2__.computeGraphDigest)(graph) : null,
163
- reproducibility: {
164
- graphId: graph?.graphId ?? null,
165
- graphDigest: graph ? (0,_lineage_export_json_js__WEBPACK_IMPORTED_MODULE_2__.computeGraphDigest)(graph) : null,
166
- engineVersion: engineVersion ?? null,
167
- rulesetVersion: rulesetVersion ?? null,
168
- bundleSha: bundleSha ?? null,
169
- generatedAt: generatedAt ?? new Date().toISOString(),
170
- },
171
- disclaimer: _evidence_grade_wording_js__WEBPACK_IMPORTED_MODULE_1__/* .EVIDENCE_GRADE_DISCLAIMER */ .ux,
172
- };
173
- }
174
-
175
- /** Sign a pack. Returns a new object; the input is not mutated. */
176
- function signObligationEvidencePack(pack, privateKeyPem) {
177
- const sig = node_crypto__WEBPACK_IMPORTED_MODULE_3__.sign(null, Buffer.from((0,_evidence_bundle_js__WEBPACK_IMPORTED_MODULE_0__/* .canonicalJson */ .dj)(pack), 'utf8'), privateKeyPem);
178
- return {
179
- ...pack,
180
- signature: { algorithm: 'ed25519', canonicalisation: OBLIGATION_EVIDENCE_PACK_SCHEMA, value: sig.toString('base64') },
181
- };
182
- }
183
-
184
- const OBLIGATION_EVIDENCE_PACK_TOP_LEVEL_KEYS = new Set([
185
- 'schema', 'framework', 'scope', 'facts', 'evidenceIndex', 'unknownItems',
186
- 'manualItems', 'acceptedExceptions', 'scanHealth', 'limitations',
187
- 'graphDigest', 'reproducibility', 'disclaimer', 'signature',
188
- ]);
189
-
190
- /**
191
- * Verify with a PUBLIC key only. Rejects any top-level key outside the
192
- * allowlist BEFORE checking the signature — same EA-03 discipline every
193
- * sibling in this family carries: a signature only covers the bytes it
194
- * was computed over, so a key stapled on after signing would otherwise
195
- * verify as authentic.
196
- */
197
- function verifyObligationEvidencePack(pack, publicKeyPem) {
198
- if (!pack || typeof pack !== 'object') return { ok: false, reason: 'pack is not an object' };
199
- if (pack.schema !== OBLIGATION_EVIDENCE_PACK_SCHEMA) return { ok: false, reason: `unrecognised schema: ${pack.schema}` };
200
- const unknownKeys = Object.keys(pack).filter((k) => !OBLIGATION_EVIDENCE_PACK_TOP_LEVEL_KEYS.has(k));
201
- if (unknownKeys.length) {
202
- return { ok: false, reason: `unrecognised top-level key(s) not covered by the signature: ${unknownKeys.join(', ')}` };
203
- }
204
- const sig = pack.signature;
205
- if (!sig?.value) return { ok: false, reason: 'pack is unsigned' };
206
- if (sig.algorithm !== 'ed25519') return { ok: false, reason: `unsupported algorithm: ${sig.algorithm}` };
207
- if (!publicKeyPem) return { ok: false, reason: 'no public key supplied' };
208
- const { signature, ...unsigned } = pack;
209
- let ok = false;
210
- try {
211
- ok = node_crypto__WEBPACK_IMPORTED_MODULE_3__.verify(null, Buffer.from((0,_evidence_bundle_js__WEBPACK_IMPORTED_MODULE_0__/* .canonicalJson */ .dj)(unsigned), 'utf8'), publicKeyPem, Buffer.from(sig.value, 'base64'));
212
- } catch (e) {
213
- return { ok: false, reason: `verification error: ${e.message}` };
214
- }
215
- return ok
216
- ? { ok: true, reason: null }
217
- : { ok: false, reason: 'signature does not match the pack contents — it was modified after signing' };
218
- }
219
-
220
-
221
-
222
-
223
- /***/ })
224
-
225
- };