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.
- package/README.md +96 -45
- package/assets/gradle-init/list-dependencies.init.gradle +20 -0
- package/data/dataset.hash +1 -0
- package/data/dataset.json +288056 -0
- package/dist/ai/AiAssistant.d.ts +11 -0
- package/dist/ai/AiAssistant.js +34 -0
- package/dist/ai/AiAssistant.js.map +1 -0
- package/dist/ai/ProjectManifestReader.d.ts +8 -0
- package/dist/ai/ProjectManifestReader.js +34 -0
- package/dist/ai/ProjectManifestReader.js.map +1 -0
- package/dist/ai/ProjectManifestReader.test.d.ts +1 -0
- package/dist/ai/ProjectManifestReader.test.js +59 -0
- package/dist/ai/ProjectManifestReader.test.js.map +1 -0
- package/dist/ai/PromptBuilder.d.ts +5 -0
- package/dist/ai/PromptBuilder.js +42 -0
- package/dist/ai/PromptBuilder.js.map +1 -0
- package/dist/ai/PromptBuilder.test.d.ts +1 -0
- package/dist/ai/PromptBuilder.test.js +66 -0
- package/dist/ai/PromptBuilder.test.js.map +1 -0
- package/dist/cmd/cve-scanner.d.ts +2 -0
- package/dist/cmd/cve-scanner.js +60 -0
- package/dist/cmd/cve-scanner.js.map +1 -0
- package/dist/core/CommandRunner.d.ts +6 -0
- package/dist/core/CommandRunner.js +16 -0
- package/dist/core/CommandRunner.js.map +1 -0
- package/dist/core/DatasetLoader.d.ts +14 -0
- package/dist/core/DatasetLoader.js +44 -0
- package/dist/core/DatasetLoader.js.map +1 -0
- package/dist/core/DatasetLoader.test.d.ts +1 -0
- package/dist/core/DatasetLoader.test.js +41 -0
- package/dist/core/DatasetLoader.test.js.map +1 -0
- package/dist/core/ProjectRootDiscovery.d.ts +9 -0
- package/dist/core/ProjectRootDiscovery.js +62 -0
- package/dist/core/ProjectRootDiscovery.js.map +1 -0
- package/dist/core/ProjectRootDiscovery.test.d.ts +1 -0
- package/dist/core/ProjectRootDiscovery.test.js +47 -0
- package/dist/core/ProjectRootDiscovery.test.js.map +1 -0
- package/dist/core/Scanner.d.ts +19 -0
- package/dist/core/Scanner.js +66 -0
- package/dist/core/Scanner.js.map +1 -0
- package/dist/core/Scanner.test.d.ts +1 -0
- package/dist/core/Scanner.test.js +157 -0
- package/dist/core/Scanner.test.js.map +1 -0
- package/dist/core/VersionComparator.d.ts +6 -0
- package/dist/core/VersionComparator.js +35 -0
- package/dist/core/VersionComparator.js.map +1 -0
- package/dist/core/VersionComparator.test.d.ts +1 -0
- package/dist/core/VersionComparator.test.js +21 -0
- package/dist/core/VersionComparator.test.js.map +1 -0
- package/dist/core/cpe.d.ts +9 -0
- package/dist/core/cpe.js +32 -0
- package/dist/core/cpe.js.map +1 -0
- package/dist/core/cpe.test.d.ts +1 -0
- package/dist/core/cpe.test.js +25 -0
- package/dist/core/cpe.test.js.map +1 -0
- package/dist/core/types.d.ts +57 -0
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -0
- package/dist/ecosystems/enrichment/CveListCache.d.ts +13 -0
- package/dist/ecosystems/enrichment/CveListCache.js +88 -0
- package/dist/ecosystems/enrichment/CveListCache.js.map +1 -0
- package/dist/ecosystems/enrichment/CveListCache.test.d.ts +1 -0
- package/dist/ecosystems/enrichment/CveListCache.test.js +49 -0
- package/dist/ecosystems/enrichment/CveListCache.test.js.map +1 -0
- package/dist/ecosystems/enrichment/CveOrgDatasetReader.d.ts +12 -0
- package/dist/ecosystems/enrichment/CveOrgDatasetReader.js +59 -0
- package/dist/ecosystems/enrichment/CveOrgDatasetReader.js.map +1 -0
- package/dist/ecosystems/enrichment/CveOrgDatasetReader.test.d.ts +1 -0
- package/dist/ecosystems/enrichment/CveOrgDatasetReader.test.js +48 -0
- package/dist/ecosystems/enrichment/CveOrgDatasetReader.test.js.map +1 -0
- package/dist/ecosystems/enrichment/ZipExtractor.d.ts +18 -0
- package/dist/ecosystems/enrichment/ZipExtractor.js +105 -0
- package/dist/ecosystems/enrichment/ZipExtractor.js.map +1 -0
- package/dist/ecosystems/enrichment/ZipExtractor.test.d.ts +1 -0
- package/dist/ecosystems/enrichment/ZipExtractor.test.js +36 -0
- package/dist/ecosystems/enrichment/ZipExtractor.test.js.map +1 -0
- package/dist/ecosystems/java/GradleDependencyDetector.d.ts +9 -0
- package/dist/ecosystems/java/GradleDependencyDetector.js +33 -0
- package/dist/ecosystems/java/GradleDependencyDetector.js.map +1 -0
- package/dist/ecosystems/java/MavenDependencyDetector.d.ts +9 -0
- package/dist/ecosystems/java/MavenDependencyDetector.js +39 -0
- package/dist/ecosystems/java/MavenDependencyDetector.js.map +1 -0
- package/dist/ecosystems/java/parsers/gradleDependencyOutputParser.d.ts +12 -0
- package/dist/ecosystems/java/parsers/gradleDependencyOutputParser.js +32 -0
- package/dist/ecosystems/java/parsers/gradleDependencyOutputParser.js.map +1 -0
- package/dist/ecosystems/java/parsers/gradleDependencyOutputParser.test.d.ts +1 -0
- package/dist/ecosystems/java/parsers/gradleDependencyOutputParser.test.js +36 -0
- package/dist/ecosystems/java/parsers/gradleDependencyOutputParser.test.js.map +1 -0
- package/dist/ecosystems/java/parsers/mavenDependencyListParser.d.ts +11 -0
- package/dist/ecosystems/java/parsers/mavenDependencyListParser.js +31 -0
- package/dist/ecosystems/java/parsers/mavenDependencyListParser.js.map +1 -0
- package/dist/ecosystems/java/parsers/mavenDependencyListParser.test.d.ts +1 -0
- package/dist/ecosystems/java/parsers/mavenDependencyListParser.test.js +37 -0
- package/dist/ecosystems/java/parsers/mavenDependencyListParser.test.js.map +1 -0
- package/dist/ecosystems/java/resolveExecutable.d.ts +8 -0
- package/dist/ecosystems/java/resolveExecutable.js +22 -0
- package/dist/ecosystems/java/resolveExecutable.js.map +1 -0
- package/dist/ecosystems/java/resolveExecutable.test.d.ts +1 -0
- package/dist/ecosystems/java/resolveExecutable.test.js +42 -0
- package/dist/ecosystems/java/resolveExecutable.test.js.map +1 -0
- package/dist/ecosystems/npm/NpmDetector.d.ts +6 -0
- package/dist/ecosystems/npm/NpmDetector.js +31 -0
- package/dist/ecosystems/npm/NpmDetector.js.map +1 -0
- package/dist/ecosystems/npm/PackageLockParser.d.ts +11 -0
- package/dist/ecosystems/npm/PackageLockParser.js +65 -0
- package/dist/ecosystems/npm/PackageLockParser.js.map +1 -0
- package/dist/ecosystems/npm/PackageLockParser.test.d.ts +1 -0
- package/dist/ecosystems/npm/PackageLockParser.test.js +59 -0
- package/dist/ecosystems/npm/PackageLockParser.test.js.map +1 -0
- package/dist/lib/CveScannerApi.d.ts +13 -0
- package/dist/lib/CveScannerApi.js +55 -0
- package/dist/lib/CveScannerApi.js.map +1 -0
- package/dist/lib/report.d.ts +4 -0
- package/dist/lib/report.js +47 -0
- package/dist/lib/report.js.map +1 -0
- package/dist/pipeline/dedupeEntries.d.ts +8 -0
- package/dist/pipeline/dedupeEntries.js +20 -0
- package/dist/pipeline/dedupeEntries.js.map +1 -0
- package/dist/pipeline/dedupeEntries.test.d.ts +1 -0
- package/dist/pipeline/dedupeEntries.test.js +27 -0
- package/dist/pipeline/dedupeEntries.test.js.map +1 -0
- package/dist/pipeline/hashDataset.d.ts +7 -0
- package/dist/pipeline/hashDataset.js +13 -0
- package/dist/pipeline/hashDataset.js.map +1 -0
- package/dist/pipeline/hashDataset.test.d.ts +1 -0
- package/dist/pipeline/hashDataset.test.js +28 -0
- package/dist/pipeline/hashDataset.test.js.map +1 -0
- package/dist/pipeline/mapSeverityToScore.d.ts +9 -0
- package/dist/pipeline/mapSeverityToScore.js +26 -0
- package/dist/pipeline/mapSeverityToScore.js.map +1 -0
- package/dist/pipeline/mapSeverityToScore.test.d.ts +1 -0
- package/dist/pipeline/mapSeverityToScore.test.js +20 -0
- package/dist/pipeline/mapSeverityToScore.test.js.map +1 -0
- package/dist/pipeline/parseOsvRecord.d.ts +37 -0
- package/dist/pipeline/parseOsvRecord.js +88 -0
- package/dist/pipeline/parseOsvRecord.js.map +1 -0
- package/dist/pipeline/parseOsvRecord.test.d.ts +1 -0
- package/dist/pipeline/parseOsvRecord.test.js +220 -0
- package/dist/pipeline/parseOsvRecord.test.js.map +1 -0
- package/package.json +37 -6
|
@@ -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_test_1 = require("node:test");
|
|
8
|
+
const gradleDependencyOutputParser_1 = require("./gradleDependencyOutputParser");
|
|
9
|
+
(0, node_test_1.test)('parses marked dependency lines and ignores everything else', () => {
|
|
10
|
+
const text = `
|
|
11
|
+
> Task :cveScannerListDependencies
|
|
12
|
+
CVE_SCANNER_DEP:org.apache.logging.log4j:log4j-core:2.14.1
|
|
13
|
+
some other gradle noise
|
|
14
|
+
CVE_SCANNER_DEP:com.google.guava:guava:31.1-jre
|
|
15
|
+
|
|
16
|
+
BUILD SUCCESSFUL in 1s
|
|
17
|
+
`;
|
|
18
|
+
strict_1.default.deepEqual((0, gradleDependencyOutputParser_1.gradleDependencyOutputParser)(text), [
|
|
19
|
+
{
|
|
20
|
+
group: 'org.apache.logging.log4j',
|
|
21
|
+
name: 'log4j-core',
|
|
22
|
+
version: '2.14.1',
|
|
23
|
+
},
|
|
24
|
+
{ group: 'com.google.guava', name: 'guava', version: '31.1-jre' },
|
|
25
|
+
]);
|
|
26
|
+
});
|
|
27
|
+
(0, node_test_1.test)('deduplicates identical lines', () => {
|
|
28
|
+
const text = `
|
|
29
|
+
CVE_SCANNER_DEP:a:b:1.0
|
|
30
|
+
CVE_SCANNER_DEP:a:b:1.0
|
|
31
|
+
`;
|
|
32
|
+
strict_1.default.deepEqual((0, gradleDependencyOutputParser_1.gradleDependencyOutputParser)(text), [
|
|
33
|
+
{ group: 'a', name: 'b', version: '1.0' },
|
|
34
|
+
]);
|
|
35
|
+
});
|
|
36
|
+
//# sourceMappingURL=gradleDependencyOutputParser.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gradleDependencyOutputParser.test.js","sourceRoot":"","sources":["../../../../src/ecosystems/java/parsers/gradleDependencyOutputParser.test.ts"],"names":[],"mappings":";;;;;AAAA,gEAAwC;AACxC,yCAAiC;AACjC,iFAA8E;AAE9E,IAAA,gBAAI,EAAC,4DAA4D,EAAE,GAAG,EAAE;IACtE,MAAM,IAAI,GAAG;;;;;;;CAOd,CAAC;IAEA,gBAAM,CAAC,SAAS,CAAC,IAAA,2DAA4B,EAAC,IAAI,CAAC,EAAE;QACnD;YACE,KAAK,EAAE,0BAA0B;YACjC,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,QAAQ;SAClB;QACD,EAAE,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE;KAClE,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,IAAA,gBAAI,EAAC,8BAA8B,EAAE,GAAG,EAAE;IACxC,MAAM,IAAI,GAAG;;;CAGd,CAAC;IACA,gBAAM,CAAC,SAAS,CAAC,IAAA,2DAA4B,EAAC,IAAI,CAAC,EAAE;QACnD,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE;KAC1C,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface MavenCoordinate {
|
|
2
|
+
groupId: string;
|
|
3
|
+
artifactId: string;
|
|
4
|
+
version: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Parses the text written by `mvn dependency:list -DoutputFile=<file>`.
|
|
8
|
+
* Typical line: " com.fasterxml.jackson.core:jackson-databind:jar:2.9.8:compile"
|
|
9
|
+
* See specs/30-java-detector.md.
|
|
10
|
+
*/
|
|
11
|
+
export declare function mavenDependencyListParser(text: string): MavenCoordinate[];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mavenDependencyListParser = mavenDependencyListParser;
|
|
4
|
+
/**
|
|
5
|
+
* Parses the text written by `mvn dependency:list -DoutputFile=<file>`.
|
|
6
|
+
* Typical line: " com.fasterxml.jackson.core:jackson-databind:jar:2.9.8:compile"
|
|
7
|
+
* See specs/30-java-detector.md.
|
|
8
|
+
*/
|
|
9
|
+
function mavenDependencyListParser(text) {
|
|
10
|
+
const results = [];
|
|
11
|
+
const seen = new Set();
|
|
12
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
13
|
+
const line = rawLine.trim();
|
|
14
|
+
const parts = line.split(':');
|
|
15
|
+
if (parts.length < 4)
|
|
16
|
+
continue;
|
|
17
|
+
const [groupId, artifactId, , version] = parts;
|
|
18
|
+
if (!groupId || !artifactId || !version)
|
|
19
|
+
continue;
|
|
20
|
+
if (!/^[A-Za-z0-9._-]+$/.test(groupId) ||
|
|
21
|
+
!/^[A-Za-z0-9._-]+$/.test(artifactId))
|
|
22
|
+
continue;
|
|
23
|
+
const key = `${groupId}:${artifactId}:${version}`;
|
|
24
|
+
if (seen.has(key))
|
|
25
|
+
continue;
|
|
26
|
+
seen.add(key);
|
|
27
|
+
results.push({ groupId, artifactId, version });
|
|
28
|
+
}
|
|
29
|
+
return results;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=mavenDependencyListParser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mavenDependencyListParser.js","sourceRoot":"","sources":["../../../../src/ecosystems/java/parsers/mavenDependencyListParser.ts"],"names":[],"mappings":";;;AAMA;;;;GAIG;AACH,mCAA0C,IAAY;IACpD,MAAM,OAAO,GAAsB,EAAE,CAAC;IACtC,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,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,SAAS;QAE/B,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,AAAD,EAAG,OAAO,CAAC,GAAG,KAAK,CAAC;QAC/C,IAAI,CAAC,OAAO,IAAI,CAAC,UAAU,IAAI,CAAC,OAAO;YAAE,SAAS;QAClD,IACE,CAAC,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC;YAClC,CAAC,mBAAmB,CAAC,IAAI,CAAC,UAAU,CAAC;YAErC,SAAS;QAEX,MAAM,GAAG,GAAG,GAAG,OAAO,IAAI,UAAU,IAAI,OAAO,EAAE,CAAC;QAClD,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEd,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC;IACjD,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
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_test_1 = require("node:test");
|
|
8
|
+
const mavenDependencyListParser_1 = require("./mavenDependencyListParser");
|
|
9
|
+
(0, node_test_1.test)('parses a typical mvn dependency:list output file', () => {
|
|
10
|
+
const text = `
|
|
11
|
+
The following files have been resolved:
|
|
12
|
+
com.fasterxml.jackson.core:jackson-databind:jar:2.9.8:compile
|
|
13
|
+
junit:junit:jar:4.13.2:test
|
|
14
|
+
`;
|
|
15
|
+
strict_1.default.deepEqual((0, mavenDependencyListParser_1.mavenDependencyListParser)(text), [
|
|
16
|
+
{
|
|
17
|
+
groupId: 'com.fasterxml.jackson.core',
|
|
18
|
+
artifactId: 'jackson-databind',
|
|
19
|
+
version: '2.9.8',
|
|
20
|
+
},
|
|
21
|
+
{ groupId: 'junit', artifactId: 'junit', version: '4.13.2' },
|
|
22
|
+
]);
|
|
23
|
+
});
|
|
24
|
+
(0, node_test_1.test)('ignores blank lines and banners', () => {
|
|
25
|
+
const text = '\n\nThe following files have been resolved:\n\n';
|
|
26
|
+
strict_1.default.deepEqual((0, mavenDependencyListParser_1.mavenDependencyListParser)(text), []);
|
|
27
|
+
});
|
|
28
|
+
(0, node_test_1.test)('deduplicates identical coordinate:version lines', () => {
|
|
29
|
+
const text = `
|
|
30
|
+
com.example:lib:jar:1.0.0:compile
|
|
31
|
+
com.example:lib:jar:1.0.0:compile
|
|
32
|
+
`;
|
|
33
|
+
strict_1.default.deepEqual((0, mavenDependencyListParser_1.mavenDependencyListParser)(text), [
|
|
34
|
+
{ groupId: 'com.example', artifactId: 'lib', version: '1.0.0' },
|
|
35
|
+
]);
|
|
36
|
+
});
|
|
37
|
+
//# sourceMappingURL=mavenDependencyListParser.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mavenDependencyListParser.test.js","sourceRoot":"","sources":["../../../../src/ecosystems/java/parsers/mavenDependencyListParser.test.ts"],"names":[],"mappings":";;;;;AAAA,gEAAwC;AACxC,yCAAiC;AACjC,2EAAwE;AAExE,IAAA,gBAAI,EAAC,kDAAkD,EAAE,GAAG,EAAE;IAC5D,MAAM,IAAI,GAAG;;;;CAId,CAAC;IAEA,gBAAM,CAAC,SAAS,CAAC,IAAA,qDAAyB,EAAC,IAAI,CAAC,EAAE;QAChD;YACE,OAAO,EAAE,4BAA4B;YACrC,UAAU,EAAE,kBAAkB;YAC9B,OAAO,EAAE,OAAO;SACjB;QACD,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE;KAC7D,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,IAAA,gBAAI,EAAC,iCAAiC,EAAE,GAAG,EAAE;IAC3C,MAAM,IAAI,GAAG,iDAAiD,CAAC;IAC/D,gBAAM,CAAC,SAAS,CAAC,IAAA,qDAAyB,EAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;AACxD,CAAC,CAAC,CAAC;AAEH,IAAA,gBAAI,EAAC,iDAAiD,EAAE,GAAG,EAAE;IAC3D,MAAM,IAAI,GAAG;;;CAGd,CAAC;IACA,gBAAM,CAAC,SAAS,CAAC,IAAA,qDAAyB,EAAC,IAAI,CAAC,EAAE;QAChD,EAAE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE;KAChE,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prefer the project's own wrapper script (e.g. ./mvnw, ./gradlew) if checked
|
|
3
|
+
* in, otherwise fall back to a global executable resolved via PATH. On
|
|
4
|
+
* Windows, prefers the platform's own wrapper variant (e.g. mvnw.cmd,
|
|
5
|
+
* gradlew.bat) over the Unix shell script, since cmd.exe can't run the
|
|
6
|
+
* latter directly. See specs/30-java-detector.md.
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveExecutable(scanDir: string, wrapperName: string, globalName: string, windowsWrapperName?: string): string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveExecutable = resolveExecutable;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
/**
|
|
7
|
+
* Prefer the project's own wrapper script (e.g. ./mvnw, ./gradlew) if checked
|
|
8
|
+
* in, otherwise fall back to a global executable resolved via PATH. On
|
|
9
|
+
* Windows, prefers the platform's own wrapper variant (e.g. mvnw.cmd,
|
|
10
|
+
* gradlew.bat) over the Unix shell script, since cmd.exe can't run the
|
|
11
|
+
* latter directly. See specs/30-java-detector.md.
|
|
12
|
+
*/
|
|
13
|
+
function resolveExecutable(scanDir, wrapperName, globalName, windowsWrapperName) {
|
|
14
|
+
if (process.platform === 'win32' && windowsWrapperName) {
|
|
15
|
+
const windowsWrapperPath = (0, path_1.join)(scanDir, windowsWrapperName);
|
|
16
|
+
if ((0, fs_1.existsSync)(windowsWrapperPath))
|
|
17
|
+
return windowsWrapperPath;
|
|
18
|
+
}
|
|
19
|
+
const wrapperPath = (0, path_1.join)(scanDir, wrapperName);
|
|
20
|
+
return (0, fs_1.existsSync)(wrapperPath) ? wrapperPath : globalName;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=resolveExecutable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveExecutable.js","sourceRoot":"","sources":["../../../src/ecosystems/java/resolveExecutable.ts"],"names":[],"mappings":";;;AAAA,2BAAgC;AAChC,+BAA4B;AAE5B;;;;;;GAMG;AACH,2BACE,OAAe,EACf,WAAmB,EACnB,UAAkB,EAClB,kBAA2B;IAE3B,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,kBAAkB,EAAE,CAAC;QACvD,MAAM,kBAAkB,GAAG,IAAA,WAAI,EAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;QAC7D,IAAI,IAAA,eAAU,EAAC,kBAAkB,CAAC;YAAE,OAAO,kBAAkB,CAAC;IAChE,CAAC;IAED,MAAM,WAAW,GAAG,IAAA,WAAI,EAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAC/C,OAAO,IAAA,eAAU,EAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC;AAC5D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
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 resolveExecutable_1 = require("./resolveExecutable");
|
|
12
|
+
function makeScanDir() {
|
|
13
|
+
return (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'cve-scanner-exec-'));
|
|
14
|
+
}
|
|
15
|
+
(0, node_test_1.test)('falls back to the global command when no wrapper is checked in', () => {
|
|
16
|
+
const dir = makeScanDir();
|
|
17
|
+
strict_1.default.equal((0, resolveExecutable_1.resolveExecutable)(dir, 'mvnw', 'mvn', 'mvnw.cmd'), 'mvn');
|
|
18
|
+
});
|
|
19
|
+
(0, node_test_1.test)('prefers the checked-in Unix wrapper when not on Windows', () => {
|
|
20
|
+
const dir = makeScanDir();
|
|
21
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, 'mvnw'), '#!/bin/sh');
|
|
22
|
+
strict_1.default.equal((0, resolveExecutable_1.resolveExecutable)(dir, 'mvnw', 'mvn', 'mvnw.cmd'), (0, node_path_1.join)(dir, 'mvnw'));
|
|
23
|
+
});
|
|
24
|
+
(0, node_test_1.test)('ignores the windows variant when not given one', () => {
|
|
25
|
+
const dir = makeScanDir();
|
|
26
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, 'gradlew'), '#!/bin/sh');
|
|
27
|
+
strict_1.default.equal((0, resolveExecutable_1.resolveExecutable)(dir, 'gradlew', 'gradle'), (0, node_path_1.join)(dir, 'gradlew'));
|
|
28
|
+
});
|
|
29
|
+
(0, node_test_1.test)('prefers the windows wrapper variant over the Unix one when running on win32', () => {
|
|
30
|
+
const dir = makeScanDir();
|
|
31
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, 'gradlew'), '#!/bin/sh');
|
|
32
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, 'gradlew.bat'), '@echo off');
|
|
33
|
+
const originalPlatform = process.platform;
|
|
34
|
+
Object.defineProperty(process, 'platform', { value: 'win32' });
|
|
35
|
+
try {
|
|
36
|
+
strict_1.default.equal((0, resolveExecutable_1.resolveExecutable)(dir, 'gradlew', 'gradle', 'gradlew.bat'), (0, node_path_1.join)(dir, 'gradlew.bat'));
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
Object.defineProperty(process, 'platform', { value: originalPlatform });
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
//# sourceMappingURL=resolveExecutable.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveExecutable.test.js","sourceRoot":"","sources":["../../../src/ecosystems/java/resolveExecutable.test.ts"],"names":[],"mappings":";;;;;AAAA,gEAAwC;AACxC,qCAAqD;AACrD,qCAAiC;AACjC,yCAAiC;AACjC,yCAAiC;AACjC,2DAAwD;AAExD,SAAS,WAAW;IAClB,OAAO,IAAA,qBAAW,EAAC,IAAA,gBAAI,EAAC,IAAA,gBAAM,GAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,IAAA,gBAAI,EAAC,gEAAgE,EAAE,GAAG,EAAE;IAC1E,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;IAC1B,gBAAM,CAAC,KAAK,CAAC,IAAA,qCAAiB,EAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,EAAE,KAAK,CAAC,CAAC;AACzE,CAAC,CAAC,CAAC;AAEH,IAAA,gBAAI,EAAC,yDAAyD,EAAE,GAAG,EAAE;IACnE,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;IAC1B,IAAA,uBAAa,EAAC,IAAA,gBAAI,EAAC,GAAG,EAAE,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC;IAC9C,gBAAM,CAAC,KAAK,CACV,IAAA,qCAAiB,EAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,EACjD,IAAA,gBAAI,EAAC,GAAG,EAAE,MAAM,CAAC,CAClB,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAA,gBAAI,EAAC,gDAAgD,EAAE,GAAG,EAAE;IAC1D,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;IAC1B,IAAA,uBAAa,EAAC,IAAA,gBAAI,EAAC,GAAG,EAAE,SAAS,CAAC,EAAE,WAAW,CAAC,CAAC;IACjD,gBAAM,CAAC,KAAK,CACV,IAAA,qCAAiB,EAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,CAAC,EAC3C,IAAA,gBAAI,EAAC,GAAG,EAAE,SAAS,CAAC,CACrB,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAA,gBAAI,EAAC,6EAA6E,EAAE,GAAG,EAAE;IACvF,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;IAC1B,IAAA,uBAAa,EAAC,IAAA,gBAAI,EAAC,GAAG,EAAE,SAAS,CAAC,EAAE,WAAW,CAAC,CAAC;IACjD,IAAA,uBAAa,EAAC,IAAA,gBAAI,EAAC,GAAG,EAAE,aAAa,CAAC,EAAE,WAAW,CAAC,CAAC;IAErD,MAAM,gBAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAC1C,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;IAC/D,IAAI,CAAC;QACH,gBAAM,CAAC,KAAK,CACV,IAAA,qCAAiB,EAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,CAAC,EAC1D,IAAA,gBAAI,EAAC,GAAG,EAAE,aAAa,CAAC,CACzB,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAC1E,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { DetectedArtifact, Detector, ScanOptions } from '../../core/types';
|
|
2
|
+
export declare class NpmDetector implements Detector {
|
|
3
|
+
readonly ecosystem: 'npm';
|
|
4
|
+
isApplicable(options: ScanOptions): Promise<boolean>;
|
|
5
|
+
detect(options: ScanOptions): Promise<DetectedArtifact[]>;
|
|
6
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NpmDetector = void 0;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const PackageLockParser_1 = require("./PackageLockParser");
|
|
7
|
+
const LOCKFILE_NAMES = ['package-lock.json', 'npm-shrinkwrap.json'];
|
|
8
|
+
class NpmDetector {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.ecosystem = 'npm';
|
|
11
|
+
}
|
|
12
|
+
async isApplicable(options) {
|
|
13
|
+
return LOCKFILE_NAMES.some((name) => (0, fs_1.existsSync)((0, path_1.join)(options.scanDir, name)));
|
|
14
|
+
}
|
|
15
|
+
async detect(options) {
|
|
16
|
+
const lockfileName = LOCKFILE_NAMES.find((name) => (0, fs_1.existsSync)((0, path_1.join)(options.scanDir, name)));
|
|
17
|
+
if (!lockfileName)
|
|
18
|
+
return [];
|
|
19
|
+
const raw = (0, fs_1.readFileSync)((0, path_1.join)(options.scanDir, lockfileName), 'utf8');
|
|
20
|
+
const packages = (0, PackageLockParser_1.parsePackageLock)(JSON.parse(raw));
|
|
21
|
+
return packages.map((pkg) => ({
|
|
22
|
+
ecosystem: this.ecosystem,
|
|
23
|
+
coordinate: pkg.name,
|
|
24
|
+
version: pkg.version,
|
|
25
|
+
source: lockfileName,
|
|
26
|
+
projectRoot: options.scanDir,
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.NpmDetector = NpmDetector;
|
|
31
|
+
//# sourceMappingURL=NpmDetector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NpmDetector.js","sourceRoot":"","sources":["../../../src/ecosystems/npm/NpmDetector.ts"],"names":[],"mappings":";;;AAAA,2BAA8C;AAC9C,+BAA4B;AAE5B,2DAAuD;AAEvD,MAAM,cAAc,GAAG,CAAC,mBAAmB,EAAE,qBAAqB,CAAC,CAAC;AAEpE;IAAA;QACW,cAAS,GAAG,KAAc,CAAC;IAyBtC,CAAC;IAvBC,KAAK,CAAC,YAAY,CAAC,OAAoB;QACrC,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAClC,IAAA,eAAU,EAAC,IAAA,WAAI,EAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CACxC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAoB;QAC/B,MAAM,YAAY,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAChD,IAAA,eAAU,EAAC,IAAA,WAAI,EAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CACxC,CAAC;QACF,IAAI,CAAC,YAAY;YAAE,OAAO,EAAE,CAAC;QAE7B,MAAM,GAAG,GAAG,IAAA,iBAAY,EAAC,IAAA,WAAI,EAAC,OAAO,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE,MAAM,CAAC,CAAC;QACtE,MAAM,QAAQ,GAAG,IAAA,oCAAgB,EAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QAEnD,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAC5B,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,UAAU,EAAE,GAAG,CAAC,IAAI;YACpB,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,MAAM,EAAE,YAAY;YACpB,WAAW,EAAE,OAAO,CAAC,OAAO;SAC7B,CAAC,CAAC,CAAC;IACN,CAAC;CACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface NpmPackage {
|
|
2
|
+
name: string;
|
|
3
|
+
version: string;
|
|
4
|
+
integrity?: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Parses package-lock.json / npm-shrinkwrap.json, supporting both the v1
|
|
8
|
+
* nested `dependencies` tree and the v2/v3 flat `packages` map.
|
|
9
|
+
* See specs/40-npm-detector.md.
|
|
10
|
+
*/
|
|
11
|
+
export declare function parsePackageLock(lockfile: unknown): NpmPackage[];
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parsePackageLock = parsePackageLock;
|
|
4
|
+
function packageNameFromNodeModulesPath(pathKey) {
|
|
5
|
+
const idx = pathKey.lastIndexOf('node_modules/');
|
|
6
|
+
if (idx === -1)
|
|
7
|
+
return undefined;
|
|
8
|
+
const afterNodeModules = pathKey.slice(idx + 'node_modules/'.length);
|
|
9
|
+
if (afterNodeModules.startsWith('@')) {
|
|
10
|
+
const [scope, name] = afterNodeModules.split('/');
|
|
11
|
+
return name ? `${scope}/${name}` : undefined;
|
|
12
|
+
}
|
|
13
|
+
return afterNodeModules.split('/')[0];
|
|
14
|
+
}
|
|
15
|
+
function walkV1(dependencies, results) {
|
|
16
|
+
if (!dependencies)
|
|
17
|
+
return;
|
|
18
|
+
for (const [name, entry] of Object.entries(dependencies)) {
|
|
19
|
+
if (entry.version) {
|
|
20
|
+
results.push({
|
|
21
|
+
name,
|
|
22
|
+
version: entry.version,
|
|
23
|
+
integrity: entry.integrity,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
walkV1(entry.dependencies, results);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Parses package-lock.json / npm-shrinkwrap.json, supporting both the v1
|
|
31
|
+
* nested `dependencies` tree and the v2/v3 flat `packages` map.
|
|
32
|
+
* See specs/40-npm-detector.md.
|
|
33
|
+
*/
|
|
34
|
+
function parsePackageLock(lockfile) {
|
|
35
|
+
const results = [];
|
|
36
|
+
const asV2V3 = lockfile;
|
|
37
|
+
if (asV2V3.packages) {
|
|
38
|
+
for (const [pathKey, entry] of Object.entries(asV2V3.packages)) {
|
|
39
|
+
if (pathKey === '')
|
|
40
|
+
continue; // the project itself
|
|
41
|
+
if (!entry.version)
|
|
42
|
+
continue;
|
|
43
|
+
const name = packageNameFromNodeModulesPath(pathKey);
|
|
44
|
+
if (!name)
|
|
45
|
+
continue;
|
|
46
|
+
results.push({
|
|
47
|
+
name,
|
|
48
|
+
version: entry.version,
|
|
49
|
+
integrity: entry.integrity,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
walkV1(lockfile.dependencies, results);
|
|
55
|
+
}
|
|
56
|
+
const seen = new Set();
|
|
57
|
+
return results.filter((pkg) => {
|
|
58
|
+
const key = `${pkg.name}:${pkg.version}`;
|
|
59
|
+
if (seen.has(key))
|
|
60
|
+
return false;
|
|
61
|
+
seen.add(key);
|
|
62
|
+
return true;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=PackageLockParser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PackageLockParser.js","sourceRoot":"","sources":["../../../src/ecosystems/npm/PackageLockParser.ts"],"names":[],"mappings":";;;AA0BA,SAAS,8BAA8B,CAAC,OAAe;IACrD,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;IACjD,IAAI,GAAG,KAAK,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACjC,MAAM,gBAAgB,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACrE,IAAI,gBAAgB,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,gBAAgB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/C,CAAC;IACD,OAAO,gBAAgB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACxC,CAAC;AAED,SAAS,MAAM,CACb,YAAqD,EACrD,OAAqB;IAErB,IAAI,CAAC,YAAY;QAAE,OAAO;IAC1B,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;QACzD,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YAClB,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI;gBACJ,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,SAAS,EAAE,KAAK,CAAC,SAAS;aAC3B,CAAC,CAAC;QACL,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,0BAAiC,QAAiB;IAChD,MAAM,OAAO,GAAiB,EAAE,CAAC;IACjC,MAAM,MAAM,GAAG,QAAwB,CAAC;IAExC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,KAAK,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/D,IAAI,OAAO,KAAK,EAAE;gBAAE,SAAS,CAAC,qBAAqB;YACnD,IAAI,CAAC,KAAK,CAAC,OAAO;gBAAE,SAAS;YAC7B,MAAM,IAAI,GAAG,8BAA8B,CAAC,OAAO,CAAC,CAAC;YACrD,IAAI,CAAC,IAAI;gBAAE,SAAS;YACpB,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI;gBACJ,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,SAAS,EAAE,KAAK,CAAC,SAAS;aAC3B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;SAAM,CAAC;QACN,MAAM,CAAE,QAAuB,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE;QAC5B,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QACzC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QAChC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
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_test_1 = require("node:test");
|
|
8
|
+
const PackageLockParser_1 = require("./PackageLockParser");
|
|
9
|
+
(0, node_test_1.test)('parses a v1 nested dependencies tree, including nested duplicates', () => {
|
|
10
|
+
const lockfile = {
|
|
11
|
+
lockfileVersion: 1,
|
|
12
|
+
dependencies: {
|
|
13
|
+
lodash: { version: '4.17.11', integrity: 'sha512-a' },
|
|
14
|
+
a: {
|
|
15
|
+
version: '1.0.0',
|
|
16
|
+
integrity: 'sha512-b',
|
|
17
|
+
dependencies: {
|
|
18
|
+
lodash: { version: '3.10.1', integrity: 'sha512-c' },
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
const result = (0, PackageLockParser_1.parsePackageLock)(lockfile);
|
|
24
|
+
strict_1.default.deepEqual(result.sort((x, y) => x.version.localeCompare(y.version)), [
|
|
25
|
+
{ name: 'lodash', version: '3.10.1', integrity: 'sha512-c' },
|
|
26
|
+
{ name: 'a', version: '1.0.0', integrity: 'sha512-b' },
|
|
27
|
+
{ name: 'lodash', version: '4.17.11', integrity: 'sha512-a' },
|
|
28
|
+
].sort((x, y) => x.version.localeCompare(y.version)));
|
|
29
|
+
});
|
|
30
|
+
(0, node_test_1.test)('parses a v2/v3 flat packages map, including scoped packages and nested node_modules', () => {
|
|
31
|
+
const lockfile = {
|
|
32
|
+
lockfileVersion: 3,
|
|
33
|
+
packages: {
|
|
34
|
+
'': { name: 'my-app', version: '1.0.0' },
|
|
35
|
+
'node_modules/lodash': { version: '4.17.21', integrity: 'sha512-x' },
|
|
36
|
+
'node_modules/@scope/pkg': { version: '2.0.0', integrity: 'sha512-y' },
|
|
37
|
+
'node_modules/a/node_modules/b': { version: '0.1.0' },
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
const result = (0, PackageLockParser_1.parsePackageLock)(lockfile);
|
|
41
|
+
strict_1.default.deepEqual(result.sort((x, y) => x.name.localeCompare(y.name)), [
|
|
42
|
+
{ name: '@scope/pkg', version: '2.0.0', integrity: 'sha512-y' },
|
|
43
|
+
{ name: 'b', version: '0.1.0', integrity: undefined },
|
|
44
|
+
{ name: 'lodash', version: '4.17.21', integrity: 'sha512-x' },
|
|
45
|
+
]);
|
|
46
|
+
});
|
|
47
|
+
(0, node_test_1.test)('deduplicates identical name+version pairs', () => {
|
|
48
|
+
const lockfile = {
|
|
49
|
+
packages: {
|
|
50
|
+
'': {},
|
|
51
|
+
'node_modules/lodash': { version: '4.17.21' },
|
|
52
|
+
'node_modules/a/node_modules/lodash': { version: '4.17.21' },
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
strict_1.default.deepEqual((0, PackageLockParser_1.parsePackageLock)(lockfile), [
|
|
56
|
+
{ name: 'lodash', version: '4.17.21', integrity: undefined },
|
|
57
|
+
]);
|
|
58
|
+
});
|
|
59
|
+
//# sourceMappingURL=PackageLockParser.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PackageLockParser.test.js","sourceRoot":"","sources":["../../../src/ecosystems/npm/PackageLockParser.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ScanOptions, ScanResult } from '../core/types';
|
|
2
|
+
export interface CveScannerApiOptions extends ScanOptions {
|
|
3
|
+
cveDataDownload?: boolean;
|
|
4
|
+
aiEndpoint?: string;
|
|
5
|
+
aiApiKey?: string;
|
|
6
|
+
aiModel?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class CveScannerApi {
|
|
9
|
+
private opts;
|
|
10
|
+
private datasetPath;
|
|
11
|
+
constructor(opts: CveScannerApiOptions, datasetPath?: string);
|
|
12
|
+
scan(): Promise<ScanResult>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CveScannerApi = void 0;
|
|
4
|
+
const path_1 = require("path");
|
|
5
|
+
const AiAssistant_1 = require("../ai/AiAssistant");
|
|
6
|
+
const PromptBuilder_1 = require("../ai/PromptBuilder");
|
|
7
|
+
const ProjectManifestReader_1 = require("../ai/ProjectManifestReader");
|
|
8
|
+
const DatasetLoader_1 = require("../core/DatasetLoader");
|
|
9
|
+
const Scanner_1 = require("../core/Scanner");
|
|
10
|
+
const CveListCache_1 = require("../ecosystems/enrichment/CveListCache");
|
|
11
|
+
const CveOrgDatasetReader_1 = require("../ecosystems/enrichment/CveOrgDatasetReader");
|
|
12
|
+
const GradleDependencyDetector_1 = require("../ecosystems/java/GradleDependencyDetector");
|
|
13
|
+
const MavenDependencyDetector_1 = require("../ecosystems/java/MavenDependencyDetector");
|
|
14
|
+
const NpmDetector_1 = require("../ecosystems/npm/NpmDetector");
|
|
15
|
+
// dist/lib -> dist -> package root -> data/dataset.json
|
|
16
|
+
const DEFAULT_DATASET_PATH = (0, path_1.join)(__dirname, '..', '..', 'data', 'dataset.json');
|
|
17
|
+
class CveScannerApi {
|
|
18
|
+
constructor(opts, datasetPath = DEFAULT_DATASET_PATH) {
|
|
19
|
+
this.opts = opts;
|
|
20
|
+
this.datasetPath = datasetPath;
|
|
21
|
+
}
|
|
22
|
+
async scan() {
|
|
23
|
+
const dataset = DatasetLoader_1.DatasetLoader.fromFile(this.datasetPath);
|
|
24
|
+
const scanner = new Scanner_1.Scanner([
|
|
25
|
+
new MavenDependencyDetector_1.MavenDependencyDetector(),
|
|
26
|
+
new GradleDependencyDetector_1.GradleDependencyDetector(),
|
|
27
|
+
new NpmDetector_1.NpmDetector(),
|
|
28
|
+
], dataset);
|
|
29
|
+
const findings = await scanner.scan(this.opts);
|
|
30
|
+
const cveDataDir = this.opts.cveDataDownload
|
|
31
|
+
? await (0, CveListCache_1.ensureCveListCache)()
|
|
32
|
+
: this.opts.cveDataDir;
|
|
33
|
+
const enricher = new CveOrgDatasetReader_1.CveOrgDatasetReader(cveDataDir);
|
|
34
|
+
for (const finding of findings) {
|
|
35
|
+
finding.enrichment = enricher.read(finding.cve.id);
|
|
36
|
+
}
|
|
37
|
+
const manifestsByEcosystem = new Map();
|
|
38
|
+
for (const finding of findings) {
|
|
39
|
+
const ecosystem = finding.artifact.ecosystem;
|
|
40
|
+
if (!manifestsByEcosystem.has(ecosystem)) {
|
|
41
|
+
manifestsByEcosystem.set(ecosystem, (0, ProjectManifestReader_1.readProjectManifest)(this.opts.scanDir, ecosystem));
|
|
42
|
+
}
|
|
43
|
+
finding.projectManifest = manifestsByEcosystem.get(ecosystem);
|
|
44
|
+
}
|
|
45
|
+
if (this.opts.aiEndpoint && this.opts.aiApiKey && this.opts.aiModel) {
|
|
46
|
+
const assistant = new AiAssistant_1.AiAssistant(this.opts.aiEndpoint, this.opts.aiApiKey, this.opts.aiModel);
|
|
47
|
+
for (const finding of findings) {
|
|
48
|
+
finding.aiAnalysis = await assistant.analyze((0, PromptBuilder_1.buildInvestigationPrompt)(finding));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { datasetGeneratedAt: dataset.generatedAt, findings };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.CveScannerApi = CveScannerApi;
|
|
55
|
+
//# sourceMappingURL=CveScannerApi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CveScannerApi.js","sourceRoot":"","sources":["../../src/lib/CveScannerApi.ts"],"names":[],"mappings":";;;AAAA,+BAA4B;AAC5B,mDAAgD;AAChD,uDAA+D;AAC/D,uEAAkE;AAClE,yDAAsD;AACtD,6CAA0C;AAO1C,wEAA2E;AAC3E,sFAAmF;AACnF,0FAAuF;AACvF,wFAAqF;AACrF,+DAA4D;AAE5D,wDAAwD;AACxD,MAAM,oBAAoB,GAAG,IAAA,WAAI,EAC/B,SAAS,EACT,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,cAAc,CACf,CAAC;AASF;IACE,YACU,IAA0B,EAC1B,WAAW,GAAW,oBAAoB;oBAD1C,IAAI;2BACJ,WAAW;IAClB,CAAC;IAEJ,KAAK,CAAC,IAAI;QACR,MAAM,OAAO,GAAG,6BAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACzD,MAAM,OAAO,GAAG,IAAI,iBAAO,CACzB;YACE,IAAI,iDAAuB,EAAE;YAC7B,IAAI,mDAAwB,EAAE;YAC9B,IAAI,yBAAW,EAAE;SAClB,EACD,OAAO,CACR,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE/C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe;YAC1C,CAAC,CAAC,MAAM,IAAA,iCAAkB,GAAE;YAC5B,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC;QACzB,MAAM,QAAQ,GAAG,IAAI,yCAAmB,CAAC,UAAU,CAAC,CAAC;QACrD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,OAAO,CAAC,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAGjC,CAAC;QACJ,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;YAC7C,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;gBACzC,oBAAoB,CAAC,GAAG,CACtB,SAAS,EACT,IAAA,2CAAmB,EAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAClD,CAAC;YACJ,CAAC;YACD,OAAO,CAAC,eAAe,GAAG,oBAAoB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAChE,CAAC;QAED,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YACpE,MAAM,SAAS,GAAG,IAAI,yBAAW,CAC/B,IAAI,CAAC,IAAI,CAAC,UAAU,EACpB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAClB,IAAI,CAAC,IAAI,CAAC,OAAO,CAClB,CAAC;YACF,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC/B,OAAO,CAAC,UAAU,GAAG,MAAM,SAAS,CAAC,OAAO,CAC1C,IAAA,wCAAwB,EAAC,OAAO,CAAC,CAClC,CAAC;YACJ,CAAC;QACH,CAAC;QAED,OAAO,EAAE,kBAAkB,EAAE,OAAO,CAAC,WAAW,EAAE,QAAQ,EAAE,CAAC;IAC/D,CAAC;CACF"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ScanResult } from '../core/types';
|
|
2
|
+
export type ReportFormat = 'table' | 'json';
|
|
3
|
+
export declare function formatReport(result: ScanResult, format: ReportFormat, scanDir: string): string;
|
|
4
|
+
export declare function computeExitCode(result: ScanResult, failOnScore?: number): number;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatReport = formatReport;
|
|
4
|
+
exports.computeExitCode = computeExitCode;
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
function projectLabel(scanDir, projectRoot) {
|
|
7
|
+
const rel = (0, path_1.relative)(scanDir, projectRoot);
|
|
8
|
+
return rel === '' ? '' : `:${rel}`;
|
|
9
|
+
}
|
|
10
|
+
function formatReport(result, format, scanDir) {
|
|
11
|
+
const { findings, datasetGeneratedAt } = result;
|
|
12
|
+
if (format === 'json') {
|
|
13
|
+
return JSON.stringify({ datasetGeneratedAt, findings }, null, 2);
|
|
14
|
+
}
|
|
15
|
+
const lines = [
|
|
16
|
+
`Vulnerability dataset generated: ${datasetGeneratedAt}`,
|
|
17
|
+
'',
|
|
18
|
+
];
|
|
19
|
+
if (findings.length === 0) {
|
|
20
|
+
lines.push('No known vulnerabilities found.');
|
|
21
|
+
return lines.join('\n');
|
|
22
|
+
}
|
|
23
|
+
for (const finding of findings) {
|
|
24
|
+
const { artifact, cve, enrichment, aiAnalysis } = finding;
|
|
25
|
+
lines.push(`[${artifact.ecosystem}${projectLabel(scanDir, artifact.projectRoot)}] ${artifact.coordinate}@${artifact.version} - ${cve.id} (score ${cve.score})`);
|
|
26
|
+
lines.push(` ${cve.title}`);
|
|
27
|
+
if (enrichment?.description) {
|
|
28
|
+
lines.push(` ${enrichment.description}`);
|
|
29
|
+
}
|
|
30
|
+
if (aiAnalysis) {
|
|
31
|
+
lines.push(' AI analysis:');
|
|
32
|
+
for (const line of aiAnalysis.split('\n'))
|
|
33
|
+
lines.push(` ${line}`);
|
|
34
|
+
}
|
|
35
|
+
lines.push('');
|
|
36
|
+
}
|
|
37
|
+
return lines.join('\n').trimEnd();
|
|
38
|
+
}
|
|
39
|
+
function computeExitCode(result, failOnScore) {
|
|
40
|
+
const { findings } = result;
|
|
41
|
+
if (findings.length === 0)
|
|
42
|
+
return 0;
|
|
43
|
+
if (failOnScore === undefined)
|
|
44
|
+
return 1;
|
|
45
|
+
return findings.some((f) => f.cve.score >= failOnScore) ? 1 : 0;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/lib/report.ts"],"names":[],"mappings":";;;;AAAA,+BAAgC;AAKhC,SAAS,YAAY,CAAC,OAAe,EAAE,WAAmB;IACxD,MAAM,GAAG,GAAG,IAAA,eAAQ,EAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAC3C,OAAO,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC;AACrC,CAAC;AAED,sBACE,MAAkB,EAClB,MAAoB,EACpB,OAAe;IAEf,MAAM,EAAE,QAAQ,EAAE,kBAAkB,EAAE,GAAG,MAAM,CAAC;IAEhD,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,kBAAkB,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,KAAK,GAAa;QACtB,oCAAoC,kBAAkB,EAAE;QACxD,EAAE;KACH,CAAC;IAEF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;QAC9C,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;QAC1D,KAAK,CAAC,IAAI,CACR,IAAI,QAAQ,CAAC,SAAS,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,KAAK,QAAQ,CAAC,UAAU,IAAI,QAAQ,CAAC,OAAO,MAAM,GAAG,CAAC,EAAE,WAAW,GAAG,CAAC,KAAK,GAAG,CACpJ,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;QAC7B,IAAI,UAAU,EAAE,WAAW,EAAE,CAAC;YAC5B,KAAK,CAAC,IAAI,CAAC,KAAK,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;QAC5C,CAAC;QACD,IAAI,UAAU,EAAE,CAAC;YACf,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YAC7B,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;QACvE,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;AACpC,CAAC;AAED,yBACE,MAAkB,EAClB,WAAoB;IAEpB,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IAC5B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACpC,IAAI,WAAW,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC;IACxC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { VulnerableEntry } from '../core/types';
|
|
2
|
+
/**
|
|
3
|
+
* Drops exact-duplicate VulnerableEntry rows (same ecosystem, cpe, version
|
|
4
|
+
* bounds, and CVEs) that can arise when more than one `affected[]` block
|
|
5
|
+
* across the OSV corpus describes the same package+range+CVE combination.
|
|
6
|
+
* Preserves the first occurrence's position. See specs/60-data-pipeline.md.
|
|
7
|
+
*/
|
|
8
|
+
export declare function dedupeEntries(entries: VulnerableEntry[]): VulnerableEntry[];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dedupeEntries = dedupeEntries;
|
|
4
|
+
/**
|
|
5
|
+
* Drops exact-duplicate VulnerableEntry rows (same ecosystem, cpe, version
|
|
6
|
+
* bounds, and CVEs) that can arise when more than one `affected[]` block
|
|
7
|
+
* across the OSV corpus describes the same package+range+CVE combination.
|
|
8
|
+
* Preserves the first occurrence's position. See specs/60-data-pipeline.md.
|
|
9
|
+
*/
|
|
10
|
+
function dedupeEntries(entries) {
|
|
11
|
+
const seen = new Set();
|
|
12
|
+
return entries.filter((entry) => {
|
|
13
|
+
const key = JSON.stringify(entry);
|
|
14
|
+
if (seen.has(key))
|
|
15
|
+
return false;
|
|
16
|
+
seen.add(key);
|
|
17
|
+
return true;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=dedupeEntries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dedupeEntries.js","sourceRoot":"","sources":["../../src/pipeline/dedupeEntries.ts"],"names":[],"mappings":";;;AAEA;;;;;GAKG;AACH,uBAA8B,OAA0B;IACtD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QAChC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
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_test_1 = require("node:test");
|
|
8
|
+
const dedupeEntries_1 = require("./dedupeEntries");
|
|
9
|
+
const entry = {
|
|
10
|
+
ecosystem: 'npm',
|
|
11
|
+
cpe: 'cpe:2.3:a:lodash:lodash:*:*:*:*:*:*:*:*',
|
|
12
|
+
versionEndExcluding: '4.17.12',
|
|
13
|
+
cves: [{ id: 'CVE-1', score: 5, title: 'test' }],
|
|
14
|
+
};
|
|
15
|
+
(0, node_test_1.test)('drops exact duplicates, keeping the first occurrence', () => {
|
|
16
|
+
const result = (0, dedupeEntries_1.dedupeEntries)([entry, { ...entry }, entry]);
|
|
17
|
+
strict_1.default.equal(result.length, 1);
|
|
18
|
+
strict_1.default.deepEqual(result[0], entry);
|
|
19
|
+
});
|
|
20
|
+
(0, node_test_1.test)('keeps entries that differ in any field', () => {
|
|
21
|
+
const other = { ...entry, versionEndExcluding: '4.17.21' };
|
|
22
|
+
strict_1.default.deepEqual((0, dedupeEntries_1.dedupeEntries)([entry, other]), [entry, other]);
|
|
23
|
+
});
|
|
24
|
+
(0, node_test_1.test)('handles an empty list', () => {
|
|
25
|
+
strict_1.default.deepEqual((0, dedupeEntries_1.dedupeEntries)([]), []);
|
|
26
|
+
});
|
|
27
|
+
//# sourceMappingURL=dedupeEntries.test.js.map
|