@clear-capabilities/agentic-security-scanner 0.77.0 → 0.79.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 (69) hide show
  1. package/bin/.agentic-security/findings.json +1907 -0
  2. package/bin/.agentic-security/last-scan.json +1907 -0
  3. package/bin/.agentic-security/last-scan.json.sig +1 -0
  4. package/bin/.agentic-security/scan-history.json +166 -0
  5. package/bin/.agentic-security/streak.json +20 -0
  6. package/bin/agentic-security.js +55 -9
  7. package/dist/178.index.js +1 -1
  8. package/dist/384.index.js +1 -1
  9. package/dist/476.index.js +5 -5
  10. package/dist/637.index.js +1 -1
  11. package/dist/700.index.js +138 -0
  12. package/dist/718.index.js +159 -0
  13. package/dist/824.index.js +126 -0
  14. package/dist/838.index.js +1 -1
  15. package/dist/985.index.js +5 -0
  16. package/dist/agentic-security.mjs +32 -32
  17. package/dist/agentic-security.mjs.sha256 +1 -1
  18. package/package.json +4 -4
  19. package/src/dataflow/async-sequencing.js +16 -7
  20. package/src/dataflow/builtin-summaries.js +131 -0
  21. package/src/dataflow/catalog.js +107 -0
  22. package/src/dataflow/cross-repo.js +75 -1
  23. package/src/dataflow/engine.js +181 -8
  24. package/src/dataflow/implicit-flow.js +24 -6
  25. package/src/dataflow/stub-aware-filter.js +69 -11
  26. package/src/dataflow/summaries.js +28 -3
  27. package/src/engine-parallel.js +70 -0
  28. package/src/engine.js +270 -19
  29. package/src/integrations/index.js +2 -1
  30. package/src/ir/callgraph.js +27 -7
  31. package/src/ir/index.js +22 -1
  32. package/src/ir/parser-go.js +403 -0
  33. package/src/ir/parser-js.js +2 -0
  34. package/src/ir/parser-php.js +330 -0
  35. package/src/ir/parser-py.helper.py +137 -11
  36. package/src/ir/parser-rb.js +309 -0
  37. package/src/llm-validator/index.js +7 -5
  38. package/src/mcp/audit.js +5 -0
  39. package/src/posture/calibration-drift.js +2 -1
  40. package/src/posture/calibration.js +16 -1
  41. package/src/posture/fix-history.js +8 -2
  42. package/src/posture/profile.js +4 -5
  43. package/src/posture/rule-overrides.js +2 -3
  44. package/src/posture/rule-pack-signing.js +2 -3
  45. package/src/posture/rule-synthesis.js +5 -6
  46. package/src/posture/security-trend.js +4 -7
  47. package/src/posture/state-dir.js +124 -0
  48. package/src/posture/streak.js +3 -0
  49. package/src/posture/suppressions.js +5 -8
  50. package/src/posture/triage.js +16 -5
  51. package/src/posture/validator-metrics.js +3 -6
  52. package/src/report/index.js +23 -2
  53. package/src/sast/cache-poisoning.js +77 -0
  54. package/src/sast/comparison-safety.js +73 -0
  55. package/src/sast/db-taint.js +78 -0
  56. package/src/sast/graphql.js +127 -0
  57. package/src/sast/llm-stored-prompt.js +57 -0
  58. package/src/sast/mutation-xss.js +43 -0
  59. package/src/sast/nosql-injection.js +5 -0
  60. package/src/sast/null-byte-injection.js +76 -0
  61. package/src/sast/redos-nfa.js +338 -0
  62. package/src/sast/rust.js +26 -0
  63. package/src/sast/sensitive-data-logging.js +73 -0
  64. package/src/sast/weak-password-hash.js +77 -0
  65. package/src/sast/weak-randomness.js +100 -0
  66. package/src/sca/binary-metadata.js +124 -0
  67. package/src/sca/llm-function-extract.js +107 -0
  68. package/src/sca/py-package-functions.js +118 -0
  69. package/src/sca/vendor-detect.js +144 -0
