@clear-capabilities/agentic-security-scanner 0.172.1 → 0.173.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/bin/agentic-security.js +37 -0
  3. package/dist/119.index.js +197 -0
  4. package/dist/128.index.js +129 -0
  5. package/dist/1293.index.js +5689 -0
  6. package/dist/1351.index.js +133 -0
  7. package/dist/1387.index.js +271 -0
  8. package/dist/1437.index.js +23 -0
  9. package/dist/1445.index.js +110 -0
  10. package/dist/1505.index.js +151 -0
  11. package/dist/1557.index.js +293 -0
  12. package/dist/1621.index.js +311 -0
  13. package/dist/1751.index.js +160 -0
  14. package/dist/1765.index.js +727 -0
  15. package/dist/1861.index.js +690 -0
  16. package/dist/1898.index.js +116 -0
  17. package/dist/1906.index.js +433 -0
  18. package/dist/1920.index.js +1 -291
  19. package/dist/208.index.js +1631 -0
  20. package/dist/2199.index.js +0 -239
  21. package/dist/2223.index.js +159 -0
  22. package/dist/2530.index.js +152 -0
  23. package/dist/2565.index.js +116 -0
  24. package/dist/2574.index.js +127 -0
  25. package/dist/2620.index.js +345 -0
  26. package/dist/2773.index.js +98 -0
  27. package/dist/2775.index.js +32 -0
  28. package/dist/2966.index.js +171 -0
  29. package/dist/2972.index.js +87 -0
  30. package/dist/2991.index.js +131 -0
  31. package/dist/3063.index.js +833 -0
  32. package/dist/3075.index.js +227 -0
  33. package/dist/3121.index.js +297 -0
  34. package/dist/3314.index.js +263 -0
  35. package/dist/3480.index.js +1822 -0
  36. package/dist/3481.index.js +24 -0
  37. package/dist/3506.index.js +268 -0
  38. package/dist/3533.index.js +591 -0
  39. package/dist/3653.index.js +450 -0
  40. package/dist/3822.index.js +131 -0
  41. package/dist/3868.index.js +292 -0
  42. package/dist/3917.index.js +152 -0
  43. package/dist/3918.index.js +192 -0
  44. package/dist/4090.index.js +288 -0
  45. package/dist/4121.index.js +1190 -0
  46. package/dist/4142.index.js +0 -239
  47. package/dist/4336.index.js +436 -0
  48. package/dist/4387.index.js +532 -0
  49. package/dist/4413.index.js +302 -0
  50. package/dist/4476.index.js +4510 -0
  51. package/dist/4491.index.js +133 -0
  52. package/dist/4538.index.js +185 -0
  53. package/dist/4684.index.js +319 -0
  54. package/dist/4740.index.js +165 -0
  55. package/dist/4780.index.js +209 -0
  56. package/dist/4802.index.js +225 -0
  57. package/dist/4812.index.js +379 -0
  58. package/dist/4835.index.js +126 -0
  59. package/dist/494.index.js +226 -0
  60. package/dist/4991.index.js +549 -0
  61. package/dist/5199.index.js +305 -0
  62. package/dist/5216.index.js +109 -0
  63. package/dist/5439.index.js +301 -0
  64. package/dist/5443.index.js +97 -0
  65. package/dist/5496.index.js +377 -0
  66. package/dist/550.index.js +2 -1
  67. package/dist/5801.index.js +588 -0
  68. package/dist/5858.index.js +223 -0
  69. package/dist/5933.index.js +143 -0
  70. package/dist/601.index.js +23 -0
  71. package/dist/6173.index.js +273 -0
  72. package/dist/6291.index.js +1871 -0
  73. package/dist/6312.index.js +666 -0
  74. package/dist/634.index.js +16182 -0
  75. package/dist/644.index.js +87 -0
  76. package/dist/6508.index.js +283 -0
  77. package/dist/6560.index.js +645 -0
  78. package/dist/6576.index.js +422 -0
  79. package/dist/6579.index.js +271 -0
  80. package/dist/6745.index.js +305 -0
  81. package/dist/6818.index.js +21642 -0
  82. package/dist/6972.index.js +167 -0
  83. package/dist/6995.index.js +163 -0
  84. package/dist/7249.index.js +191 -0
  85. package/dist/7372.index.js +1197 -0
  86. package/dist/7546.index.js +541 -0
  87. package/dist/76.index.js +448 -0
  88. package/dist/7879.index.js +163 -0
  89. package/dist/8025.index.js +130 -0
  90. package/dist/8054.index.js +248 -0
  91. package/dist/8097.index.js +201 -0
  92. package/dist/8126.index.js +191 -0
  93. package/dist/8178.index.js +78 -0
  94. package/dist/818.index.js +212 -0
  95. package/dist/8382.index.js +93 -0
  96. package/dist/8408.index.js +261 -0
  97. package/dist/8540.index.js +174 -0
  98. package/dist/8635.index.js +802 -0
  99. package/dist/8688.index.js +410 -0
  100. package/dist/8699.index.js +779 -0
  101. package/dist/8891.index.js +457 -0
  102. package/dist/8916.index.js +885 -0
  103. package/dist/9146.index.js +170 -0
  104. package/dist/9168.index.js +841 -0
  105. package/dist/9292.index.js +369 -0
  106. package/dist/9382.index.js +135 -11
  107. package/dist/9422.index.js +866 -0
  108. package/dist/9440.index.js +453 -0
  109. package/dist/9479.index.js +152 -0
  110. package/dist/9481.index.js +220 -0
  111. package/dist/9539.index.js +100 -0
  112. package/dist/9547.index.js +103 -0
  113. package/dist/9560.index.js +41 -61
  114. package/dist/9692.index.js +90 -0
  115. package/dist/9800.index.js +213 -1
  116. package/dist/9899.index.js +138 -0
  117. package/dist/9981.index.js +206 -0
  118. package/dist/999.index.js +117 -0
  119. package/dist/agentic-security.mjs +27 -27
  120. package/dist/agentic-security.mjs.sha256 +1 -1
  121. package/package.json +28 -3
  122. package/src/context-intelligence/candidates.js +2 -1
  123. package/src/context-intelligence/capabilities.js +2 -1
  124. package/src/context-intelligence/cli.js +2 -0
  125. package/src/context-intelligence/config.js +3 -1
  126. package/src/context-intelligence/controls.js +49 -1
  127. package/src/context-intelligence/counterexample.js +200 -0
  128. package/src/context-intelligence/cross-route.js +111 -0
  129. package/src/context-intelligence/findings.js +166 -0
  130. package/src/context-intelligence/invariants.js +197 -0
  131. package/src/context-intelligence/queue.js +6 -1
  132. package/src/context-intelligence/reachability.js +2 -2
  133. package/src/context-intelligence/review.js +68 -4
  134. package/src/dataflow/access-paths.js +11 -0
  135. package/src/dataflow/analysis-limits.js +66 -0
  136. package/src/dataflow/engine.js +272 -20
  137. package/src/dataflow/service-edges.js +174 -0
  138. package/src/dataflow/summaries.js +7 -1
  139. package/src/discovery/judge.js +2 -1
  140. package/src/engine.js +82 -14
  141. package/src/fix/apply-fix-service.js +55 -8
  142. package/src/fix/batch-journal.js +84 -0
  143. package/src/fix/exploit-replay.js +51 -0
  144. package/src/fix/footprint.js +65 -0
  145. package/src/fix/patch-policy.js +49 -0
  146. package/src/fix/secret-persistence.js +39 -0
  147. package/src/fix/test-commands.js +84 -0
  148. package/src/fix/verification-adapters.js +97 -0
  149. package/src/fix/verification-record.js +243 -0
  150. package/src/fix/verify-patch.js +89 -0
  151. package/src/ir/CLAUDE.md +5 -0
  152. package/src/ir/callgraph.js +23 -1
  153. package/src/ir/module-resolver.js +315 -0
  154. package/src/ir/parser-js.js +21 -2
  155. package/src/ir/semantic-graph.js +245 -0
  156. package/src/lineage/build-attested-correlation.js +63 -0
  157. package/src/lineage/deployment-identity.js +76 -0
  158. package/src/mcp/tools.js +13 -34
  159. package/src/pipeline/canonical-output.js +64 -0
  160. package/src/pipeline/detector-runner.js +7 -0
  161. package/src/pipeline/scan-health.js +14 -0
  162. package/src/pipeline/supervised-analyzers.js +125 -0
  163. package/src/pipeline/supervised-child.js +17 -0
  164. package/src/pipeline/telemetry.js +79 -0
  165. package/src/posture/accuracy-scorecard.js +98 -9
  166. package/src/posture/artifact-registry.js +5 -0
  167. package/src/posture/assurance/evidence-registry.js +215 -0
  168. package/src/posture/assurance/release-package-verify.js +135 -0
  169. package/src/posture/assurance/release-package.js +118 -0
  170. package/src/posture/assurance/release-verdict.js +94 -0
  171. package/src/posture/assurance/waiver.js +200 -0
  172. package/src/posture/compliance-truth.js +100 -0
  173. package/src/posture/evaluation/scan-child.js +3 -1
  174. package/src/posture/evaluation/why-missed.js +3 -1
  175. package/src/posture/exploitability-evidence.js +141 -0
  176. package/src/posture/fix-verify.js +4 -2
  177. package/src/posture/fleet/exposure-ranking.js +115 -0
  178. package/src/posture/fleet/service-chain-graph.js +150 -0
  179. package/src/posture/graph-reconciliation.js +154 -0
  180. package/src/posture/provenance/coverage.js +103 -0
  181. package/src/posture/provenance/git-evidence.js +84 -0
  182. package/src/posture/provenance/origin-resolver.js +7 -1
  183. package/src/posture/provenance/predicate-replay.js +14 -4
  184. package/src/posture/runtime-correlation.js +61 -15
  185. package/src/posture/runtime-explanation.js +116 -0
  186. package/src/posture/runtime-trace-ingest.js +194 -0
  187. package/src/posture/scan-checkpoint.js +38 -10
  188. package/src/posture/waf-ingest.js +7 -2
  189. package/src/report/index.js +4 -0
  190. package/src/report/oscal.js +38 -0
  191. package/src/report/perspectives.js +150 -0
  192. package/src/sast/_comment-strip.js +8 -1
  193. package/src/sast/ruby.js +26 -0
  194. package/src/util/string-memo.js +38 -0
@@ -0,0 +1,98 @@
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
+ };
@@ -0,0 +1,32 @@
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
+ };
@@ -0,0 +1,171 @@
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
+ };
@@ -0,0 +1,87 @@
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
+ };
@@ -0,0 +1,131 @@
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
+ };