@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,98 +0,0 @@
1
- export const id = 2773;
2
- export const ids = [2773];
3
- export const modules = {
4
-
5
- /***/ 2773:
6
- /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
7
-
8
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9
- /* harmony export */ persistLanguageArtifacts: () => (/* binding */ persistLanguageArtifacts)
10
- /* harmony export */ });
11
- /* unused harmony exports LANGUAGE_ANALYSIS_FILE, LANGUAGE_BOM_FILE, LANGUAGE_ARTIFACT_SCHEMA, hasLanguageContent, buildLanguageArtifacts */
12
- /* harmony import */ var node_crypto__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(77598);
13
- /* harmony import */ var _posture_state_dir_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(69259);
14
- /* harmony import */ var _posture_integrity_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(37455);
15
- /* harmony import */ var _context_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(70634);
16
- // Haskell and Nix analysis metadata as registered state artifacts (X-016).
17
- //
18
- // An ordinary scan already leaves last-scan.json. Two things a reviewer, auditor or retention policy needs about the
19
- // Haskell/Nix side were not artifacts of their own: WHAT analysed the sources (parser/resolver outcomes, capability
20
- // status, limitations, optional modes) and WHICH components the language BOM recorded. They are written beside
21
- // last-scan.json as `language-analysis.json` and `language-bom.json`, each with a detached signature, registered in
22
- // posture/artifact-registry.js (so export, retention, legal hold and `reset` all see them), and described by a record
23
- // that carries sha256 digests of every input and of the artifact bodies. No source text is ever written: paths and
24
- // digests only, so the file is metadata, not a copy of customer code.
25
-
26
-
27
-
28
-
29
-
30
-
31
- const LANGUAGE_ANALYSIS_FILE = 'language-analysis.json';
32
- const LANGUAGE_BOM_FILE = 'language-bom.json';
33
- const LANGUAGE_ARTIFACT_SCHEMA = 'agentic-security/language-analysis@1';
34
-
35
- const sha256 = (s) => node_crypto__WEBPACK_IMPORTED_MODULE_0__.createHash('sha256').update(s).digest('hex');
36
- function canonical(v) {
37
- if (Array.isArray(v)) return `[${v.map(canonical).join(',')}]`;
38
- if (v && typeof v === 'object') return `{${Object.keys(v).sort().map((k) => `${JSON.stringify(k)}:${canonical(v[k])}`).join(',')}}`;
39
- return JSON.stringify(v === undefined ? null : v);
40
- }
41
-
42
- /** True when the scan has anything Haskell/Nix to record. */
43
- function hasLanguageContent(scan) {
44
- const lc = scan && scan.scanHealth && scan.scanHealth.languageCoverage;
45
- const files = lc && lc.totals && Number(lc.totals.files || lc.totals.analyzed || 0);
46
- return !!((lc && (files > 0 || (lc.capabilities && Object.keys(lc.capabilities).length))) || (scan && scan.languageBom && (scan.languageBom.components || []).length));
47
- }
48
-
49
- /**
50
- * The analysis record for `scan`, plus the BOM body. `inputs` is [{path, sha256}] over the Haskell/Nix sources and
51
- * manifests (digests only). Pure: no I/O.
52
- */
53
- function buildLanguageArtifacts(scan, inputs = []) {
54
- const lc = (scan && scan.scanHealth && scan.scanHealth.languageCoverage) || null;
55
- const bom = (scan && scan.languageBom) || null;
56
- const bridges = (scan && scan.languageBridges) || null;
57
- const bomBody = bom ? JSON.stringify(bom, null, 2) : null;
58
- const record = {
59
- schema: LANGUAGE_ARTIFACT_SCHEMA,
60
- classification: { class: 'scan-metadata', containsSourceText: false, containsSecrets: false },
61
- inputs: inputs.map((i) => ({ path: String(i.path), sha256: String(i.sha256) })).sort((a, b) => (a.path < b.path ? -1 : 1)),
62
- analysis: lc ? { totals: lc.totals, byKind: lc.byKind, conditions: lc.conditions, limitations: lc.limitations, capabilities: lc.capabilities, optionalModes: lc.optionalModes } : null,
63
- bom: bom ? { file: LANGUAGE_BOM_FILE, sha256: sha256(bomBody), components: (bom.components || []).length } : null,
64
- bridges: bridges ? { sha256: sha256(canonical(bridges)) } : null,
65
- };
66
- return { record, bomBody };
67
- }
68
-
69
- /**
70
- * Writes the artifacts under the project's state directory, each with a `.sig`. Returns {written:[{name, sha256}], skipped}.
71
- * Never throws and never blocks a scan; a project that disallows state writes gets `skipped: 'state-writes-disabled'`.
72
- */
73
- function persistLanguageArtifacts(root, scan) {
74
- if (!hasLanguageContent(scan)) return { written: [], skipped: 'no-language-content' };
75
- let inputs = [];
76
- try {
77
- const proj = (0,_context_js__WEBPACK_IMPORTED_MODULE_3__.loadLanguageProject)(root);
78
- inputs = [...Object.entries(proj.files), ...Object.entries(proj.manifests)].map(([path, text]) => ({ path, sha256: sha256(text) }));
79
- } catch { /* the record then says it has no input digests */ }
80
- const { record, bomBody } = buildLanguageArtifacts(scan, inputs);
81
- const out = [];
82
- const put = (name, body) => {
83
- const fp = (0,_posture_state_dir_js__WEBPACK_IMPORTED_MODULE_1__.statePath)(root, name);
84
- if (!(0,_posture_state_dir_js__WEBPACK_IMPORTED_MODULE_1__.safeWriteState)(fp, body)) return false;
85
- try { (0,_posture_state_dir_js__WEBPACK_IMPORTED_MODULE_1__.safeWriteState)(`${fp}.sig`, (0,_posture_integrity_js__WEBPACK_IMPORTED_MODULE_2__/* .signLastScan */ .lU)(body)); } catch { /* the signature is best effort, as for last-scan.json */ }
86
- out.push({ name, sha256: sha256(body) });
87
- return true;
88
- };
89
- const body = JSON.stringify(record, null, 2);
90
- if (!put(LANGUAGE_ANALYSIS_FILE, body)) return { written: [], skipped: 'state-writes-disabled' };
91
- if (bomBody) put(LANGUAGE_BOM_FILE, bomBody);
92
- return { written: out, skipped: null };
93
- }
94
-
95
-
96
- /***/ })
97
-
98
- };
@@ -1,32 +0,0 @@
1
- export const id = 2775;
2
- export const ids = [2775];
3
- export const modules = {
4
-
5
- /***/ 72775:
6
- /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
7
-
8
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9
- /* harmony export */ correlateImageVulnerabilities: () => (/* binding */ correlateImageVulnerabilities)
10
- /* harmony export */ });
11
- // PRD 5 P0 §4 — Container Vulnerability Analysis. "Reuse current SCA
12
- // semantics where technically valid": this module does nothing but hand an
13
- // image's OS + application package inventory to the engine's existing
14
- // OSV/KEV/EPSS query path (`queryOSV`, imported via `src/sca/index.js`),
15
- // exactly as every other SCA finding in this codebase gets its CVE data.
16
- // `queryOSVFn` is injected (not imported directly) so this module's own
17
- // tests never make a real network call and stay independent of engine.js's
18
- // load cost; the CLI wiring passes the real `queryOSV`.
19
- async function correlateImageVulnerabilities({ inventory, fileContents, queryOSVFn }) {
20
- if (typeof queryOSVFn !== 'function') throw new TypeError('correlateImageVulnerabilities requires queryOSVFn(components, allFileContents)');
21
- if (!inventory || !Array.isArray(inventory.osPackages) || !Array.isArray(inventory.appPackages)) {
22
- throw new TypeError('correlateImageVulnerabilities requires an inventory with osPackages/appPackages arrays');
23
- }
24
- const components = [...inventory.osPackages, ...inventory.appPackages];
25
- const vulnerabilities = await queryOSVFn(components, fileContents || {});
26
- return { components, vulnerabilities };
27
- }
28
-
29
-
30
- /***/ })
31
-
32
- };
@@ -1,171 +0,0 @@
1
- export const id = 2966;
2
- export const ids = [2966];
3
- export const modules = {
4
-
5
- /***/ 72966:
6
- /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
7
-
8
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9
- /* harmony export */ queryFindingsMemory: () => (/* binding */ queryFindingsMemory)
10
- /* harmony export */ });
11
- /* unused harmony export _internals */
12
- /* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(73024);
13
- /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(76760);
14
- /* harmony import */ var _state_dir_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(69259);
15
- // Findings memory — natural-language Q&A over the institutional knowledge
16
- // the scanner has accumulated. Backs the MCP query_findings_memory tool.
17
- //
18
- // Sources searched, in this order:
19
- //
20
- // 1. .agentic-security/last-scan.json current findings
21
- // 2. .agentic-security/triage-memory.jsonl past wont-fix / FP decisions
22
- // 3. .agentic-security/scan-history/*.json prior scans
23
- // 4. .agentic-security/AGENTS.md continual-learning narrative
24
- //
25
- // Naive keyword matching for v1. Each match has a `score` (count of query
26
- // terms matched) and a `source` ('finding' | 'triage' | 'history' |
27
- // 'agents-md'). Returns top-10 by score.
28
-
29
-
30
-
31
-
32
-
33
-
34
- function _read(scanRoot, name) {
35
- try { return node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync((0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__.statePath)(scanRoot, name), 'utf8'); } catch { return null; }
36
- }
37
-
38
- function _readJson(scanRoot, name) {
39
- const raw = _read(scanRoot, name);
40
- if (!raw) return null;
41
- try { return JSON.parse(raw); } catch { return null; }
42
- }
43
-
44
- function _terms(query) {
45
- return String(query || '').toLowerCase().split(/\s+/).filter(t => t.length >= 2);
46
- }
47
-
48
- function _score(haystack, terms) {
49
- const lower = String(haystack || '').toLowerCase();
50
- let s = 0;
51
- for (const t of terms) if (lower.includes(t)) s++;
52
- return s;
53
- }
54
-
55
- function _findingHaystack(f) {
56
- return [f.vuln, f.family, f.file, f.severity, f.description, f.cwe, f.id]
57
- .filter(Boolean).join(' | ');
58
- }
59
-
60
- function _truncate(s, n = 160) {
61
- return String(s || '').replace(/\s+/g, ' ').slice(0, n);
62
- }
63
-
64
- /**
65
- * Run a natural-language query over the scanner's accumulated memory.
66
- */
67
- function queryFindingsMemory(scanRoot, query) {
68
- const terms = _terms(query);
69
- if (!terms.length) return { results: [], count: 0 };
70
-
71
- const results = [];
72
-
73
- // 1. Current findings.
74
- const scan = _readJson(scanRoot, 'last-scan.json');
75
- if (scan && Array.isArray(scan.findings)) {
76
- for (const f of scan.findings) {
77
- const hay = _findingHaystack(f);
78
- const score = _score(hay, terms);
79
- if (!score) continue;
80
- results.push({
81
- source: 'finding',
82
- score,
83
- finding_id: f.id || null,
84
- severity: f.severity,
85
- family: f.family,
86
- file: f.file,
87
- line: f.line,
88
- snippet: _truncate(f.vuln || f.description || f.family),
89
- });
90
- }
91
- }
92
-
93
- // 2. Triage memory (past decisions).
94
- const triageRaw = _read(scanRoot, 'triage-memory.jsonl');
95
- if (triageRaw) {
96
- const lines = triageRaw.split('\n').filter(Boolean);
97
- for (const ln of lines) {
98
- let entry; try { entry = JSON.parse(ln); } catch { continue; }
99
- const hay = [entry.decision, entry.reason, entry.family, entry.vuln, entry.file].join(' ');
100
- const score = _score(hay, terms);
101
- if (!score) continue;
102
- results.push({
103
- source: 'triage',
104
- score,
105
- decision: entry.decision,
106
- at: entry.at,
107
- family: entry.family,
108
- snippet: _truncate(entry.reason || entry.vuln),
109
- bucket: entry.bucket,
110
- });
111
- }
112
- }
113
-
114
- // 3. Scan history.
115
- try {
116
- const histDir = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__.statePath)(scanRoot, 'scan-history');
117
- if (node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(histDir)) {
118
- const files = node_fs__WEBPACK_IMPORTED_MODULE_0__.readdirSync(histDir).filter(f => f.endsWith('.json')).slice(-10);
119
- for (const f of files) {
120
- try {
121
- const hist = JSON.parse(node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(node_path__WEBPACK_IMPORTED_MODULE_1__.join(histDir, f), 'utf8'));
122
- if (!Array.isArray(hist.findings)) continue;
123
- for (const x of hist.findings.slice(0, 50)) {
124
- const hay = _findingHaystack(x);
125
- const score = _score(hay, terms);
126
- if (!score) continue;
127
- results.push({
128
- source: 'history',
129
- score,
130
- from: f.replace(/\.json$/, ''),
131
- severity: x.severity,
132
- family: x.family,
133
- file: x.file,
134
- snippet: _truncate(x.vuln || x.description),
135
- });
136
- }
137
- } catch {}
138
- }
139
- }
140
- } catch {}
141
-
142
- // 4. AGENTS.md narrative.
143
- const agents = _read(scanRoot, 'AGENTS.md');
144
- if (agents) {
145
- const sections = agents.split(/^##\s+/m);
146
- for (const sec of sections) {
147
- const score = _score(sec, terms);
148
- if (!score) continue;
149
- const title = sec.split('\n')[0] || '';
150
- results.push({
151
- source: 'agents-md',
152
- score,
153
- title: _truncate(title, 80),
154
- snippet: _truncate(sec.replace(title, ''), 200),
155
- });
156
- }
157
- }
158
-
159
- // Top-10 by score, ties broken by source priority (finding > triage >
160
- // history > agents-md so live data wins).
161
- const PRI = { finding: 4, triage: 3, history: 2, 'agents-md': 1 };
162
- results.sort((a, b) => (b.score - a.score) || (PRI[b.source] - PRI[a.source]));
163
- return { results: results.slice(0, 10), count: results.length };
164
- }
165
-
166
- const _internals = { _terms, _score, _findingHaystack };
167
-
168
-
169
- /***/ })
170
-
171
- };
@@ -1,87 +0,0 @@
1
- export const id = 2972;
2
- export const ids = [2972];
3
- export const modules = {
4
-
5
- /***/ 72972:
6
- /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
7
-
8
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9
- /* harmony export */ checkSecretLive: () => (/* binding */ checkSecretLive)
10
- /* harmony export */ });
11
- /* unused harmony export _internal */
12
- // Live-secret validation (#22) — label a detected secret live | dead | unknown.
13
- //
14
- // "This Stripe/GitHub key is LIVE and was committed 40 commits ago" is a P0 the
15
- // vibecoder must rotate now; "you have a high-entropy string" is noise. This
16
- // closes that gap for the providers with a cheap, read-only "whoami" check.
17
- //
18
- // STRICTLY opt-in (a --validate-secrets flag / AGENTIC_SECURITY_VALIDATE_SECRETS)
19
- // and OFFLINE-DEGRADING: any network error, timeout, or unrecognized provider
20
- // yields 'unknown' — never a false 'dead'. No runtime cloud calls by default,
21
- // per the scanner's no-network-by-default convention. The request builder is
22
- // pure (no I/O) so it's testable without hitting a provider.
23
-
24
- // Map a detected secret to a read-only validation request, or null when we have
25
- // no safe check for that provider. Only providers whose token is a self-
26
- // contained bearer/token credential (no signing, no extra params) are covered.
27
- function buildLiveCheckRequest(secret) {
28
- const val = (secret && (secret.match || secret.value || secret.secret || secret.token)) || '';
29
- if (typeof val !== 'string' || val.length < 8) return null;
30
-
31
- // GitHub PAT / OAuth token → GET /user (200 = live, 401 = dead).
32
- if (/^gh[posru]_[A-Za-z0-9]{20,}$/.test(val) || /^github_pat_[A-Za-z0-9_]{20,}$/.test(val)) {
33
- return { provider: 'github', method: 'GET', url: 'https://api.github.com/user',
34
- headers: { Authorization: `token ${val}`, 'User-Agent': 'agentic-security', Accept: 'application/vnd.github+json' } };
35
- }
36
- // Stripe secret key → GET /v1/account (200 = live, 401 = dead).
37
- if (/^sk_live_[A-Za-z0-9]{16,}$/.test(val) || /^rk_live_[A-Za-z0-9]{16,}$/.test(val)) {
38
- return { provider: 'stripe', method: 'GET', url: 'https://api.stripe.com/v1/account',
39
- headers: { Authorization: `Bearer ${val}` } };
40
- }
41
- // OpenAI key → GET /v1/models.
42
- if (/^sk-[A-Za-z0-9]{20,}$/.test(val) && !/^sk_live_/.test(val)) {
43
- return { provider: 'openai', method: 'GET', url: 'https://api.openai.com/v1/models',
44
- headers: { Authorization: `Bearer ${val}` } };
45
- }
46
- // SendGrid key → GET /v3/scopes.
47
- if (/^SG\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}$/.test(val)) {
48
- return { provider: 'sendgrid', method: 'GET', url: 'https://api.sendgrid.com/v3/scopes',
49
- headers: { Authorization: `Bearer ${val}` } };
50
- }
51
- return null;
52
- }
53
-
54
- // Classify an HTTP status into a liveness verdict. 200-2xx = live; 401/403 =
55
- // dead (rejected credential); anything else = unknown (rate-limit, 5xx, etc. —
56
- // we don't know, so don't claim dead).
57
- function classifyStatus(status) {
58
- if (status >= 200 && status < 300) return 'live';
59
- if (status === 401 || status === 403) return 'dead';
60
- return 'unknown';
61
- }
62
-
63
- // Perform the validation. Returns { verdict: 'live'|'dead'|'unknown', provider }.
64
- // Offline-degrading: on any error/timeout, verdict is 'unknown'.
65
- async function checkSecretLive(secret, { timeoutMs = 4000 } = {}) {
66
- const req = buildLiveCheckRequest(secret);
67
- if (!req) return { verdict: 'unknown', provider: null };
68
- const ctrl = new AbortController();
69
- const t = setTimeout(() => ctrl.abort(), timeoutMs);
70
- try {
71
- const r = await fetch(req.url, { method: req.method, headers: req.headers, signal: ctrl.signal });
72
- return { verdict: classifyStatus(r.status), provider: req.provider };
73
- } catch {
74
- return { verdict: 'unknown', provider: req.provider };
75
- } finally {
76
- clearTimeout(t);
77
- }
78
- }
79
-
80
- // Pure surfaces exposed for tests (no network) — kept off the public API so the
81
- // dead-module guard doesn't flag them; `checkSecretLive` is the wired entry.
82
- const _internal = { buildLiveCheckRequest, classifyStatus };
83
-
84
-
85
- /***/ })
86
-
87
- };
@@ -1,131 +0,0 @@
1
- export const id = 2991;
2
- export const ids = [2991];
3
- export const modules = {
4
-
5
- /***/ 2991:
6
- /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
7
-
8
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9
- /* harmony export */ diffScans: () => (/* binding */ diffScans),
10
- /* harmony export */ renderDiff: () => (/* binding */ renderDiff)
11
- /* harmony export */ });
12
- /* unused harmony export summarizeDiff */
13
- // Scan-result baseline comparison.
14
- //
15
- // Distinct from agentic-security-diff (which runs two SCANNER VERSIONS
16
- // against the same code). This module diffs TWO SCAN RESULTS, regardless
17
- // of scanner version — i.e., it compares findings between yesterday's
18
- // scan and today's scan to surface what was introduced / what was fixed.
19
- //
20
- // Keys per-finding on `stableId` when present (refactor-stable), else on
21
- // `(file, line, family)`. Output:
22
- // {
23
- // added: [...], // present in current, absent in previous
24
- // removed: [...], // present in previous, absent in current
25
- // changed: [...], // same key, different severity / verdict
26
- // unchanged: <count>
27
- // }
28
-
29
- function _keyOf(f) {
30
- if (!f || typeof f !== 'object') return '';
31
- if (f.stableId) return `sid:${f.stableId}`;
32
- return `pos:${f.file || '?'}:${f.line || 0}:${f.family || f.vuln || '?'}`;
33
- }
34
-
35
- function _indexBy(findings, keyFn) {
36
- const m = new Map();
37
- if (!Array.isArray(findings)) return m;
38
- for (const f of findings) {
39
- const k = keyFn(f);
40
- if (!k) continue;
41
- if (!m.has(k)) m.set(k, f);
42
- }
43
- return m;
44
- }
45
-
46
- function diffScans(previous, current) {
47
- const prevFindings = (previous && Array.isArray(previous.findings)) ? previous.findings : [];
48
- const currFindings = (current && Array.isArray(current.findings)) ? current.findings : [];
49
- const prevIdx = _indexBy(prevFindings, _keyOf);
50
- const currIdx = _indexBy(currFindings, _keyOf);
51
-
52
- const added = [], removed = [], changed = [];
53
- let unchanged = 0;
54
- for (const [k, c] of currIdx) {
55
- const p = prevIdx.get(k);
56
- if (!p) { added.push(c); continue; }
57
- const sevChanged = p.severity !== c.severity;
58
- const verdictChanged = p.mitigationVerdict !== c.mitigationVerdict;
59
- const validatorChanged = p.validator_verdict !== c.validator_verdict;
60
- if (sevChanged || verdictChanged || validatorChanged) {
61
- changed.push({ before: p, after: c, fields: {
62
- severity: sevChanged ? [p.severity, c.severity] : null,
63
- mitigationVerdict: verdictChanged ? [p.mitigationVerdict, c.mitigationVerdict] : null,
64
- validator_verdict: validatorChanged ? [p.validator_verdict, c.validator_verdict] : null,
65
- }});
66
- } else {
67
- unchanged++;
68
- }
69
- }
70
- for (const [k, p] of prevIdx) {
71
- if (!currIdx.has(k)) removed.push(p);
72
- }
73
- return { added, removed, changed, unchanged };
74
- }
75
-
76
- function summarizeDiff(diff) {
77
- const bySev = { critical: { added: 0, removed: 0 }, high: { added: 0, removed: 0 }, medium: { added: 0, removed: 0 }, low: { added: 0, removed: 0 } };
78
- for (const f of diff.added) if (bySev[f.severity]) bySev[f.severity].added++;
79
- for (const f of diff.removed) if (bySev[f.severity]) bySev[f.severity].removed++;
80
- return {
81
- addedCount: diff.added.length,
82
- removedCount: diff.removed.length,
83
- changedCount: diff.changed.length,
84
- unchangedCount: diff.unchanged,
85
- bySeverity: bySev,
86
- };
87
- }
88
-
89
- function renderDiff(diff, opts = {}) {
90
- const color = opts.color !== false;
91
- const C = color ? { RED: '\x1b[31m', GREEN: '\x1b[32m', YELLOW: '\x1b[33m', DIM: '\x1b[2m', BOLD: '\x1b[1m', RESET: '\x1b[0m' } : { RED:'', GREEN:'', YELLOW:'', DIM:'', BOLD:'', RESET:'' };
92
- const sum = summarizeDiff(diff);
93
- const out = [];
94
- out.push('');
95
- out.push(`${C.BOLD}Scan-result diff${C.RESET}`);
96
- out.push(` ${C.RED}+${sum.addedCount} added${C.RESET} ${C.GREEN}-${sum.removedCount} removed${C.RESET} ${C.YELLOW}~${sum.changedCount} changed${C.RESET} ${C.DIM}${sum.unchangedCount} unchanged${C.RESET}`);
97
- out.push('');
98
- if (diff.added.length) {
99
- out.push(`${C.RED}${C.BOLD}Added (${diff.added.length})${C.RESET}`);
100
- for (const f of diff.added.slice(0, 25)) {
101
- out.push(` ${C.RED}+${C.RESET} [${(f.severity || '').toUpperCase()}] ${(f.vuln || '').slice(0, 60)} ${C.DIM}${f.file || '?'}:${f.line || 0}${C.RESET}`);
102
- }
103
- if (diff.added.length > 25) out.push(` ${C.DIM}... and ${diff.added.length - 25} more${C.RESET}`);
104
- out.push('');
105
- }
106
- if (diff.removed.length) {
107
- out.push(`${C.GREEN}${C.BOLD}Removed (${diff.removed.length})${C.RESET}`);
108
- for (const f of diff.removed.slice(0, 25)) {
109
- out.push(` ${C.GREEN}-${C.RESET} [${(f.severity || '').toUpperCase()}] ${(f.vuln || '').slice(0, 60)} ${C.DIM}${f.file || '?'}:${f.line || 0}${C.RESET}`);
110
- }
111
- if (diff.removed.length > 25) out.push(` ${C.DIM}... and ${diff.removed.length - 25} more${C.RESET}`);
112
- out.push('');
113
- }
114
- if (diff.changed.length) {
115
- out.push(`${C.YELLOW}${C.BOLD}Changed (${diff.changed.length})${C.RESET}`);
116
- for (const c of diff.changed.slice(0, 15)) {
117
- const sevDelta = c.fields.severity ? `${c.fields.severity[0]} → ${c.fields.severity[1]}` : '';
118
- const verdictDelta = c.fields.mitigationVerdict ? `verdict ${c.fields.mitigationVerdict[0]} → ${c.fields.mitigationVerdict[1]}` : '';
119
- const validatorDelta = c.fields.validator_verdict ? `validator ${c.fields.validator_verdict[0]} → ${c.fields.validator_verdict[1]}` : '';
120
- const delta = [sevDelta, verdictDelta, validatorDelta].filter(Boolean).join('; ');
121
- out.push(` ${C.YELLOW}~${C.RESET} ${(c.after.vuln || '').slice(0, 50)} ${C.DIM}${c.after.file || '?'}:${c.after.line || 0}${C.RESET} ${delta}`);
122
- }
123
- out.push('');
124
- }
125
- return out.join('\n');
126
- }
127
-
128
-
129
- /***/ })
130
-
131
- };