@@ -0,0 +1 @@
1
+ 5bb921912d508574fede241a7e16cd7ac261f415b200e9eff04093fa70426dd1
@@ -0,0 +1,166 @@
1
+ [
2
+ {
3
+ "timestamp": "2026-05-26T04:00:10.464Z",
4
+ "label": "scan",
5
+ "total": 4,
6
+ "critical": 0,
7
+ "high": 0,
8
+ "medium": 4,
9
+ "low": 0,
10
+ "kev": 0,
11
+ "ids": [
12
+ "toctou-fs:agentic-security-audit.js:55",
13
+ "toctou-fs:agentic-security-consistency.js:44",
14
+ "toctou-fs:agentic-security-consistency.js:66",
15
+ "toctou-fs:agentic-security.js:1108"
16
+ ]
17
+ },
18
+ {
19
+ "timestamp": "2026-05-26T04:00:56.905Z",
20
+ "label": "scan",
21
+ "total": 4,
22
+ "critical": 0,
23
+ "high": 0,
24
+ "medium": 4,
25
+ "low": 0,
26
+ "kev": 0,
27
+ "ids": [
28
+ "toctou-fs:agentic-security-audit.js:55",
29
+ "toctou-fs:agentic-security-consistency.js:44",
30
+ "toctou-fs:agentic-security-consistency.js:66",
31
+ "toctou-fs:agentic-security.js:1108"
32
+ ]
33
+ },
34
+ {
35
+ "timestamp": "2026-05-26T04:02:41.681Z",
36
+ "label": "scan",
37
+ "total": 4,
38
+ "critical": 0,
39
+ "high": 0,
40
+ "medium": 4,
41
+ "low": 0,
42
+ "kev": 0,
43
+ "ids": [
44
+ "toctou-fs:agentic-security-audit.js:55",
45
+ "toctou-fs:agentic-security-consistency.js:44",
46
+ "toctou-fs:agentic-security-consistency.js:66",
47
+ "toctou-fs:agentic-security.js:1108"
48
+ ]
49
+ },
50
+ {
51
+ "timestamp": "2026-05-27T01:03:34.318Z",
52
+ "label": "scan",
53
+ "total": 4,
54
+ "critical": 0,
55
+ "high": 0,
56
+ "medium": 4,
57
+ "low": 0,
58
+ "kev": 0,
59
+ "ids": [
60
+ "toctou-fs:agentic-security-audit.js:55",
61
+ "toctou-fs:agentic-security-consistency.js:44",
62
+ "toctou-fs:agentic-security-consistency.js:66",
63
+ "toctou-fs:agentic-security.js:1109"
64
+ ]
65
+ },
66
+ {
67
+ "timestamp": "2026-05-27T01:05:45.968Z",
68
+ "label": "scan",
69
+ "total": 4,
70
+ "critical": 0,
71
+ "high": 0,
72
+ "medium": 4,
73
+ "low": 0,
74
+ "kev": 0,
75
+ "ids": [
76
+ "toctou-fs:agentic-security-audit.js:55",
77
+ "toctou-fs:agentic-security-consistency.js:44",
78
+ "toctou-fs:agentic-security-consistency.js:66",
79
+ "toctou-fs:agentic-security.js:1114"
80
+ ]
81
+ },
82
+ {
83
+ "timestamp": "2026-05-27T11:21:25.101Z",
84
+ "label": "scan",
85
+ "total": 4,
86
+ "critical": 0,
87
+ "high": 0,
88
+ "medium": 4,
89
+ "low": 0,
90
+ "kev": 0,
91
+ "ids": [
92
+ "toctou-fs:agentic-security-audit.js:55",
93
+ "toctou-fs:agentic-security-consistency.js:44",
94
+ "toctou-fs:agentic-security-consistency.js:66",
95
+ "toctou-fs:agentic-security.js:1116"
96
+ ]
97
+ },
98
+ {
99
+ "timestamp": "2026-05-27T11:23:11.242Z",
100
+ "label": "scan",
101
+ "total": 5,
102
+ "critical": 0,
103
+ "high": 0,
104
+ "medium": 5,
105
+ "low": 0,
106
+ "kev": 0,
107
+ "ids": [
108
+ "toctou-fs:agentic-security-audit.js:55",
109
+ "toctou-fs:agentic-security-consistency.js:44",
110
+ "toctou-fs:agentic-security-consistency.js:66",
111
+ "toctou-fs:agentic-security.js:1136",
112
+ "toctou-fs:agentic-security.js:362"
113
+ ]
114
+ },
115
+ {
116
+ "timestamp": "2026-05-28T14:13:54.951Z",
117
+ "label": "scan",
118
+ "total": 5,
119
+ "critical": 0,
120
+ "high": 0,
121
+ "medium": 5,
122
+ "low": 0,
123
+ "kev": 0,
124
+ "ids": [
125
+ "toctou-fs:agentic-security-audit.js:55",
126
+ "toctou-fs:agentic-security-consistency.js:44",
127
+ "toctou-fs:agentic-security-consistency.js:66",
128
+ "toctou-fs:agentic-security.js:1141",
129
+ "toctou-fs:agentic-security.js:362"
130
+ ]
131
+ },
132
+ {
133
+ "timestamp": "2026-05-28T14:16:39.437Z",
134
+ "label": "scan",
135
+ "total": 5,
136
+ "critical": 0,
137
+ "high": 0,
138
+ "medium": 5,
139
+ "low": 0,
140
+ "kev": 0,
141
+ "ids": [
142
+ "toctou-fs:agentic-security-audit.js:55",
143
+ "toctou-fs:agentic-security-consistency.js:44",
144
+ "toctou-fs:agentic-security-consistency.js:66",
145
+ "toctou-fs:agentic-security.js:1146",
146
+ "toctou-fs:agentic-security.js:367"
147
+ ]
148
+ },
149
+ {
150
+ "timestamp": "2026-05-28T14:16:53.162Z",
151
+ "label": "scan",
152
+ "total": 5,
153
+ "critical": 0,
154
+ "high": 0,
155
+ "medium": 5,
156
+ "low": 0,
157
+ "kev": 0,
158
+ "ids": [
159
+ "toctou-fs:agentic-security-audit.js:55",
160
+ "toctou-fs:agentic-security-consistency.js:44",
161
+ "toctou-fs:agentic-security-consistency.js:66",
162
+ "toctou-fs:agentic-security.js:1151",
163
+ "toctou-fs:agentic-security.js:367"
164
+ ]
165
+ }
166
+ ]
@@ -0,0 +1,20 @@
1
+ {
2
+ "firstScanDate": "2026-05-26T04:00:10.482Z",
3
+ "lastScanDate": "2026-05-28T14:16:53.192Z",
4
+ "totalScans": 10,
5
+ "daysCleanCritical": 3,
6
+ "lastCleanDate": "2026-05-28",
7
+ "lastCriticalDate": null,
8
+ "hasEverHadCritical": false,
9
+ "bestDaysCleanCritical": 3,
10
+ "totalFindingsAtFirstScan": 11,
11
+ "totalFindingsAtLastScan": 13,
12
+ "totalFixesInferred": 0,
13
+ "lastGrade": "A-",
14
+ "bestGrade": "A-",
15
+ "launchCheckPassedAt": null,
16
+ "achievements": [
17
+ "first-scan"
18
+ ],
19
+ "previousGrade": "A-"
20
+ }
@@ -4,6 +4,9 @@
4
4
  import * as fs from 'node:fs';
