cve-scanner 0.0.0 → 0.0.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 (140) hide show
  1. package/README.md +96 -45
  2. package/assets/gradle-init/list-dependencies.init.gradle +20 -0
  3. package/data/dataset.hash +1 -0
  4. package/data/dataset.json +288056 -0
  5. package/dist/ai/AiAssistant.d.ts +11 -0
  6. package/dist/ai/AiAssistant.js +34 -0
  7. package/dist/ai/AiAssistant.js.map +1 -0
  8. package/dist/ai/ProjectManifestReader.d.ts +8 -0
  9. package/dist/ai/ProjectManifestReader.js +34 -0
  10. package/dist/ai/ProjectManifestReader.js.map +1 -0
  11. package/dist/ai/ProjectManifestReader.test.d.ts +1 -0
  12. package/dist/ai/ProjectManifestReader.test.js +59 -0
  13. package/dist/ai/ProjectManifestReader.test.js.map +1 -0
  14. package/dist/ai/PromptBuilder.d.ts +5 -0
  15. package/dist/ai/PromptBuilder.js +42 -0
  16. package/dist/ai/PromptBuilder.js.map +1 -0
  17. package/dist/ai/PromptBuilder.test.d.ts +1 -0
  18. package/dist/ai/PromptBuilder.test.js +66 -0
  19. package/dist/ai/PromptBuilder.test.js.map +1 -0
  20. package/dist/cmd/cve-scanner.d.ts +2 -0
  21. package/dist/cmd/cve-scanner.js +60 -0
  22. package/dist/cmd/cve-scanner.js.map +1 -0
  23. package/dist/core/CommandRunner.d.ts +6 -0
  24. package/dist/core/CommandRunner.js +16 -0
  25. package/dist/core/CommandRunner.js.map +1 -0
  26. package/dist/core/DatasetLoader.d.ts +14 -0
  27. package/dist/core/DatasetLoader.js +44 -0
  28. package/dist/core/DatasetLoader.js.map +1 -0
  29. package/dist/core/DatasetLoader.test.d.ts +1 -0
  30. package/dist/core/DatasetLoader.test.js +41 -0
  31. package/dist/core/DatasetLoader.test.js.map +1 -0
  32. package/dist/core/ProjectRootDiscovery.d.ts +9 -0
  33. package/dist/core/ProjectRootDiscovery.js +62 -0
  34. package/dist/core/ProjectRootDiscovery.js.map +1 -0
  35. package/dist/core/ProjectRootDiscovery.test.d.ts +1 -0
  36. package/dist/core/ProjectRootDiscovery.test.js +47 -0
  37. package/dist/core/ProjectRootDiscovery.test.js.map +1 -0
  38. package/dist/core/Scanner.d.ts +19 -0
  39. package/dist/core/Scanner.js +66 -0
  40. package/dist/core/Scanner.js.map +1 -0
  41. package/dist/core/Scanner.test.d.ts +1 -0
  42. package/dist/core/Scanner.test.js +157 -0
  43. package/dist/core/Scanner.test.js.map +1 -0
  44. package/dist/core/VersionComparator.d.ts +6 -0
  45. package/dist/core/VersionComparator.js +35 -0
  46. package/dist/core/VersionComparator.js.map +1 -0
  47. package/dist/core/VersionComparator.test.d.ts +1 -0
  48. package/dist/core/VersionComparator.test.js +21 -0
  49. package/dist/core/VersionComparator.test.js.map +1 -0
  50. package/dist/core/cpe.d.ts +9 -0
  51. package/dist/core/cpe.js +32 -0
  52. package/dist/core/cpe.js.map +1 -0
  53. package/dist/core/cpe.test.d.ts +1 -0
  54. package/dist/core/cpe.test.js +25 -0
  55. package/dist/core/cpe.test.js.map +1 -0
  56. package/dist/core/types.d.ts +57 -0
  57. package/dist/core/types.js +3 -0
  58. package/dist/core/types.js.map +1 -0
  59. package/dist/ecosystems/enrichment/CveListCache.d.ts +13 -0
  60. package/dist/ecosystems/enrichment/CveListCache.js +88 -0
  61. package/dist/ecosystems/enrichment/CveListCache.js.map +1 -0
  62. package/dist/ecosystems/enrichment/CveListCache.test.d.ts +1 -0
  63. package/dist/ecosystems/enrichment/CveListCache.test.js +49 -0
  64. package/dist/ecosystems/enrichment/CveListCache.test.js.map +1 -0
  65. package/dist/ecosystems/enrichment/CveOrgDatasetReader.d.ts +12 -0
  66. package/dist/ecosystems/enrichment/CveOrgDatasetReader.js +59 -0
  67. package/dist/ecosystems/enrichment/CveOrgDatasetReader.js.map +1 -0
  68. package/dist/ecosystems/enrichment/CveOrgDatasetReader.test.d.ts +1 -0
  69. package/dist/ecosystems/enrichment/CveOrgDatasetReader.test.js +48 -0
  70. package/dist/ecosystems/enrichment/CveOrgDatasetReader.test.js.map +1 -0
  71. package/dist/ecosystems/enrichment/ZipExtractor.d.ts +18 -0
  72. package/dist/ecosystems/enrichment/ZipExtractor.js +105 -0
  73. package/dist/ecosystems/enrichment/ZipExtractor.js.map +1 -0
  74. package/dist/ecosystems/enrichment/ZipExtractor.test.d.ts +1 -0
  75. package/dist/ecosystems/enrichment/ZipExtractor.test.js +36 -0
  76. package/dist/ecosystems/enrichment/ZipExtractor.test.js.map +1 -0
  77. package/dist/ecosystems/java/GradleDependencyDetector.d.ts +9 -0
  78. package/dist/ecosystems/java/GradleDependencyDetector.js +33 -0
  79. package/dist/ecosystems/java/GradleDependencyDetector.js.map +1 -0
  80. package/dist/ecosystems/java/MavenDependencyDetector.d.ts +9 -0
  81. package/dist/ecosystems/java/MavenDependencyDetector.js +39 -0
  82. package/dist/ecosystems/java/MavenDependencyDetector.js.map +1 -0
  83. package/dist/ecosystems/java/parsers/gradleDependencyOutputParser.d.ts +12 -0
  84. package/dist/ecosystems/java/parsers/gradleDependencyOutputParser.js +32 -0
  85. package/dist/ecosystems/java/parsers/gradleDependencyOutputParser.js.map +1 -0
  86. package/dist/ecosystems/java/parsers/gradleDependencyOutputParser.test.d.ts +1 -0
  87. package/dist/ecosystems/java/parsers/gradleDependencyOutputParser.test.js +36 -0
  88. package/dist/ecosystems/java/parsers/gradleDependencyOutputParser.test.js.map +1 -0
  89. package/dist/ecosystems/java/parsers/mavenDependencyListParser.d.ts +11 -0
  90. package/dist/ecosystems/java/parsers/mavenDependencyListParser.js +31 -0
  91. package/dist/ecosystems/java/parsers/mavenDependencyListParser.js.map +1 -0
  92. package/dist/ecosystems/java/parsers/mavenDependencyListParser.test.d.ts +1 -0
  93. package/dist/ecosystems/java/parsers/mavenDependencyListParser.test.js +37 -0
  94. package/dist/ecosystems/java/parsers/mavenDependencyListParser.test.js.map +1 -0
  95. package/dist/ecosystems/java/resolveExecutable.d.ts +8 -0
  96. package/dist/ecosystems/java/resolveExecutable.js +22 -0
  97. package/dist/ecosystems/java/resolveExecutable.js.map +1 -0
  98. package/dist/ecosystems/java/resolveExecutable.test.d.ts +1 -0
  99. package/dist/ecosystems/java/resolveExecutable.test.js +42 -0
  100. package/dist/ecosystems/java/resolveExecutable.test.js.map +1 -0
  101. package/dist/ecosystems/npm/NpmDetector.d.ts +6 -0
  102. package/dist/ecosystems/npm/NpmDetector.js +31 -0
  103. package/dist/ecosystems/npm/NpmDetector.js.map +1 -0
  104. package/dist/ecosystems/npm/PackageLockParser.d.ts +11 -0
  105. package/dist/ecosystems/npm/PackageLockParser.js +65 -0
  106. package/dist/ecosystems/npm/PackageLockParser.js.map +1 -0
  107. package/dist/ecosystems/npm/PackageLockParser.test.d.ts +1 -0
  108. package/dist/ecosystems/npm/PackageLockParser.test.js +59 -0
  109. package/dist/ecosystems/npm/PackageLockParser.test.js.map +1 -0
  110. package/dist/lib/CveScannerApi.d.ts +13 -0
  111. package/dist/lib/CveScannerApi.js +55 -0
  112. package/dist/lib/CveScannerApi.js.map +1 -0
  113. package/dist/lib/report.d.ts +4 -0
  114. package/dist/lib/report.js +47 -0
  115. package/dist/lib/report.js.map +1 -0
  116. package/dist/pipeline/dedupeEntries.d.ts +8 -0
  117. package/dist/pipeline/dedupeEntries.js +20 -0
  118. package/dist/pipeline/dedupeEntries.js.map +1 -0
  119. package/dist/pipeline/dedupeEntries.test.d.ts +1 -0
  120. package/dist/pipeline/dedupeEntries.test.js +27 -0
  121. package/dist/pipeline/dedupeEntries.test.js.map +1 -0
  122. package/dist/pipeline/hashDataset.d.ts +7 -0
  123. package/dist/pipeline/hashDataset.js +13 -0
  124. package/dist/pipeline/hashDataset.js.map +1 -0
  125. package/dist/pipeline/hashDataset.test.d.ts +1 -0
  126. package/dist/pipeline/hashDataset.test.js +28 -0
  127. package/dist/pipeline/hashDataset.test.js.map +1 -0
  128. package/dist/pipeline/mapSeverityToScore.d.ts +9 -0
  129. package/dist/pipeline/mapSeverityToScore.js +26 -0
  130. package/dist/pipeline/mapSeverityToScore.js.map +1 -0
  131. package/dist/pipeline/mapSeverityToScore.test.d.ts +1 -0
  132. package/dist/pipeline/mapSeverityToScore.test.js +20 -0
  133. package/dist/pipeline/mapSeverityToScore.test.js.map +1 -0
  134. package/dist/pipeline/parseOsvRecord.d.ts +37 -0
  135. package/dist/pipeline/parseOsvRecord.js +88 -0
  136. package/dist/pipeline/parseOsvRecord.js.map +1 -0
  137. package/dist/pipeline/parseOsvRecord.test.d.ts +1 -0
  138. package/dist/pipeline/parseOsvRecord.test.js +220 -0
  139. package/dist/pipeline/parseOsvRecord.test.js.map +1 -0
  140. package/package.json +37 -6
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.cacheRootDir = cacheRootDir;
7
+ exports.isCacheFresh = isCacheFresh;
8
+ exports.ensureCveListCache = ensureCveListCache;
9
+ const fs_1 = require("fs");
10
+ const https_1 = __importDefault(require("https"));
11
+ const os_1 = require("os");
12
+ const path_1 = require("path");
13
+ const ZipExtractor_1 = require("./ZipExtractor");
14
+ const CVE_LIST_ZIP_URL = 'https://github.com/CVEProject/cvelistV5/archive/refs/heads/main.zip';
15
+ const CACHE_TTL_MS = 12 * 60 * 60 * 1000;
16
+ function cacheRootDir() {
17
+ return (0, path_1.join)((0, os_1.tmpdir)(), 'cve-scanner-cve-list-cache');
18
+ }
19
+ function extractedRepoDir(cacheRoot) {
20
+ return (0, path_1.join)(cacheRoot, 'cvelistV5-main');
21
+ }
22
+ function markerFile(cacheRoot) {
23
+ return (0, path_1.join)(cacheRoot, '.fetched-at');
24
+ }
25
+ /**
26
+ * Pure (given the marker's mtime as input): is the cache under `cacheRoot`
27
+ * still within the 12-hour TTL? See specs/80-cve-enrichment.md.
28
+ */
29
+ function isCacheFresh(cacheRoot, now = Date.now()) {
30
+ const marker = markerFile(cacheRoot);
31
+ if (!(0, fs_1.existsSync)(marker) || !(0, fs_1.existsSync)(extractedRepoDir(cacheRoot)))
32
+ return false;
33
+ const fetchedAt = Date.parse((0, fs_1.readFileSync)(marker, 'utf8').trim());
34
+ if (Number.isNaN(fetchedAt))
35
+ return false;
36
+ return now - fetchedAt < CACHE_TTL_MS;
37
+ }
38
+ function download(url, destPath, redirectsLeft = 5) {
39
+ return new Promise((resolveResult, reject) => {
40
+ https_1.default
41
+ .get(url, (res) => {
42
+ const status = res.statusCode ?? 0;
43
+ if (status >= 300 && status < 400 && res.headers.location) {
44
+ res.resume();
45
+ if (redirectsLeft === 0) {
46
+ reject(new Error('Too many redirects downloading the CVE List archive.'));
47
+ return;
48
+ }
49
+ download(res.headers.location, destPath, redirectsLeft - 1).then(resolveResult, reject);
50
+ return;
51
+ }
52
+ if (status !== 200) {
53
+ res.resume();
54
+ reject(new Error(`Failed to download the CVE List archive: HTTP ${status}`));
55
+ return;
56
+ }
57
+ const file = (0, fs_1.createWriteStream)(destPath);
58
+ res.pipe(file);
59
+ file.on('finish', () => file.close(() => resolveResult()));
60
+ file.on('error', reject);
61
+ })
62
+ .on('error', reject);
63
+ });
64
+ }
65
+ /**
66
+ * Downloads and extracts the full CVE List (https://github.com/CVEProject/cvelistV5)
67
+ * into a 12-hour-TTL local cache, reusing it across runs when still fresh.
68
+ * Returns the directory to use as --cve-data-dir (the one containing cves/).
69
+ * See specs/80-cve-enrichment.md.
70
+ */
71
+ async function ensureCveListCache() {
72
+ const cacheRoot = cacheRootDir();
73
+ const repoDir = extractedRepoDir(cacheRoot);
74
+ if (isCacheFresh(cacheRoot)) {
75
+ return repoDir;
76
+ }
77
+ (0, fs_1.mkdirSync)(cacheRoot, { recursive: true });
78
+ const zipPath = (0, path_1.join)(cacheRoot, 'cvelistV5-main.zip');
79
+ console.error('Downloading the full CVE List (https://github.com/CVEProject/cvelistV5) — large, cached for 12 hours...');
80
+ await download(CVE_LIST_ZIP_URL, zipPath);
81
+ (0, fs_1.rmSync)(repoDir, { recursive: true, force: true });
82
+ console.error('Extracting CVE List archive...');
83
+ await (0, ZipExtractor_1.extractZip)(zipPath, cacheRoot);
84
+ (0, fs_1.rmSync)(zipPath);
85
+ (0, fs_1.writeFileSync)(markerFile(cacheRoot), new Date().toISOString());
86
+ return repoDir;
87
+ }
88
+ //# sourceMappingURL=CveListCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CveListCache.js","sourceRoot":"","sources":["../../../src/ecosystems/enrichment/CveListCache.ts"],"names":[],"mappings":";;;;;;;;AAAA,2BAOY;AACZ,kDAA0B;AAC1B,2BAA4B;AAC5B,+BAA4B;AAC5B,iDAA4C;AAE5C,MAAM,gBAAgB,GACpB,qEAAqE,CAAC;AACxE,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEzC;IACE,OAAO,IAAA,WAAI,EAAC,IAAA,WAAM,GAAE,EAAE,4BAA4B,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,gBAAgB,CAAC,SAAiB;IACzC,OAAO,IAAA,WAAI,EAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,UAAU,CAAC,SAAiB;IACnC,OAAO,IAAA,WAAI,EAAC,SAAS,EAAE,aAAa,CAAC,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,sBACE,SAAiB,EACjB,GAAG,GAAW,IAAI,CAAC,GAAG,EAAE;IAExB,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IACrC,IAAI,CAAC,IAAA,eAAU,EAAC,MAAM,CAAC,IAAI,CAAC,IAAA,eAAU,EAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;QACjE,OAAO,KAAK,CAAC;IAEf,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAA,iBAAY,EAAC,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAClE,IAAI,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC;QAAE,OAAO,KAAK,CAAC;IAE1C,OAAO,GAAG,GAAG,SAAS,GAAG,YAAY,CAAC;AACxC,CAAC;AAED,SAAS,QAAQ,CACf,GAAW,EACX,QAAgB,EAChB,aAAa,GAAG,CAAC;IAEjB,OAAO,IAAI,OAAO,CAAC,CAAC,aAAa,EAAE,MAAM,EAAE,EAAE;QAC3C,eAAK;aACF,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE;YAChB,MAAM,MAAM,GAAG,GAAG,CAAC,UAAU,IAAI,CAAC,CAAC;YAEnC,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;gBAC1D,GAAG,CAAC,MAAM,EAAE,CAAC;gBACb,IAAI,aAAa,KAAK,CAAC,EAAE,CAAC;oBACxB,MAAM,CACJ,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAClE,CAAC;oBACF,OAAO;gBACT,CAAC;gBACD,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,aAAa,GAAG,CAAC,CAAC,CAAC,IAAI,CAC9D,aAAa,EACb,MAAM,CACP,CAAC;gBACF,OAAO;YACT,CAAC;YAED,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;gBACnB,GAAG,CAAC,MAAM,EAAE,CAAC;gBACb,MAAM,CACJ,IAAI,KAAK,CACP,iDAAiD,MAAM,EAAE,CAC1D,CACF,CAAC;gBACF,OAAO;YACT,CAAC;YAED,MAAM,IAAI,GAAG,IAAA,sBAAiB,EAAC,QAAQ,CAAC,CAAC;YACzC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACf,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;YAC3D,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC3B,CAAC,CAAC;aACD,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACI,KAAK;IACV,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;IACjC,MAAM,OAAO,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAE5C,IAAI,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC;QAC5B,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IAAA,cAAS,EAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,IAAA,WAAI,EAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;IAEtD,OAAO,CAAC,KAAK,CACX,yGAAyG,CAC1G,CAAC;IACF,MAAM,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;IAE1C,IAAA,WAAM,EAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;IAChD,MAAM,IAAA,yBAAU,EAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACrC,IAAA,WAAM,EAAC,OAAO,CAAC,CAAC;IAEhB,IAAA,kBAAa,EAAC,UAAU,CAAC,SAAS,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;IAC/D,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const strict_1 = __importDefault(require("node:assert/strict"));
7
+ const node_fs_1 = require("node:fs");
8
+ const node_os_1 = require("node:os");
9
+ const node_path_1 = require("node:path");
10
+ const node_test_1 = require("node:test");
11
+ const CveListCache_1 = require("./CveListCache");
12
+ function makeFreshCache(fetchedAtIso) {
13
+ const cacheRoot = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'cve-scanner-cache-test-'));
14
+ (0, node_fs_1.mkdirSync)((0, node_path_1.join)(cacheRoot, 'cvelistV5-main'), { recursive: true });
15
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(cacheRoot, '.fetched-at'), fetchedAtIso);
16
+ return cacheRoot;
17
+ }
18
+ (0, node_test_1.test)('is fresh when fetched just now', () => {
19
+ const cacheRoot = makeFreshCache(new Date().toISOString());
20
+ strict_1.default.equal((0, CveListCache_1.isCacheFresh)(cacheRoot), true);
21
+ });
22
+ (0, node_test_1.test)('is fresh when fetched 11 hours ago', () => {
23
+ const elevenHoursAgo = new Date(Date.now() - 11 * 60 * 60 * 1000).toISOString();
24
+ const cacheRoot = makeFreshCache(elevenHoursAgo);
25
+ strict_1.default.equal((0, CveListCache_1.isCacheFresh)(cacheRoot), true);
26
+ });
27
+ (0, node_test_1.test)('is stale when fetched 13 hours ago', () => {
28
+ const thirteenHoursAgo = new Date(Date.now() - 13 * 60 * 60 * 1000).toISOString();
29
+ const cacheRoot = makeFreshCache(thirteenHoursAgo);
30
+ strict_1.default.equal((0, CveListCache_1.isCacheFresh)(cacheRoot), false);
31
+ });
32
+ (0, node_test_1.test)('is stale when there is no cache at all', () => {
33
+ const cacheRoot = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'cve-scanner-cache-test-'));
34
+ strict_1.default.equal((0, CveListCache_1.isCacheFresh)(cacheRoot), false);
35
+ });
36
+ (0, node_test_1.test)('is stale when the marker exists but the extracted repo dir does not', () => {
37
+ const cacheRoot = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'cve-scanner-cache-test-'));
38
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(cacheRoot, '.fetched-at'), new Date().toISOString());
39
+ strict_1.default.equal((0, CveListCache_1.isCacheFresh)(cacheRoot), false);
40
+ });
41
+ (0, node_test_1.test)('accepts an explicit "now" for deterministic testing', () => {
42
+ const fetchedAt = new Date('2026-01-01T00:00:00.000Z').toISOString();
43
+ const cacheRoot = makeFreshCache(fetchedAt);
44
+ const justUnderTtl = new Date('2026-01-01T11:59:00.000Z').getTime();
45
+ const justOverTtl = new Date('2026-01-01T12:01:00.000Z').getTime();
46
+ strict_1.default.equal((0, CveListCache_1.isCacheFresh)(cacheRoot, justUnderTtl), true);
47
+ strict_1.default.equal((0, CveListCache_1.isCacheFresh)(cacheRoot, justOverTtl), false);
48
+ });
49
+ //# sourceMappingURL=CveListCache.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CveListCache.test.js","sourceRoot":"","sources":["../../../src/ecosystems/enrichment/CveListCache.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { CveEnrichment } from '../../core/types';
2
+ /**
3
+ * Reads enrichment detail for a CVE id from a locally extracted CVE.org bulk
4
+ * download (https://www.cve.org/Downloads). See specs/80-cve-enrichment.md.
5
+ */
6
+ export declare class CveOrgDatasetReader {
7
+ private cveDataDir;
8
+ constructor(cveDataDir: string | undefined);
9
+ read(cveId: string): CveEnrichment | undefined;
10
+ private resolvePath;
11
+ private parse;
12
+ }
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CveOrgDatasetReader = void 0;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ /**
7
+ * Reads enrichment detail for a CVE id from a locally extracted CVE.org bulk
8
+ * download (https://www.cve.org/Downloads). See specs/80-cve-enrichment.md.
9
+ */
10
+ class CveOrgDatasetReader {
11
+ constructor(cveDataDir) {
12
+ this.cveDataDir = cveDataDir;
13
+ }
14
+ read(cveId) {
15
+ if (!this.cveDataDir)
16
+ return undefined;
17
+ const filePath = this.resolvePath(cveId);
18
+ if (!filePath || !(0, fs_1.existsSync)(filePath))
19
+ return undefined;
20
+ const record = JSON.parse((0, fs_1.readFileSync)(filePath, 'utf8'));
21
+ return this.parse(record);
22
+ }
23
+ resolvePath(cveId) {
24
+ const match = /^CVE-(\d{4})-(\d+)$/.exec(cveId);
25
+ if (!match)
26
+ return undefined;
27
+ const [, year, number] = match;
28
+ const bucket = number.length > 3 ? `${number.slice(0, -3)}xxx` : '0xxx';
29
+ return (0, path_1.join)(this.cveDataDir, 'cves', year, bucket, `${cveId}.json`);
30
+ }
31
+ parse(record) {
32
+ const cna = record.containers?.cna;
33
+ const description = cna?.descriptions?.find((d) => d.lang === 'en')?.value ??
34
+ cna?.descriptions?.[0]?.value ??
35
+ '';
36
+ const cvssVector = cna?.metrics
37
+ ?.map((m) => m.cvssV3_1?.vectorString ??
38
+ m.cvssV3_0?.vectorString ??
39
+ m.cvssV2_0?.vectorString)
40
+ .find((v) => v !== undefined);
41
+ const cwe = [
42
+ ...new Set((cna?.problemTypes ?? [])
43
+ .flatMap((pt) => pt.descriptions ?? [])
44
+ .map((d) => d.cweId)
45
+ .filter((id) => id !== undefined)),
46
+ ];
47
+ const references = (cna?.references ?? [])
48
+ .map((r) => r.url)
49
+ .filter((url) => url !== undefined);
50
+ return {
51
+ description,
52
+ cvssVector,
53
+ cwe: cwe.length > 0 ? cwe : undefined,
54
+ references,
55
+ };
56
+ }
57
+ }
58
+ exports.CveOrgDatasetReader = CveOrgDatasetReader;
59
+ //# sourceMappingURL=CveOrgDatasetReader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CveOrgDatasetReader.js","sourceRoot":"","sources":["../../../src/ecosystems/enrichment/CveOrgDatasetReader.ts"],"names":[],"mappings":";;;AAAA,2BAA8C;AAC9C,+BAA4B;AA6B5B;;;GAGG;AACH;IACE,YAAoB,UAA8B;0BAA9B,UAAU;IAAuB,CAAC;IAEtD,IAAI,CAAC,KAAa;QAChB,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAO,SAAS,CAAC;QAEvC,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QACzC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAA,eAAU,EAAC,QAAQ,CAAC;YAAE,OAAO,SAAS,CAAC;QAEzD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAA,iBAAY,EAAC,QAAQ,EAAE,MAAM,CAAC,CAAc,CAAC;QACvE,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC;IAEO,WAAW,CAAC,KAAa;QAC/B,MAAM,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChD,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAE7B,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC;QAC/B,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QACxE,OAAO,IAAA,WAAI,EAAC,IAAI,CAAC,UAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC;IACvE,CAAC;IAEO,KAAK,CAAC,MAAiB;QAC7B,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC;QAEnC,MAAM,WAAW,GACf,GAAG,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,KAAK;YACtD,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK;YAC7B,EAAE,CAAC;QAEL,MAAM,UAAU,GAAG,GAAG,EAAE,OAAO;YAC7B,EAAE,GAAG,CACH,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,QAAQ,EAAE,YAAY;YACxB,CAAC,CAAC,QAAQ,EAAE,YAAY;YACxB,CAAC,CAAC,QAAQ,EAAE,YAAY,CAC3B;aACA,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;QAEhC,MAAM,GAAG,GAAG;YACV,GAAG,IAAI,GAAG,CACR,CAAC,GAAG,EAAE,YAAY,IAAI,EAAE,CAAC;iBACtB,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,YAAY,IAAI,EAAE,CAAC;iBACtC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;iBACnB,MAAM,CAAC,CAAC,EAAE,EAAgB,EAAE,CAAC,EAAE,KAAK,SAAS,CAAC,CAClD;SACF,CAAC;QAEF,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,UAAU,IAAI,EAAE,CAAC;aACvC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;aACjB,MAAM,CAAC,CAAC,GAAG,EAAiB,EAAE,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC;QAErD,OAAO;YACL,WAAW;YACX,UAAU;YACV,GAAG,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;YACrC,UAAU;SACX,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const strict_1 = __importDefault(require("node:assert/strict"));
7
+ const node_fs_1 = require("node:fs");
8
+ const node_os_1 = require("node:os");
9
+ const node_path_1 = require("node:path");
10
+ const node_test_1 = require("node:test");
11
+ const CveOrgDatasetReader_1 = require("./CveOrgDatasetReader");
12
+ function makeFixtureDir() {
13
+ const root = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'cve-scanner-enrichment-'));
14
+ const bucketDir = (0, node_path_1.join)(root, 'cves', '2023', '45xxx');
15
+ (0, node_fs_1.mkdirSync)(bucketDir, { recursive: true });
16
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(bucketDir, 'CVE-2023-45678.json'), JSON.stringify({
17
+ containers: {
18
+ cna: {
19
+ descriptions: [
20
+ { lang: 'en', value: 'An example vulnerability description.' },
21
+ ],
22
+ metrics: [{ cvssV3_1: { vectorString: 'CVSS:3.1/AV:N/AC:L' } }],
23
+ problemTypes: [{ descriptions: [{ cweId: 'CWE-79' }] }],
24
+ references: [{ url: 'https://example.com/advisory' }],
25
+ },
26
+ },
27
+ }));
28
+ return root;
29
+ }
30
+ (0, node_test_1.test)('returns undefined when cveDataDir is not set', () => {
31
+ const reader = new CveOrgDatasetReader_1.CveOrgDatasetReader(undefined);
32
+ strict_1.default.equal(reader.read('CVE-2023-45678'), undefined);
33
+ });
34
+ (0, node_test_1.test)('returns undefined when the matching file does not exist', () => {
35
+ const reader = new CveOrgDatasetReader_1.CveOrgDatasetReader(makeFixtureDir());
36
+ strict_1.default.equal(reader.read('CVE-2023-99999'), undefined);
37
+ });
38
+ (0, node_test_1.test)('parses a matching CVE JSON 5.0 record', () => {
39
+ const reader = new CveOrgDatasetReader_1.CveOrgDatasetReader(makeFixtureDir());
40
+ const enrichment = reader.read('CVE-2023-45678');
41
+ strict_1.default.deepEqual(enrichment, {
42
+ description: 'An example vulnerability description.',
43
+ cvssVector: 'CVSS:3.1/AV:N/AC:L',
44
+ cwe: ['CWE-79'],
45
+ references: ['https://example.com/advisory'],
46
+ });
47
+ });
48
+ //# sourceMappingURL=CveOrgDatasetReader.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CveOrgDatasetReader.test.js","sourceRoot":"","sources":["../../../src/ecosystems/enrichment/CveOrgDatasetReader.test.ts"],"names":[],"mappings":";;;;;AAAA,gEAAwC;AACxC,qCAAgE;AAChE,qCAAiC;AACjC,yCAAiC;AACjC,yCAAiC;AACjC,+DAA4D;AAE5D,SAAS,cAAc;IACrB,MAAM,IAAI,GAAG,IAAA,qBAAW,EAAC,IAAA,gBAAI,EAAC,IAAA,gBAAM,GAAE,EAAE,yBAAyB,CAAC,CAAC,CAAC;IACpE,MAAM,SAAS,GAAG,IAAA,gBAAI,EAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IACtD,IAAA,mBAAS,EAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,IAAA,uBAAa,EACX,IAAA,gBAAI,EAAC,SAAS,EAAE,qBAAqB,CAAC,EACtC,IAAI,CAAC,SAAS,CAAC;QACb,UAAU,EAAE;YACV,GAAG,EAAE;gBACH,YAAY,EAAE;oBACZ,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,uCAAuC,EAAE;iBAC/D;gBACD,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,YAAY,EAAE,oBAAoB,EAAE,EAAE,CAAC;gBAC/D,YAAY,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;gBACvD,UAAU,EAAE,CAAC,EAAE,GAAG,EAAE,8BAA8B,EAAE,CAAC;aACtD;SACF;KACF,CAAC,CACH,CAAC;IACF,OAAO,IAAI,CAAC;AACd,CAAC;AAED,IAAA,gBAAI,EAAC,8CAA8C,EAAE,GAAG,EAAE;IACxD,MAAM,MAAM,GAAG,IAAI,yCAAmB,CAAC,SAAS,CAAC,CAAC;IAClD,gBAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,SAAS,CAAC,CAAC;AACzD,CAAC,CAAC,CAAC;AAEH,IAAA,gBAAI,EAAC,yDAAyD,EAAE,GAAG,EAAE;IACnE,MAAM,MAAM,GAAG,IAAI,yCAAmB,CAAC,cAAc,EAAE,CAAC,CAAC;IACzD,gBAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,SAAS,CAAC,CAAC;AACzD,CAAC,CAAC,CAAC;AAEH,IAAA,gBAAI,EAAC,uCAAuC,EAAE,GAAG,EAAE;IACjD,MAAM,MAAM,GAAG,IAAI,yCAAmB,CAAC,cAAc,EAAE,CAAC,CAAC;IACzD,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IACjD,gBAAM,CAAC,SAAS,CAAC,UAAU,EAAE;QAC3B,WAAW,EAAE,uCAAuC;QACpD,UAAU,EAAE,oBAAoB;QAChC,GAAG,EAAE,CAAC,QAAQ,CAAC;QACf,UAAU,EAAE,CAAC,8BAA8B,CAAC;KAC7C,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,18 @@
1
+ import * as yauzl from 'yauzl';
2
+ /**
3
+ * Resolves a zip entry's file name against `targetDir`, rejecting it
4
+ * (returns undefined) if it would escape `targetDir` — the "zip slip" path
5
+ * traversal class of vulnerability (e.g. a malicious "../../etc/evil" entry
6
+ * name). Pure, no I/O — see specs/80-cve-enrichment.md.
7
+ */
8
+ export declare function resolveSafeEntryPath(targetDir: string, fileName: string): string | undefined;
9
+ export declare function isSymlinkEntry(entry: yauzl.Entry): boolean;
10
+ /**
11
+ * Extracts a zip file using yauzl directly (not the `extract-zip` package,
12
+ * which has unpatched symlink path-traversal advisories — GHSA-jmr9-qjv8-65gv,
13
+ * GHSA-7pqw-9j4j-h8q3 — with no fix available). Mitigates the same class of
14
+ * issue via `resolveSafeEntryPath`, and by skipping symlink entries entirely
15
+ * — this archive is plain JSON files, there's no legitimate reason for one
16
+ * to contain a symlink. See specs/80-cve-enrichment.md.
17
+ */
18
+ export declare function extractZip(zipPath: string, targetDir: string): Promise<void>;
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.resolveSafeEntryPath = resolveSafeEntryPath;
37
+ exports.isSymlinkEntry = isSymlinkEntry;
38
+ exports.extractZip = extractZip;
39
+ const fs_1 = require("fs");
40
+ const path_1 = require("path");
41
+ const yauzl = __importStar(require("yauzl"));
42
+ const SYMLINK_MODE_BITS = 0xa000; // S_IFLNK, packed into the upper 16 bits of externalFileAttributes
43
+ /**
44
+ * Resolves a zip entry's file name against `targetDir`, rejecting it
45
+ * (returns undefined) if it would escape `targetDir` — the "zip slip" path
46
+ * traversal class of vulnerability (e.g. a malicious "../../etc/evil" entry
47
+ * name). Pure, no I/O — see specs/80-cve-enrichment.md.
48
+ */
49
+ function resolveSafeEntryPath(targetDir, fileName) {
50
+ const resolvedTargetDir = (0, path_1.resolve)(targetDir);
51
+ const entryPath = (0, path_1.resolve)(resolvedTargetDir, fileName);
52
+ const isInsideTarget = entryPath === resolvedTargetDir ||
53
+ entryPath.startsWith(resolvedTargetDir + path_1.sep);
54
+ return isInsideTarget ? entryPath : undefined;
55
+ }
56
+ function isSymlinkEntry(entry) {
57
+ const unixMode = entry.externalFileAttributes >>> 16;
58
+ return (unixMode & 0xf000) === SYMLINK_MODE_BITS;
59
+ }
60
+ /**
61
+ * Extracts a zip file using yauzl directly (not the `extract-zip` package,
62
+ * which has unpatched symlink path-traversal advisories — GHSA-jmr9-qjv8-65gv,
63
+ * GHSA-7pqw-9j4j-h8q3 — with no fix available). Mitigates the same class of
64
+ * issue via `resolveSafeEntryPath`, and by skipping symlink entries entirely
65
+ * — this archive is plain JSON files, there's no legitimate reason for one
66
+ * to contain a symlink. See specs/80-cve-enrichment.md.
67
+ */
68
+ function extractZip(zipPath, targetDir) {
69
+ return new Promise((resolveResult, reject) => {
70
+ yauzl.open(zipPath, { lazyEntries: true }, (openErr, zipfile) => {
71
+ if (openErr) {
72
+ reject(openErr);
73
+ return;
74
+ }
75
+ zipfile.readEntry();
76
+ zipfile.on('entry', (entry) => {
77
+ const entryPath = resolveSafeEntryPath(targetDir, entry.fileName);
78
+ if (!entryPath || isSymlinkEntry(entry)) {
79
+ zipfile.readEntry();
80
+ return;
81
+ }
82
+ if (/\/$/.test(entry.fileName)) {
83
+ (0, fs_1.mkdirSync)(entryPath, { recursive: true });
84
+ zipfile.readEntry();
85
+ return;
86
+ }
87
+ (0, fs_1.mkdirSync)((0, path_1.dirname)(entryPath), { recursive: true });
88
+ zipfile.openReadStream(entry, (streamErr, readStream) => {
89
+ if (streamErr) {
90
+ reject(streamErr);
91
+ return;
92
+ }
93
+ const writeStream = (0, fs_1.createWriteStream)(entryPath);
94
+ readStream.pipe(writeStream);
95
+ writeStream.on('finish', () => zipfile.readEntry());
96
+ writeStream.on('error', reject);
97
+ readStream.on('error', reject);
98
+ });
99
+ });
100
+ zipfile.on('end', () => resolveResult());
101
+ zipfile.on('error', reject);
102
+ });
103
+ });
104
+ }
105
+ //# sourceMappingURL=ZipExtractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ZipExtractor.js","sourceRoot":"","sources":["../../../src/ecosystems/enrichment/ZipExtractor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const strict_1 = __importDefault(require("node:assert/strict"));
7
+ const node_path_1 = require("node:path");
8
+ const node_test_1 = require("node:test");
9
+ const ZipExtractor_1 = require("./ZipExtractor");
10
+ (0, node_test_1.test)('resolves an ordinary nested entry inside the target dir', () => {
11
+ const result = (0, ZipExtractor_1.resolveSafeEntryPath)('/out', 'repo-main/cves/2023/1xxx/CVE-2023-0001.json');
12
+ strict_1.default.equal(result, (0, node_path_1.join)('/out', 'repo-main/cves/2023/1xxx/CVE-2023-0001.json'));
13
+ });
14
+ (0, node_test_1.test)('rejects a zip-slip entry that escapes the target dir via ../', () => {
15
+ strict_1.default.equal((0, ZipExtractor_1.resolveSafeEntryPath)('/out', '../../etc/passwd'), undefined);
16
+ });
17
+ (0, node_test_1.test)('rejects an absolute-path entry that would overwrite outside the target dir', () => {
18
+ strict_1.default.equal((0, ZipExtractor_1.resolveSafeEntryPath)('/out', '/etc/passwd'), undefined);
19
+ });
20
+ (0, node_test_1.test)('rejects an entry that only superficially looks nested but climbs back out', () => {
21
+ strict_1.default.equal((0, ZipExtractor_1.resolveSafeEntryPath)('/out', 'a/b/../../../etc/passwd'), undefined);
22
+ });
23
+ (0, node_test_1.test)('accepts the target dir root itself', () => {
24
+ strict_1.default.equal((0, ZipExtractor_1.resolveSafeEntryPath)('/out', '.'), '/out');
25
+ });
26
+ (0, node_test_1.test)('identifies a symlink entry from its externalFileAttributes', () => {
27
+ const symlinkMode = 0xa1ff; // S_IFLNK with permission bits
28
+ const entry = { externalFileAttributes: symlinkMode << 16 };
29
+ strict_1.default.equal((0, ZipExtractor_1.isSymlinkEntry)(entry), true);
30
+ });
31
+ (0, node_test_1.test)('does not treat a regular file entry as a symlink', () => {
32
+ const regularFileMode = 0x81a4; // S_IFREG with permission bits
33
+ const entry = { externalFileAttributes: regularFileMode << 16 };
34
+ strict_1.default.equal((0, ZipExtractor_1.isSymlinkEntry)(entry), false);
35
+ });
36
+ //# sourceMappingURL=ZipExtractor.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ZipExtractor.test.js","sourceRoot":"","sources":["../../../src/ecosystems/enrichment/ZipExtractor.test.ts"],"names":[],"mappings":";;;;;AAAA,gEAAwC;AACxC,yCAAiC;AACjC,yCAAiC;AACjC,iDAAsE;AAEtE,IAAA,gBAAI,EAAC,yDAAyD,EAAE,GAAG,EAAE;IACnE,MAAM,MAAM,GAAG,IAAA,mCAAoB,EACjC,MAAM,EACN,6CAA6C,CAC9C,CAAC;IACF,gBAAM,CAAC,KAAK,CACV,MAAM,EACN,IAAA,gBAAI,EAAC,MAAM,EAAE,6CAA6C,CAAC,CAC5D,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAA,gBAAI,EAAC,8DAA8D,EAAE,GAAG,EAAE;IACxE,gBAAM,CAAC,KAAK,CAAC,IAAA,mCAAoB,EAAC,MAAM,EAAE,kBAAkB,CAAC,EAAE,SAAS,CAAC,CAAC;AAC5E,CAAC,CAAC,CAAC;AAEH,IAAA,gBAAI,EAAC,4EAA4E,EAAE,GAAG,EAAE;IACtF,gBAAM,CAAC,KAAK,CAAC,IAAA,mCAAoB,EAAC,MAAM,EAAE,aAAa,CAAC,EAAE,SAAS,CAAC,CAAC;AACvE,CAAC,CAAC,CAAC;AAEH,IAAA,gBAAI,EAAC,2EAA2E,EAAE,GAAG,EAAE;IACrF,gBAAM,CAAC,KAAK,CACV,IAAA,mCAAoB,EAAC,MAAM,EAAE,yBAAyB,CAAC,EACvD,SAAS,CACV,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAA,gBAAI,EAAC,oCAAoC,EAAE,GAAG,EAAE;IAC9C,gBAAM,CAAC,KAAK,CAAC,IAAA,mCAAoB,EAAC,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;AAC1D,CAAC,CAAC,CAAC;AAEH,IAAA,gBAAI,EAAC,4DAA4D,EAAE,GAAG,EAAE;IACtE,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,+BAA+B;IAC3D,MAAM,KAAK,GAAG,EAAE,sBAAsB,EAAE,WAAW,IAAI,EAAE,EAErD,CAAC;IACL,gBAAM,CAAC,KAAK,CAAC,IAAA,6BAAc,EAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;AAC5C,CAAC,CAAC,CAAC;AAEH,IAAA,gBAAI,EAAC,kDAAkD,EAAE,GAAG,EAAE;IAC5D,MAAM,eAAe,GAAG,MAAM,CAAC,CAAC,+BAA+B;IAC/D,MAAM,KAAK,GAAG,EAAE,sBAAsB,EAAE,eAAe,IAAI,EAAE,EAEzD,CAAC;IACL,gBAAM,CAAC,KAAK,CAAC,IAAA,6BAAc,EAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;AAC7C,CAAC,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { CommandRunner } from '../../core/CommandRunner';
2
+ import { DetectedArtifact, Detector, ScanOptions } from '../../core/types';
3
+ export declare class GradleDependencyDetector implements Detector {
4
+ private commandRunner;
5
+ readonly ecosystem: 'gradle';
6
+ constructor(commandRunner?: CommandRunner);
7
+ isApplicable(options: ScanOptions): Promise<boolean>;
8
+ detect(options: ScanOptions): Promise<DetectedArtifact[]>;
9
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GradleDependencyDetector = void 0;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const CommandRunner_1 = require("../../core/CommandRunner");
7
+ const gradleDependencyOutputParser_1 = require("./parsers/gradleDependencyOutputParser");
8
+ const resolveExecutable_1 = require("./resolveExecutable");
9
+ // dist/ecosystems/java -> dist/ecosystems -> dist -> package root -> assets/...
10
+ const INIT_SCRIPT_PATH = (0, path_1.join)(__dirname, '..', '..', '..', 'assets', 'gradle-init', 'list-dependencies.init.gradle');
11
+ class GradleDependencyDetector {
12
+ constructor(commandRunner = new CommandRunner_1.RealCommandRunner()) {
13
+ this.commandRunner = commandRunner;
14
+ this.ecosystem = 'gradle';
15
+ }
16
+ async isApplicable(options) {
17
+ return ((0, fs_1.existsSync)((0, path_1.join)(options.scanDir, 'build.gradle')) ||
18
+ (0, fs_1.existsSync)((0, path_1.join)(options.scanDir, 'build.gradle.kts')));
19
+ }
20
+ async detect(options) {
21
+ const gradle = (0, resolveExecutable_1.resolveExecutable)(options.scanDir, 'gradlew', 'gradle', 'gradlew.bat');
22
+ const stdout = await this.commandRunner.run(`${gradle} --init-script ${INIT_SCRIPT_PATH} cveScannerListDependencies -q`, options.scanDir);
23
+ return (0, gradleDependencyOutputParser_1.gradleDependencyOutputParser)(stdout).map((dep) => ({
24
+ ecosystem: this.ecosystem,
25
+ coordinate: `${dep.group}:${dep.name}`,
26
+ version: dep.version,
27
+ source: 'build.gradle via gradle init-script',
28
+ projectRoot: options.scanDir,
29
+ }));
30
+ }
31
+ }
32
+ exports.GradleDependencyDetector = GradleDependencyDetector;
33
+ //# sourceMappingURL=GradleDependencyDetector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GradleDependencyDetector.js","sourceRoot":"","sources":["../../../src/ecosystems/java/GradleDependencyDetector.ts"],"names":[],"mappings":";;;AAAA,2BAAgC;AAChC,+BAA4B;AAC5B,4DAA4E;AAE5E,yFAAsF;AACtF,2DAAwD;AAExD,gFAAgF;AAChF,MAAM,gBAAgB,GAAG,IAAA,WAAI,EAC3B,SAAS,EACT,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,QAAQ,EACR,aAAa,EACb,+BAA+B,CAChC,CAAC;AAEF;IAGE,YAAoB,aAAa,GAAkB,IAAI,iCAAiB,EAAE;QAAtD,kBAAa,GAAb,aAAa,CAAyC;QAFjE,cAAS,GAAG,QAAiB,CAAC;IAEsC,CAAC;IAE9E,KAAK,CAAC,YAAY,CAAC,OAAoB;QACrC,OAAO,CACL,IAAA,eAAU,EAAC,IAAA,WAAI,EAAC,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;YACjD,IAAA,eAAU,EAAC,IAAA,WAAI,EAAC,OAAO,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC,CACtD,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAoB;QAC/B,MAAM,MAAM,GAAG,IAAA,qCAAiB,EAC9B,OAAO,CAAC,OAAO,EACf,SAAS,EACT,QAAQ,EACR,aAAa,CACd,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,CACzC,GAAG,MAAM,kBAAkB,gBAAgB,gCAAgC,EAC3E,OAAO,CAAC,OAAO,CAChB,CAAC;QACF,OAAO,IAAA,2DAA4B,EAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YACxD,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,UAAU,EAAE,GAAG,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,IAAI,EAAE;YACtC,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,MAAM,EAAE,qCAAqC;YAC7C,WAAW,EAAE,OAAO,CAAC,OAAO;SAC7B,CAAC,CAAC,CAAC;IACN,CAAC;CACF"}
@@ -0,0 +1,9 @@
1
+ import { CommandRunner } from '../../core/CommandRunner';
2
+ import { DetectedArtifact, Detector, ScanOptions } from '../../core/types';
3
+ export declare class MavenDependencyDetector implements Detector {
4
+ private commandRunner;
5
+ readonly ecosystem: 'maven';
6
+ constructor(commandRunner?: CommandRunner);
7
+ isApplicable(options: ScanOptions): Promise<boolean>;
8
+ detect(options: ScanOptions): Promise<DetectedArtifact[]>;
9
+ }
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MavenDependencyDetector = void 0;
4
+ const fs_1 = require("fs");
5
+ const os_1 = require("os");
6
+ const path_1 = require("path");
7
+ const CommandRunner_1 = require("../../core/CommandRunner");
8
+ const mavenDependencyListParser_1 = require("./parsers/mavenDependencyListParser");
9
+ const resolveExecutable_1 = require("./resolveExecutable");
10
+ class MavenDependencyDetector {
11
+ constructor(commandRunner = new CommandRunner_1.RealCommandRunner()) {
12
+ this.commandRunner = commandRunner;
13
+ this.ecosystem = 'maven';
14
+ }
15
+ async isApplicable(options) {
16
+ return (0, fs_1.existsSync)((0, path_1.join)(options.scanDir, 'pom.xml'));
17
+ }
18
+ async detect(options) {
19
+ const mvn = (0, resolveExecutable_1.resolveExecutable)(options.scanDir, 'mvnw', 'mvn', 'mvnw.cmd');
20
+ const tmpDir = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'cve-scanner-mvn-'));
21
+ const outputFile = (0, path_1.join)(tmpDir, 'dependencies.txt');
22
+ try {
23
+ await this.commandRunner.run(`${mvn} dependency:list -DoutputFile=${outputFile} -DappendOutput=false`, options.scanDir);
24
+ const text = (0, fs_1.readFileSync)(outputFile, 'utf8');
25
+ return (0, mavenDependencyListParser_1.mavenDependencyListParser)(text).map((dep) => ({
26
+ ecosystem: this.ecosystem,
27
+ coordinate: `${dep.groupId}:${dep.artifactId}`,
28
+ version: dep.version,
29
+ source: 'pom.xml via mvn dependency:list',
30
+ projectRoot: options.scanDir,
31
+ }));
32
+ }
33
+ finally {
34
+ (0, fs_1.rmSync)(tmpDir, { recursive: true, force: true });
35
+ }
36
+ }
37
+ }
38
+ exports.MavenDependencyDetector = MavenDependencyDetector;
39
+ //# sourceMappingURL=MavenDependencyDetector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MavenDependencyDetector.js","sourceRoot":"","sources":["../../../src/ecosystems/java/MavenDependencyDetector.ts"],"names":[],"mappings":";;;AAAA,2BAAmE;AACnE,2BAA4B;AAC5B,+BAA4B;AAC5B,4DAA4E;AAE5E,mFAAgF;AAChF,2DAAwD;AAExD;IAGE,YAAoB,aAAa,GAAkB,IAAI,iCAAiB,EAAE;QAAtD,kBAAa,GAAb,aAAa,CAAyC;QAFjE,cAAS,GAAG,OAAgB,CAAC;IAEuC,CAAC;IAE9E,KAAK,CAAC,YAAY,CAAC,OAAoB;QACrC,OAAO,IAAA,eAAU,EAAC,IAAA,WAAI,EAAC,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAoB;QAC/B,MAAM,GAAG,GAAG,IAAA,qCAAiB,EAAC,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;QAC1E,MAAM,MAAM,GAAG,IAAA,gBAAW,EAAC,IAAA,WAAI,EAAC,IAAA,WAAM,GAAE,EAAE,kBAAkB,CAAC,CAAC,CAAC;QAC/D,MAAM,UAAU,GAAG,IAAA,WAAI,EAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;QAEpD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,CAC1B,GAAG,GAAG,iCAAiC,UAAU,uBAAuB,EACxE,OAAO,CAAC,OAAO,CAChB,CAAC;YACF,MAAM,IAAI,GAAG,IAAA,iBAAY,EAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YAC9C,OAAO,IAAA,qDAAyB,EAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;gBACnD,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,UAAU,EAAE,GAAG,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,UAAU,EAAE;gBAC9C,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,MAAM,EAAE,iCAAiC;gBACzC,WAAW,EAAE,OAAO,CAAC,OAAO;aAC7B,CAAC,CAAC,CAAC;QACN,CAAC;gBAAS,CAAC;YACT,IAAA,WAAM,EAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,12 @@
1
+ export interface GradleCoordinate {
2
+ group: string;
3
+ name: string;
4
+ version: string;
5
+ }
6
+ export declare const GRADLE_DEP_MARKER = "CVE_SCANNER_DEP:";
7
+ /**
8
+ * Parses stdout produced by the bundled Gradle init script, which prints one
9
+ * "CVE_SCANNER_DEP:<group>:<name>:<version>" line per resolved module.
10
+ * See specs/30-java-detector.md.
11
+ */
12
+ export declare function gradleDependencyOutputParser(text: string): GradleCoordinate[];
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GRADLE_DEP_MARKER = void 0;
4
+ exports.gradleDependencyOutputParser = gradleDependencyOutputParser;
5
+ exports.GRADLE_DEP_MARKER = 'CVE_SCANNER_DEP:';
6
+ /**
7
+ * Parses stdout produced by the bundled Gradle init script, which prints one
8
+ * "CVE_SCANNER_DEP:<group>:<name>:<version>" line per resolved module.
9
+ * See specs/30-java-detector.md.
10
+ */
11
+ function gradleDependencyOutputParser(text) {
12
+ const results = [];
13
+ const seen = new Set();
14
+ for (const rawLine of text.split(/\r?\n/)) {
15
+ const line = rawLine.trim();
16
+ if (!line.startsWith(exports.GRADLE_DEP_MARKER))
17
+ continue;
18
+ const parts = line.slice(exports.GRADLE_DEP_MARKER.length).split(':');
19
+ if (parts.length !== 3)
20
+ continue;
21
+ const [group, name, version] = parts;
22
+ if (!group || !name || !version)
23
+ continue;
24
+ const key = `${group}:${name}:${version}`;
25
+ if (seen.has(key))
26
+ continue;
27
+ seen.add(key);
28
+ results.push({ group, name, version });
29
+ }
30
+ return results;
31
+ }
32
+ //# sourceMappingURL=gradleDependencyOutputParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gradleDependencyOutputParser.js","sourceRoot":"","sources":["../../../../src/ecosystems/java/parsers/gradleDependencyOutputParser.ts"],"names":[],"mappings":";;;;AAMa,QAAA,iBAAiB,GAAG,kBAAkB,CAAC;AAEpD;;;;GAIG;AACH,sCAA6C,IAAY;IACvD,MAAM,OAAO,GAAuB,EAAE,CAAC;IACvC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAA,iBAAiB,CAAC;YAAE,SAAS;QAElD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAA,iBAAiB,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC9D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAEjC,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC;QACrC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO;YAAE,SAAS;QAE1C,MAAM,GAAG,GAAG,GAAG,KAAK,IAAI,IAAI,IAAI,OAAO,EAAE,CAAC;QAC1C,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEd,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}