@clear-capabilities/agentic-security-scanner 0.185.0 → 0.186.2

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 (174) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/bin/agentic-security.js +66 -18
  3. package/dist/1128.index.js +25 -21
  4. package/dist/{5817.index.js → 1602.index.js} +378 -29
  5. package/dist/2584.index.js +1 -1
  6. package/dist/4171.index.js +2 -2
  7. package/dist/{0b301d45b231f144a1b4.json → 4d06c8944ae121d7085c.json} +8 -8
  8. package/dist/5752.index.js +466 -0
  9. package/dist/7405.index.js +1 -1
  10. package/dist/8158.index.js +1713 -0
  11. package/dist/8798.index.js +1 -1
  12. package/dist/9382.index.js +2 -2
  13. package/dist/9560.index.js +1 -1
  14. package/dist/9579.index.js +1 -1
  15. package/dist/agentic-security.mjs +29 -29
  16. package/dist/agentic-security.mjs.sha256 +1 -1
  17. package/package.json +8 -8
  18. package/src/dataflow/CLAUDE.md +6 -3
  19. package/src/dataflow/access-paths.js +12 -2
  20. package/src/dataflow/demand-query.js +279 -40
  21. package/src/dataflow/dep-fingerprint.js +1 -1
  22. package/src/dataflow/exploit-prover.js +6 -2
  23. package/src/dataflow/ir-cache.js +268 -0
  24. package/src/dataflow/privacy-taint.js +7 -0
  25. package/src/engine.js +62 -5
  26. package/src/ir/CLAUDE.md +4 -0
  27. package/src/ir/callgraph.js +36 -8
  28. package/src/ir/index.js +18 -4
  29. package/src/ir/parser-js.js +10 -0
  30. package/src/language/CLAUDE.md +13 -0
  31. package/src/language/nix-option-types.js +170 -0
  32. package/src/language/nix-script-taint.js +46 -3
  33. package/src/language/nix-secrets.js +20 -2
  34. package/src/lineage/CLAUDE.md +2 -1
  35. package/src/lineage/smt-orchestrator.js +47 -11
  36. package/src/lineage/smt-z3-backend.js +294 -0
  37. package/src/posture/CLAUDE.md +5 -1
  38. package/src/posture/business-logic.js +16 -7
  39. package/src/posture/dag-commands.js +442 -0
  40. package/src/posture/finding-defaults.js +19 -0
  41. package/src/posture/git-history.js +91 -1
  42. package/src/posture/proof-fabric/cli.js +1 -1
  43. package/src/posture/proof-fabric/container.js +2 -2
  44. package/src/posture/proof-fabric/generate-haskell.js +205 -0
  45. package/src/posture/proof-fabric/generate.js +6 -3
  46. package/src/posture/proof-fabric/reproducer.js +4 -2
  47. package/src/posture/proof-fabric/runner.js +4 -4
  48. package/src/sast/CLAUDE.md +2 -0
  49. package/src/sast/_call-args.js +70 -0
  50. package/src/sast/init-scope.js +87 -0
  51. package/src/sast/listen-bind-address.js +49 -0
  52. package/src/sast/redirect-target.js +106 -0
  53. package/src/sast/reflected-input.js +76 -0
  54. package/src/sast/send-file-root.js +27 -0
  55. package/src/sast/sensitive-data-logging.js +38 -1
  56. package/dist/119.index.js +0 -197
  57. package/dist/128.index.js +0 -129
  58. package/dist/1293.index.js +0 -5689
  59. package/dist/1336.index.js +0 -16331
  60. package/dist/1351.index.js +0 -133
  61. package/dist/1387.index.js +0 -271
  62. package/dist/1437.index.js +0 -23
  63. package/dist/1445.index.js +0 -110
  64. package/dist/1505.index.js +0 -151
  65. package/dist/1557.index.js +0 -293
  66. package/dist/1621.index.js +0 -311
  67. package/dist/1751.index.js +0 -160
  68. package/dist/1765.index.js +0 -727
  69. package/dist/1861.index.js +0 -690
  70. package/dist/1906.index.js +0 -433
  71. package/dist/208.index.js +0 -1631
  72. package/dist/2223.index.js +0 -159
  73. package/dist/2238.index.js +0 -295
  74. package/dist/2530.index.js +0 -152
  75. package/dist/2565.index.js +0 -116
  76. package/dist/2574.index.js +0 -127
  77. package/dist/2620.index.js +0 -345
  78. package/dist/2773.index.js +0 -98
  79. package/dist/2775.index.js +0 -32
  80. package/dist/2966.index.js +0 -171
  81. package/dist/2972.index.js +0 -87
  82. package/dist/2991.index.js +0 -131
  83. package/dist/3063.index.js +0 -833
  84. package/dist/3075.index.js +0 -227
  85. package/dist/3121.index.js +0 -297
  86. package/dist/3314.index.js +0 -263
  87. package/dist/3480.index.js +0 -1822
  88. package/dist/3481.index.js +0 -24
  89. package/dist/3506.index.js +0 -268
  90. package/dist/3533.index.js +0 -591
  91. package/dist/3653.index.js +0 -450
  92. package/dist/3740.index.js +0 -722
  93. package/dist/3822.index.js +0 -131
  94. package/dist/3868.index.js +0 -292
  95. package/dist/3918.index.js +0 -192
  96. package/dist/4090.index.js +0 -288
  97. package/dist/4113.index.js +0 -1286
  98. package/dist/4121.index.js +0 -1190
  99. package/dist/4142.index.js +0 -690
  100. package/dist/4168.index.js +0 -509
  101. package/dist/4281.index.js +0 -993
  102. package/dist/4336.index.js +0 -436
  103. package/dist/4387.index.js +0 -532
  104. package/dist/4413.index.js +0 -302
  105. package/dist/4476.index.js +0 -4510
  106. package/dist/4491.index.js +0 -133
  107. package/dist/4538.index.js +0 -185
  108. package/dist/4684.index.js +0 -319
  109. package/dist/4740.index.js +0 -165
  110. package/dist/4780.index.js +0 -209
  111. package/dist/4802.index.js +0 -225
  112. package/dist/4812.index.js +0 -379
  113. package/dist/4835.index.js +0 -126
  114. package/dist/494.index.js +0 -226
  115. package/dist/4991.index.js +0 -549
  116. package/dist/5199.index.js +0 -305
  117. package/dist/5216.index.js +0 -109
  118. package/dist/5439.index.js +0 -301
  119. package/dist/5443.index.js +0 -97
  120. package/dist/5496.index.js +0 -377
  121. package/dist/5738.index.js +0 -240
  122. package/dist/5801.index.js +0 -588
  123. package/dist/5858.index.js +0 -223
  124. package/dist/5933.index.js +0 -143
  125. package/dist/601.index.js +0 -23
  126. package/dist/6173.index.js +0 -273
  127. package/dist/6291.index.js +0 -1871
  128. package/dist/6312.index.js +0 -666
  129. package/dist/634.index.js +0 -16182
  130. package/dist/644.index.js +0 -87
  131. package/dist/6508.index.js +0 -283
  132. package/dist/6560.index.js +0 -645
  133. package/dist/6576.index.js +0 -422
  134. package/dist/6579.index.js +0 -271
  135. package/dist/6655.index.js +0 -3221
  136. package/dist/6745.index.js +0 -305
  137. package/dist/6818.index.js +0 -21642
  138. package/dist/6932.index.js +0 -1190
  139. package/dist/6972.index.js +0 -167
  140. package/dist/6995.index.js +0 -163
  141. package/dist/7249.index.js +0 -191
  142. package/dist/7372.index.js +0 -1197
  143. package/dist/7546.index.js +0 -541
  144. package/dist/76.index.js +0 -448
  145. package/dist/7879.index.js +0 -163
  146. package/dist/8025.index.js +0 -130
  147. package/dist/8054.index.js +0 -248
  148. package/dist/808.index.js +0 -1330
  149. package/dist/8097.index.js +0 -201
  150. package/dist/8126.index.js +0 -191
  151. package/dist/8178.index.js +0 -78
  152. package/dist/8382.index.js +0 -93
  153. package/dist/8408.index.js +0 -261
  154. package/dist/8540.index.js +0 -174
  155. package/dist/8635.index.js +0 -802
  156. package/dist/8688.index.js +0 -410
  157. package/dist/8699.index.js +0 -779
  158. package/dist/8891.index.js +0 -457
  159. package/dist/8916.index.js +0 -885
  160. package/dist/9146.index.js +0 -170
  161. package/dist/9168.index.js +0 -841
  162. package/dist/9292.index.js +0 -369
  163. package/dist/9422.index.js +0 -866
  164. package/dist/9440.index.js +0 -453
  165. package/dist/9479.index.js +0 -152
  166. package/dist/9481.index.js +0 -220
  167. package/dist/9539.index.js +0 -100
  168. package/dist/9547.index.js +0 -103
  169. package/dist/9692.index.js +0 -90
  170. package/dist/9800.index.js +0 -462
  171. package/dist/9899.index.js +0 -138
  172. package/dist/9981.index.js +0 -206
  173. package/dist/999.index.js +0 -117
  174. 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
- };