5
5
  import * as fsp from 'node:fs/promises';
6
6
  import * as path from 'node:path';
7
+ import { createRequire } from 'node:module';
8
+ const __require = createRequire(import.meta.url);
9
+ const PKG_VERSION = __require('../package.json').version;
7
10
  import { signLastScan as _signLastScan, verifyLastScan as _verifyLastScanShared } from '../src/posture/integrity.js';
8
11
  import { runScan } from '../src/runScan.js';
9
12
  import { toJSON, toMarkdown, toSARIF, toSTIX, toCSV, toJUnit, toCLI, toCLIByProfile, toShipVerdict, toProTable, toHTML, toSummary, exitCodeFor, normalizeFindings } from '../src/report/index.js';
@@ -103,6 +106,9 @@ Options:
103
106
  --no-network Skip OSV/registry queries (offline mode)
104
107
  --pr [ref] Diff-aware: scan only files changed since ref (auto-detects PR base)
105
108
  --deterministic Reproducible scan: stable sort, no-network, lockfile-checked
109
+ --incremental Reuse taint summaries from prior scans (speeds up deep mode in CI)
110
+ --set-baseline Save current findings as baseline (suppresses pre-existing issues)
111
+ --since-baseline Only show findings NOT in the saved baseline
106
112
  --no-epss Skip EPSS exploit-prediction enrichment (default: enabled)
107
113
  --no-blast-radius Skip blast-radius / cost framing (default: enabled)
108
114
  --verbose Include fix bodies + taxonomy in CLI output
@@ -137,7 +143,7 @@ function printBanner(args) {
137
143
  BOLD: '\x1b[1m',
138
144
  RESET: '\x1b[0m',
139
145
  } : { FROG:'', DEEP:'', CREAM:'', DIM:'', BOLD:'', RESET:'' };
140
- const v = '0.75.1';
146
+ const v = PKG_VERSION;
141
147
  const compact = !args.flags.full;
142
148
  if (compact) {
143
149
  const lines = [
@@ -263,6 +269,11 @@ function renderV3Blocks(scan, flags) {
263
269
  // Always-on machine output (R2). Vibecoder gets JSON only; pro gets JSON+SARIF+CSV.
264
270
  async function writeMachineOutput(targetAbs, scan, meta, profile) {
265
271
  const stateDir = path.join(targetAbs, '.agentic-security');
272
+ const { isSafeStateDir: _isSafe } = await import('../src/posture/state-dir.js');
273
+ if (!_isSafe(stateDir)) {
274
+ if (process.env.AGENTIC_SECURITY_DEBUG === '1') process.stderr.write(`[agentic-security] refusing to write machine output at ${stateDir} — no project marker\n`);
275
+ return;
276
+ }
266
277
  await fsp.mkdir(stateDir, { recursive: true });
267
278
  // Always JSON (used by /security-fix and /security-report).
268
279
  await fsp.writeFile(path.join(stateDir, 'findings.json'),
@@ -314,6 +325,11 @@ async function cmdScan(args) {
314
325
  }
315
326
  }
316
327
 
328
+ // --incremental : reuse taint summaries from prior scans for faster deep mode.
329
+ if (args.flags['incremental'] || process.env.AGENTIC_SECURITY_INCREMENTAL === '1') {
330
+ process.env.AGENTIC_SECURITY_INCREMENTAL = '1';
331
+ }
332
+
317
333
  // --pr [ref] : friendlier alias for --changed-since that auto-detects the PR
318
334
  // base ref (GitHub/GitLab/Buildkite/Bitbucket env vars) when no value is given.
319
335
  let changedSince = args.flags['changed-since'] || null;
@@ -338,6 +354,26 @@ async function cmdScan(args) {
338
354
  if (only === 'secrets') { scan.findings = []; scan.supplyChain = []; }
339
355
  }
340
356
 
357
+ // --set-baseline: save current findings as baseline for future --since-baseline filtering
358
+ const baselinePath = path.join(target || '.', '.agentic-security', 'baseline.json');
359
+ if (args.flags['set-baseline']) {
360
+ const { normalizeFindings } = await import('../src/report/index.js');
361
+ const baselineIds = new Set(normalizeFindings(scan).map(f => f.stableId || f.id));
362
+ fs.mkdirSync(path.dirname(baselinePath), { recursive: true });
363
+ fs.writeFileSync(baselinePath, JSON.stringify({ ids: [...baselineIds], createdAt: new Date().toISOString(), count: baselineIds.size }, null, 2));
364
+ process.stderr.write(`[baseline] saved ${baselineIds.size} findings as baseline\n`);
365
+ }
366
+ // --since-baseline: filter out findings that existed in the saved baseline
367
+ if (args.flags['since-baseline'] && fs.existsSync(baselinePath)) {
368
+ try {
369
+ const baseline = JSON.parse(fs.readFileSync(baselinePath, 'utf8'));
370
+ const baselineSet = new Set(baseline.ids || []);
371
+ const before = scan.findings.length;
372
+ scan.findings = (scan.findings || []).filter(f => !baselineSet.has(f.stableId || f.id));
373
+ process.stderr.write(`[baseline] filtered ${before - scan.findings.length} baseline findings, ${scan.findings.length} new\n`);
374
+ } catch { /* baseline file unreadable, skip */ }
375
+ }
376
+
341
377
  // 0.9.0 Feat-18: --scorecard flag enables OSSF Scorecard enrichment
342
378
  if (args.flags['scorecard']) process.env.AGENTIC_SECURITY_SCORECARD = '1';
343
379
 
@@ -464,14 +500,19 @@ async function cmdScan(args) {
464
500
  else process.stdout.write(body + '\n');
465
501
 
466
502
  // Persist last scan for /security-fix and /security-report
503
+ const { isSafeStateDir: _isSafeStateDir } = await import('../src/posture/state-dir.js');
467
504
  const stateDir = path.join(path.resolve(target), '.agentic-security');
468
- await fsp.mkdir(stateDir, { recursive: true });
469
- const persistedScan = toJSON(scan, meta);
470
- const lastScanBody = JSON.stringify(persistedScan, null, 2);
471
- await fsp.writeFile(path.join(stateDir, 'last-scan.json'), lastScanBody);
472
- try {
473
- await fsp.writeFile(path.join(stateDir, 'last-scan.json.sig'), _signLastScan(lastScanBody));
474
- } catch { /* non-fatal — sig file is best-effort */ }
505
+ if (_isSafeStateDir(stateDir)) {
506
+ await fsp.mkdir(stateDir, { recursive: true });
507
+ const persistedScan = toJSON(scan, meta);
508
+ const lastScanBody = JSON.stringify(persistedScan, null, 2);
509
+ await fsp.writeFile(path.join(stateDir, 'last-scan.json'), lastScanBody);
510
+ try {
511
+ await fsp.writeFile(path.join(stateDir, 'last-scan.json.sig'), _signLastScan(lastScanBody));
512
+ } catch { /* non-fatal — sig file is best-effort */ }
513
+ } else {
514
+ if (process.env.AGENTIC_SECURITY_DEBUG === '1') process.stderr.write(`[agentic-security] refusing to write state at ${stateDir} — no project marker in ${path.resolve(target)}\n`);
515
+ }
475
516
 
476
517
  // 0.14.0 — update streak / achievements after every full scan. Suppress
477
518
  // streak side effects when the user only wants raw JSON output (CI piping).
@@ -556,6 +597,11 @@ async function cmdCi(args) {
556
597
 
557
598
  // Persist the three CI artifacts.
558
599
  const stateDir = path.join(targetAbs, '.agentic-security');
600
+ const { isSafeStateDir: _isSafeCi } = await import('../src/posture/state-dir.js');
601
+ if (!_isSafeCi(stateDir)) {
602
+ if (process.env.AGENTIC_SECURITY_DEBUG === '1') process.stderr.write(`[agentic-security] refusing to write CI artifacts at ${stateDir} — no project marker\n`);
603
+ return;
604
+ }
559
605
  await fsp.mkdir(stateDir, { recursive: true });
560
606
  await fsp.writeFile(path.join(stateDir, 'findings.json'),
561
607
  JSON.stringify(toJSON(scan, meta), null, 2));
@@ -1665,7 +1711,7 @@ async function main() {
1665
1711
  }
1666
1712
  process.exit(0);
1667
1713
  }
1668
- case 'version': console.log('agentic-security 0.75.1 · created by ClearCapabilities.Com'); process.exit(0);
1714
+ case 'version': console.log(`agentic-security ${PKG_VERSION} · created by ClearCapabilities.Com`); process.exit(0);
1669
1715
  case 'banner': { printBanner(args); process.exit(0); }
1670
1716
  case 'harness': process.exit(await cmdHarness(args));
1671
1717
  case 'scan-baseline': process.exit(await cmdScanBaseline(args));
package/dist/178.index.js CHANGED
@@ -13,7 +13,7 @@ export const modules = {
13
13
  /* harmony import */ var node_child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1421);
14
14
  /* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3024);
15
15
  /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6760);
16
- /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(3291);
16
+ /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6048);
17
17
  // Time-travel + counterfactual scanning (v0.68).
18
18
  //
19
19
  // Two new modes that exploit the pure-input shape of runFullScan:
package/dist/384.index.js CHANGED
@@ -8,7 +8,7 @@ export const modules = {
8
8
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9
9
  /* harmony export */ scanCredentials: () => (/* reexport safe */ _engine_js__WEBPACK_IMPORTED_MODULE_0__.Sv)
10
10
  /* harmony export */ });
11
- /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3291);
11
+ /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6048);
12
12
  // Secrets submodule view of the engine — credential + entropy + TODO scanning.
13
13
 
14
14
 
package/dist/476.index.js CHANGED
@@ -11,6 +11,7 @@ export const modules = {
11
11
  /* unused harmony export _internals */
12
12
  /* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3024);
13
13
  /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6760);
14
+ /* harmony import */ var _state_dir_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(1174);
14
15
  // Auto-rule synthesis from repeated FPs (FR-LEARN-6).
15
16
  //
16
17
  // Reads `.agentic-security/triage-feedback.json` (populated by the /triage
@@ -27,13 +28,11 @@ export const modules = {
27
28
 
28
29
 
29
30
 
30
- const TRIAGE_PATH = node_path__WEBPACK_IMPORTED_MODULE_1__.join('.agentic-security', 'triage-feedback.json');
31
- const PROPOSED_DIR = node_path__WEBPACK_IMPORTED_MODULE_1__.join('.agentic-security', 'rules-proposed');
32
31
 
33
32
  const DEFAULT_FP_THRESHOLD = 5;
34
33
 
35
34
  function _readTriage(scanRoot) {
36
- const fp = node_path__WEBPACK_IMPORTED_MODULE_1__.join(scanRoot || process.cwd(), TRIAGE_PATH);
35
+ const fp = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__/* .statePath */ .BQ)(scanRoot, 'triage-feedback.json');
37
36
  if (!node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(fp)) return null;
38
37
  try { return JSON.parse(node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(fp, 'utf8')); } catch { return null; }
39
38
  }
@@ -100,7 +99,8 @@ function synthesizeRules(scanRoot, opts = {}) {
100
99
  groups.get(k).push(e);
101
100
  }
102
101
  const proposals = [];
103
- const dir = node_path__WEBPACK_IMPORTED_MODULE_1__.join(scanRoot || process.cwd(), PROPOSED_DIR);
102
+ const dir = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__/* .statePath */ .BQ)(scanRoot, 'rules-proposed');
103
+ if (!opts.dryRun && !(0,_state_dir_js__WEBPACK_IMPORTED_MODULE_2__.isSafeStateDir)(node_path__WEBPACK_IMPORTED_MODULE_1__.dirname(dir))) return [];
104
104
  for (const [, group] of groups) {
105
105
  if (group.length < threshold) continue;
106
106
  const summary = _summarizeGroup(group);
@@ -118,7 +118,7 @@ function synthesizeRules(scanRoot, opts = {}) {
118
118
  return proposals;
119
119
  }
120
120
 
121
- const _internals = { DEFAULT_FP_THRESHOLD, TRIAGE_PATH, PROPOSED_DIR };
121
+ const _internals = { DEFAULT_FP_THRESHOLD };
122
122
 
123
123
 
124
124
  /***/ })
package/dist/637.index.js CHANGED
@@ -10,7 +10,7 @@ export const modules = {
10
10
  /* harmony export */ renderPrDeltaText: () => (/* binding */ renderPrDeltaText)
11
11
  /* harmony export */ });
12
12
  /* harmony import */ var node_child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1421);
13
- /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3291);
13
+ /* harmony import */ var _engine_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6048);
14
14
  // Shadowscan / security-DELTA on PR (v0.72).
15
15
  //
16
16
  // Most SAST PR-comment integrations show absolute counts — "12 findings
@@ -0,0 +1,138 @@
1
+ export const id = 700;
2
+ export const ids = [700];
3
+ export const modules = {
4
+
5
+ /***/ 1700:
6
+ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
7
+
8
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9
+ /* harmony export */ validateOsvFunctionsExist: () => (/* binding */ validateOsvFunctionsExist)
10
+ /* harmony export */ });
11
+ /* unused harmony export extractPythonPackageFunctions */
12
+ /* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3024);
13
+ /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6760);
14
+ /* harmony import */ var node_child_process__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(1421);
15
+ /* harmony import */ var _ir_parser_py_cst_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(682);
16
+ // Python package function extraction via the CST parser.
17
+ //
18
+ // Locates an installed Python package in site-packages or .venv,
19
+ // parses its source files via the Python CST parser, and returns
20
+ // a map of exported function names. Used by markUsedVulnFunctions
21
+ // to validate that OSV-named vulnerable functions actually exist
22
+ // in the installed version.
23
+
24
+
25
+
26
+
27
+
28
+
29
+ const VENV_DIRS = ['.venv', 'venv', '.env', 'env'];
30
+
31
+ function _findSitePackages(scanRoot) {
32
+ for (const vdir of VENV_DIRS) {
33
+ const base = node_path__WEBPACK_IMPORTED_MODULE_1__.join(scanRoot || '.', vdir);
34
+ if (!node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(base)) continue;
35
+ const lib = node_path__WEBPACK_IMPORTED_MODULE_1__.join(base, 'lib');
36
+ if (!node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(lib)) continue;
37
+ const pydirs = node_fs__WEBPACK_IMPORTED_MODULE_0__.readdirSync(lib).filter(d => d.startsWith('python'));
38
+ for (const pydir of pydirs) {
39
+ const sp = node_path__WEBPACK_IMPORTED_MODULE_1__.join(lib, pydir, 'site-packages');
40
+ if (node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(sp)) return sp;
41
+ }
42
+ }
43
+ // Fallback: ask python3 directly
44
+ try {
45
+ const out = (0,node_child_process__WEBPACK_IMPORTED_MODULE_2__.execFileSync)('python3', ['-c', 'import site; print(site.getsitepackages()[0])'], {
46
+ encoding: 'utf8', timeout: 5000,
47
+ }).trim();
48
+ if (out && node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(out)) return out;
49
+ } catch { /* no python3 or no site-packages */ }
50
+ return null;
51
+ }
52
+
53
+ function _findPackageDir(sitePackages, packageName) {
54
+ if (!sitePackages) return null;
55
+ const normalized = packageName.replace(/-/g, '_').toLowerCase();
56
+ const candidates = [
57
+ normalized,
58
+ packageName.toLowerCase(),
59
+ packageName,
60
+ ];
61
+ for (const name of candidates) {
62
+ const dir = node_path__WEBPACK_IMPORTED_MODULE_1__.join(sitePackages, name);
63
+ if (node_fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(dir) && node_fs__WEBPACK_IMPORTED_MODULE_0__.statSync(dir).isDirectory()) return dir;
64
+ }
65
+ return null;
66
+ }
67
+
68
+ function _readPyFilesFromDir(dir, maxFiles = 50) {
69
+ const entries = [];
70
+ try {
71
+ const files = node_fs__WEBPACK_IMPORTED_MODULE_0__.readdirSync(dir, { recursive: true })
72
+ .filter(f => f.endsWith('.py'))
73
+ .slice(0, maxFiles);
74
+ for (const f of files) {
75
+ const fp = node_path__WEBPACK_IMPORTED_MODULE_1__.join(dir, f);
76
+ try {
77
+ const content = node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(fp, 'utf8');
78
+ if (content.length < 1_000_000) {
79
+ entries.push({ file: f, content });
80
+ }
81
+ } catch { /* skip unreadable files */ }
82
+ }
83
+ } catch { /* dir not readable */ }
84
+ return entries;
85
+ }
86
+
87
+ function extractPythonPackageFunctions(packageName, scanRoot) {
88
+ const cap = (0,_ir_parser_py_cst_js__WEBPACK_IMPORTED_MODULE_3__/* .probePythonAvailable */ .w4)();
89
+ if (!cap.ok) return null;
90
+
91
+ const sitePackages = _findSitePackages(scanRoot);
92
+ const pkgDir = _findPackageDir(sitePackages, packageName);
93
+ if (!pkgDir) return null;
94
+
95
+ const pyFiles = _readPyFilesFromDir(pkgDir);
96
+ if (!pyFiles.length) return null;
97
+
98
+ const batch = (0,_ir_parser_py_cst_js__WEBPACK_IMPORTED_MODULE_3__/* .parsePythonFilesBatch */ .H2)(pyFiles);
99
+ if (!batch || !Array.isArray(batch)) return null;
100
+
101
+ const functionMap = new Map();
102
+ for (const fileIR of batch) {
103
+ if (!fileIR || !fileIR.functions) continue;
104
+ for (const fn of fileIR.functions) {
105
+ if (fn.name && !fn.name.startsWith('_')) {
106
+ functionMap.set(fn.name, {
107
+ file: fileIR.file,
108
+ line: fn.line,
109
+ qid: fn.qid,
110
+ params: fn.params,
111
+ });
112
+ }
113
+ }
114
+ }
115
+ return functionMap;
116
+ }
117
+
118
+ function validateOsvFunctionsExist(packageName, osvFunctions, scanRoot) {
119
+ if (!osvFunctions || !osvFunctions.length) return { validated: [], missing: [] };
120
+ const fnMap = extractPythonPackageFunctions(packageName, scanRoot);
121
+ if (!fnMap) return { validated: osvFunctions, missing: [] };
122
+ const validated = [];
123
+ const missing = [];
124
+ for (const fn of osvFunctions) {
125
+ const shortFn = fn.includes('.') ? fn.split('.').pop() : fn;
126
+ if (fnMap.has(shortFn) || fnMap.has(fn)) {
127
+ validated.push(shortFn);
128
+ } else {
129
+ missing.push(fn);
130
+ }
131
+ }
132
+ return { validated, missing };
133
+ }
134
+
135
+
136
+ /***/ })
137
+
138
+ };