@cyclonedx/cdxgen 10.8.9 → 10.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cdxgen.js +6 -1
- package/binary.js +1 -1
- package/index.js +48 -1
- package/package.json +3 -3
- package/server.js +5 -2
- package/types/index.d.ts.map +1 -1
- package/types/server.d.ts.map +1 -1
- package/types/utils.d.ts +48 -0
- package/types/utils.d.ts.map +1 -1
- package/types/validator.d.ts.map +1 -1
- package/utils.js +456 -63
- package/utils.test.js +190 -9
- package/validator.js +4 -1
package/bin/cdxgen.js
CHANGED
|
@@ -275,6 +275,11 @@ const args = yargs(hideBin(process.argv))
|
|
|
275
275
|
description:
|
|
276
276
|
"Do not show the donation banner. Set this attribute if you are an active sponsor for OWASP CycloneDX.",
|
|
277
277
|
})
|
|
278
|
+
.option("feature-flags", {
|
|
279
|
+
description: "Experimental feature flags to enable. Advanced users only.",
|
|
280
|
+
hidden: true,
|
|
281
|
+
choices: ["safe-pip-install"],
|
|
282
|
+
})
|
|
278
283
|
.completion("completion", "Generate bash/zsh completion")
|
|
279
284
|
.array("type")
|
|
280
285
|
.array("excludeType")
|
|
@@ -283,6 +288,7 @@ const args = yargs(hideBin(process.argv))
|
|
|
283
288
|
.array("author")
|
|
284
289
|
.array("exclude")
|
|
285
290
|
.array("standard")
|
|
291
|
+
.array("feature-flags")
|
|
286
292
|
.option("auto-compositions", {
|
|
287
293
|
type: "boolean",
|
|
288
294
|
default: true,
|
|
@@ -438,7 +444,6 @@ const applyAdvancedOptions = (options) => {
|
|
|
438
444
|
}
|
|
439
445
|
return options;
|
|
440
446
|
};
|
|
441
|
-
|
|
442
447
|
applyAdvancedOptions(options);
|
|
443
448
|
|
|
444
449
|
/**
|
package/binary.js
CHANGED
package/index.js
CHANGED
|
@@ -63,12 +63,15 @@ import {
|
|
|
63
63
|
getMvnMetadata,
|
|
64
64
|
getNugetMetadata,
|
|
65
65
|
getPipFrozenTree,
|
|
66
|
+
getPipTreeForPackages,
|
|
66
67
|
getPyMetadata,
|
|
67
68
|
getPyModules,
|
|
68
69
|
getSwiftPackageMetadata,
|
|
69
70
|
getTimestamp,
|
|
70
71
|
hasAnyProjectType,
|
|
71
72
|
includeMavenTestScope,
|
|
73
|
+
isFeatureEnabled,
|
|
74
|
+
isPartialTree,
|
|
72
75
|
isValidIriReference,
|
|
73
76
|
parseBazelActionGraph,
|
|
74
77
|
parseBazelSkyframe,
|
|
@@ -2749,6 +2752,10 @@ export async function createPythonBom(path, options) {
|
|
|
2749
2752
|
if (pyProjectMode) {
|
|
2750
2753
|
const tmpParentComponent = parsePyProjectToml(pyProjectFile);
|
|
2751
2754
|
if (tmpParentComponent?.name) {
|
|
2755
|
+
// Bug fix. Version could be missing in pyproject.toml
|
|
2756
|
+
if (!tmpParentComponent.version && parentComponent.version) {
|
|
2757
|
+
tmpParentComponent.version = parentComponent.version;
|
|
2758
|
+
}
|
|
2752
2759
|
parentComponent = tmpParentComponent;
|
|
2753
2760
|
delete parentComponent.homepage;
|
|
2754
2761
|
delete parentComponent.repository;
|
|
@@ -2815,6 +2822,7 @@ export async function createPythonBom(path, options) {
|
|
|
2815
2822
|
};
|
|
2816
2823
|
dependencies.splice(0, 0, pdependencies);
|
|
2817
2824
|
}
|
|
2825
|
+
options.parentComponent = parentComponent;
|
|
2818
2826
|
return buildBomNSData(options, pkgList, "pypi", {
|
|
2819
2827
|
src: path,
|
|
2820
2828
|
filename: poetryFiles.join(", "),
|
|
@@ -2822,7 +2830,7 @@ export async function createPythonBom(path, options) {
|
|
|
2822
2830
|
parentComponent,
|
|
2823
2831
|
formulationList,
|
|
2824
2832
|
});
|
|
2825
|
-
}
|
|
2833
|
+
} // poetryMode
|
|
2826
2834
|
if (metadataFiles?.length) {
|
|
2827
2835
|
// dist-info directories
|
|
2828
2836
|
for (const mf of metadataFiles) {
|
|
@@ -3049,6 +3057,45 @@ export async function createPythonBom(path, options) {
|
|
|
3049
3057
|
pkgList = pkgList.concat(dlist);
|
|
3050
3058
|
}
|
|
3051
3059
|
}
|
|
3060
|
+
// Check and complete the dependency tree
|
|
3061
|
+
if (
|
|
3062
|
+
isFeatureEnabled(options, "safe-pip-install") &&
|
|
3063
|
+
pkgList.length &&
|
|
3064
|
+
isPartialTree(dependencies)
|
|
3065
|
+
) {
|
|
3066
|
+
// Trim the current package list first
|
|
3067
|
+
pkgList = trimComponents(pkgList);
|
|
3068
|
+
console.log(
|
|
3069
|
+
`Attempting to recover the pip dependency tree from ${pkgList.length} packages. Please wait ...`,
|
|
3070
|
+
);
|
|
3071
|
+
const newPkgMap = getPipTreeForPackages(
|
|
3072
|
+
path,
|
|
3073
|
+
pkgList,
|
|
3074
|
+
tempDir,
|
|
3075
|
+
parentComponent,
|
|
3076
|
+
);
|
|
3077
|
+
if (DEBUG_MODE && newPkgMap.failedPkgList.length) {
|
|
3078
|
+
if (newPkgMap.failedPkgList.length < pkgList.length) {
|
|
3079
|
+
console.log(
|
|
3080
|
+
`${newPkgMap.failedPkgList.length} packages failed to install.`,
|
|
3081
|
+
);
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
if (newPkgMap?.pkgList?.length) {
|
|
3085
|
+
pkgList = pkgList.concat(newPkgMap.pkgList);
|
|
3086
|
+
pkgList = trimComponents(pkgList);
|
|
3087
|
+
}
|
|
3088
|
+
if (newPkgMap.dependenciesList) {
|
|
3089
|
+
dependencies = mergeDependencies(
|
|
3090
|
+
dependencies,
|
|
3091
|
+
newPkgMap.dependenciesList,
|
|
3092
|
+
parentComponent,
|
|
3093
|
+
);
|
|
3094
|
+
if (DEBUG_MODE && dependencies.length > 1) {
|
|
3095
|
+
console.log("Recovered", dependencies.length, "dependencies.");
|
|
3096
|
+
}
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3052
3099
|
// Clean up
|
|
3053
3100
|
if (tempDir?.startsWith(tmpdir()) && rmSync) {
|
|
3054
3101
|
rmSync(tempDir, { recursive: true, force: true });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyclonedx/cdxgen",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.9.0",
|
|
4
4
|
"description": "Creates CycloneDX Software Bill of Materials (SBOM) from source or container image",
|
|
5
5
|
"homepage": "http://github.com/cyclonedx/cdxgen",
|
|
6
6
|
"author": "Prabhu Subramanian <prabhu@appthreat.com>",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"find-up": "7.0.0",
|
|
69
69
|
"glob": "^11.0.0",
|
|
70
70
|
"global-agent": "^3.0.0",
|
|
71
|
-
"got": "14.4.
|
|
71
|
+
"got": "14.4.2",
|
|
72
72
|
"iconv-lite": "^0.6.3",
|
|
73
73
|
"js-yaml": "^4.1.0",
|
|
74
74
|
"jws": "^4.0.0",
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"devDependencies": {
|
|
112
112
|
"@biomejs/biome": "1.8.3",
|
|
113
113
|
"jest": "^29.7.0",
|
|
114
|
-
"typescript": "^5.5.
|
|
114
|
+
"typescript": "^5.5.4"
|
|
115
115
|
},
|
|
116
116
|
"scripts": {
|
|
117
117
|
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --inject-globals false docker.test.js utils.test.js display.test.js postgen.test.js",
|
package/server.js
CHANGED
|
@@ -178,14 +178,17 @@ const start = (options) => {
|
|
|
178
178
|
let bomNSData = (await createBom(srcDir, reqOptions)) || {};
|
|
179
179
|
bomNSData = postProcess(bomNSData, reqOptions);
|
|
180
180
|
if (reqOptions.serverUrl && reqOptions.apiKey) {
|
|
181
|
-
console.log(
|
|
181
|
+
console.log(
|
|
182
|
+
`Publishing SBOM ${reqOptions.projectName} to Dependency Track`,
|
|
183
|
+
reqOptions.serverUrl,
|
|
184
|
+
);
|
|
182
185
|
const response = await submitBom(reqOptions, bomNSData.bomJson);
|
|
183
186
|
const errorMessages = response?.errors;
|
|
184
187
|
if (errorMessages) {
|
|
185
188
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
186
189
|
return res.end(
|
|
187
190
|
JSON.stringify({
|
|
188
|
-
error:
|
|
191
|
+
error: `Unable to submit the SBOM ${reqOptions.projectName} to the Dependency Track server ${reqOptions.serverUrl}`,
|
|
189
192
|
details: errorMessages,
|
|
190
193
|
}),
|
|
191
194
|
);
|
package/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.js"],"names":[],"mappings":"AAyvBA;;;;;;;;GAQG;AACH,gFAFW,MAAM,SAchB;AAyUD;;;;;;;GAOG;AACH,mCALW,MAAM,qBAiEhB;AAED;;;;;GAKG;AACH,uCAHW,MAAM;;;;EAKhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM;;;;EAkBhB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,8BAi/BhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM,8BA2chB;AAED;;;;;GAKG;AACH,sCAHW,MAAM,8BAkahB;AAED;;;;;GAKG;AACH,kCAHW,MAAM,8BAkUhB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,8BAqIhB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,8BAiDhB;AAED;;;;;GAKG;AACH,mCAHW,MAAM,qBA+KhB;AAED;;;;;GAKG;AACH,uCAHW,MAAM,qBAsHhB;AAED;;;;;GAKG;AACH,uCAHW,MAAM,qBA2BhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM,qBA2BhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM,qBA2BhB;AAED;;;;;GAKG;AACH,0CAHW,MAAM,qBAuBhB;AAED;;;;;GAKG;AACH,kCAHW,MAAM,8BAqDhB;AAED;;;;;GAKG;AACH,uCAHW,MAAM,8BA4ChB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,qBA2BhB;AAED;;;;;GAKG;AACH,qCAHW,MAAM,8BAwFhB;AAED;;;;;GAKG;AACH,iDAHW,MAAM,qBAiUhB;AAED;;;;;GAKG;AACH,mCAHW,MAAM,qBAwJhB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,8BAmFhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM,8BAyWhB;AAED;;;;;GAKG;AACH,2CAHW,MAAM;;;;;;;;;;;;;;;;;;;;GAoChB;AAED;;;;;;;;KA+DC;AAED;;;;;;GAMG;AACH,yDA2CC;AAED;;;;;;;;;GASG;AACH,2GA6BC;AAED;;;;;GAKG;AACH,0CAHW,MAAM,EAAE,8BAmclB;AAED;;;;;GAKG;AACH,iCAHW,MAAM,8BAiUhB;AAED;;;;;GAKG;AACH,gCAHW,MAAM,qBAsOhB;AAED;;;;;;GAMG;AACH,wDAFY,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,SAAS,CAAC,CA2FxE"}
|
package/types/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../server.js"],"names":[],"mappings":"AAuIA,yDAKC;AAED,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../server.js"],"names":[],"mappings":"AAuIA,yDAKC;AAED,0CAuEC"}
|
package/types/utils.d.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Method to check if a given feature flag is enabled.
|
|
3
|
+
*
|
|
4
|
+
* @param {Object} cliOptions CLI options
|
|
5
|
+
* @param {String} feature Feature flag
|
|
6
|
+
*
|
|
7
|
+
* @returns {Boolean} True if the feature is enabled
|
|
8
|
+
*/
|
|
9
|
+
export function isFeatureEnabled(cliOptions: any, feature: string): boolean;
|
|
1
10
|
/**
|
|
2
11
|
* Method to check if the given project types are allowed by checking against include and exclude types passed from the CLI arguments.
|
|
3
12
|
*
|
|
@@ -607,6 +616,7 @@ export function parseCabalData(cabalData: any): any[];
|
|
|
607
616
|
export function parseMixLockData(mixData: any): any[];
|
|
608
617
|
export function parseGitHubWorkflowData(ghwData: any): any[];
|
|
609
618
|
export function parseCloudBuildData(cbwData: any): any[];
|
|
619
|
+
export function mapConanPkgRefToPurlStringAndNameAndVersion(conanPkgRef: any): any[];
|
|
610
620
|
export function parseConanLockData(conanLockData: any): any[];
|
|
611
621
|
export function parseConanData(conanData: any): any[];
|
|
612
622
|
export function parseLeiningenData(leinData: any): any[];
|
|
@@ -1076,6 +1086,8 @@ export function getPipFrozenTree(basePath: string, reqOrSetupFile: string, tempV
|
|
|
1076
1086
|
rootList: {
|
|
1077
1087
|
name: any;
|
|
1078
1088
|
version: any;
|
|
1089
|
+
purl: string;
|
|
1090
|
+
"bom-ref": string;
|
|
1079
1091
|
}[];
|
|
1080
1092
|
dependenciesList: {
|
|
1081
1093
|
ref: string;
|
|
@@ -1083,6 +1095,35 @@ export function getPipFrozenTree(basePath: string, reqOrSetupFile: string, tempV
|
|
|
1083
1095
|
}[];
|
|
1084
1096
|
frozen: boolean;
|
|
1085
1097
|
};
|
|
1098
|
+
/**
|
|
1099
|
+
* The problem: pip installation can fail for a number of reasons such as missing OS dependencies and devel packages.
|
|
1100
|
+
* When it fails, we don't get any dependency tree. As a workaroud, this method would attempt to install one package at a time to the same virtual environment and then attempts to obtain a dependency tree.
|
|
1101
|
+
* Such a tree could be incorrect or quite approximate, but some users might still find it useful to know the names of the indirect dependencies.
|
|
1102
|
+
*
|
|
1103
|
+
* @param {string} basePath Base path
|
|
1104
|
+
* @param {Array} pkgList Existing package list
|
|
1105
|
+
* @param {string} tempVenvDir Temp venv dir
|
|
1106
|
+
* @param {Object} parentComponent Parent component
|
|
1107
|
+
*
|
|
1108
|
+
* @returns List of packages from the virtual env
|
|
1109
|
+
*/
|
|
1110
|
+
export function getPipTreeForPackages(basePath: string, pkgList: any[], tempVenvDir: string, parentComponent: any): {
|
|
1111
|
+
failedPkgList?: undefined;
|
|
1112
|
+
rootList?: undefined;
|
|
1113
|
+
dependenciesList?: undefined;
|
|
1114
|
+
} | {
|
|
1115
|
+
failedPkgList: any[];
|
|
1116
|
+
rootList: {
|
|
1117
|
+
name: any;
|
|
1118
|
+
version: any;
|
|
1119
|
+
purl: string;
|
|
1120
|
+
"bom-ref": string;
|
|
1121
|
+
}[];
|
|
1122
|
+
dependenciesList: {
|
|
1123
|
+
ref: string;
|
|
1124
|
+
dependsOn: any;
|
|
1125
|
+
}[];
|
|
1126
|
+
};
|
|
1086
1127
|
export function parsePackageJsonName(name: any): {
|
|
1087
1128
|
scope: any;
|
|
1088
1129
|
fullName: string;
|
|
@@ -1166,6 +1207,13 @@ export function parseMakeDFile(dfile: string): any;
|
|
|
1166
1207
|
*
|
|
1167
1208
|
*/
|
|
1168
1209
|
export function isValidIriReference(iri: string): boolean;
|
|
1210
|
+
/**
|
|
1211
|
+
* Method to check if a given dependency tree is partial or not.
|
|
1212
|
+
*
|
|
1213
|
+
* @param {Array} dependencies List of dependencies
|
|
1214
|
+
* @returns {Boolean} True if the dependency tree lacks any non-root parents without children. False otherwise.
|
|
1215
|
+
*/
|
|
1216
|
+
export function isPartialTree(dependencies: any[]): boolean;
|
|
1169
1217
|
export const dirNameStr: string;
|
|
1170
1218
|
export const isWin: boolean;
|
|
1171
1219
|
export const isMac: boolean;
|
package/types/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../utils.js"],"names":[],"mappings":"AAsSA;;;;;;GAMG;AACH,mGAkDC;AAgBD;;;;;GAKG;AACH,qCAHW,MAAM,WACN,MAAM,0BAqBhB;AAED;;;;;;GAMG;AACH,+CAJW,MAAM,WACN,MAAM,+BAoBhB;AAYD;;;;GAIG;AACH,gCAFa,MAAM,CAIlB;AAED;;;;;;IAMI;AACJ,iDAJW,MAAM,GACJ,OAAO,CAiBnB;AAED;;;;;;;;;GASG;AACH,iEA2BC;AAED;;;;;GAKG;AACH,6CAqDC;AAED;;;;;;GAMG;AACH,sEA0DC;AAED;;;;GAIG;AACH,4EAoCC;AAED;;;GAGG;AACH;;EAUC;AAED,sEA0BC;AAED;;;;GAIG;AACH,+DA4CC;AAED;;;;;GAKG;AACH,0CAHW,MAAM,WACN,OAAO,kBAkFjB;AAED;;;;;GAKG;AACH,0CAHW,MAAM,YACN,MAAM;;;GAqVhB;AAED;;;;;;;GAOG;AACH,6CAFW,MAAM,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../utils.js"],"names":[],"mappings":"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"}
|
package/types/validator.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../validator.js"],"names":[],"mappings":"AAmBO,qCAFI,MAAM,WA6ChB;AAOM,0CAFI,MAAM,WAiDhB;AAOM,uCAFI,MAAM,WAgEhB;AA6BM,sCAFI,MAAM,
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../validator.js"],"names":[],"mappings":"AAmBO,qCAFI,MAAM,WA6ChB;AAOM,0CAFI,MAAM,WAiDhB;AAOM,uCAFI,MAAM,WAgEhB;AA6BM,sCAFI,MAAM,WA8ChB"}
|
package/utils.js
CHANGED
|
@@ -292,6 +292,36 @@ export const PROJECT_TYPE_ALIASES = {
|
|
|
292
292
|
oci: ["docker", "oci", "container", "podman"],
|
|
293
293
|
};
|
|
294
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Method to check if a given feature flag is enabled.
|
|
297
|
+
*
|
|
298
|
+
* @param {Object} cliOptions CLI options
|
|
299
|
+
* @param {String} feature Feature flag
|
|
300
|
+
*
|
|
301
|
+
* @returns {Boolean} True if the feature is enabled
|
|
302
|
+
*/
|
|
303
|
+
export function isFeatureEnabled(cliOptions, feature) {
|
|
304
|
+
if (cliOptions?.featureFlags?.includes(feature)) {
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
if (
|
|
308
|
+
process.env[feature.toUpperCase()] &&
|
|
309
|
+
["true", "1"].includes(process.env[feature.toUpperCase()])
|
|
310
|
+
) {
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
// Retry by replacing hyphens with underscore
|
|
314
|
+
if (
|
|
315
|
+
process.env[feature.replaceAll("-", "_").toUpperCase()] &&
|
|
316
|
+
["true", "1"].includes(
|
|
317
|
+
process.env[feature.replaceAll("-", "_").toUpperCase()],
|
|
318
|
+
)
|
|
319
|
+
) {
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
|
|
295
325
|
/**
|
|
296
326
|
* Method to check if the given project types are allowed by checking against include and exclude types passed from the CLI arguments.
|
|
297
327
|
*
|
|
@@ -1252,8 +1282,11 @@ export function yarnLockToIdentMap(lockData) {
|
|
|
1252
1282
|
}
|
|
1253
1283
|
}
|
|
1254
1284
|
}
|
|
1255
|
-
} else if (
|
|
1256
|
-
|
|
1285
|
+
} else if (
|
|
1286
|
+
(l.startsWith(" version") || l.startsWith(' "version')) &&
|
|
1287
|
+
currentIdents.length
|
|
1288
|
+
) {
|
|
1289
|
+
const tmpA = l.replace(/"/g, "").split(" ");
|
|
1257
1290
|
const version = tmpA[tmpA.length - 1].trim();
|
|
1258
1291
|
for (const id of currentIdents) {
|
|
1259
1292
|
identMap[id] = version;
|
|
@@ -1418,9 +1451,14 @@ export async function parseYarnLock(yarnLockFile) {
|
|
|
1418
1451
|
} else if (
|
|
1419
1452
|
name !== "" &&
|
|
1420
1453
|
(l.startsWith(" dependencies:") ||
|
|
1421
|
-
l.startsWith("
|
|
1454
|
+
l.startsWith(' "dependencies:') ||
|
|
1455
|
+
l.startsWith(" optionalDependencies:") ||
|
|
1456
|
+
l.startsWith(' "optionalDependencies:'))
|
|
1422
1457
|
) {
|
|
1423
|
-
if (
|
|
1458
|
+
if (
|
|
1459
|
+
l.startsWith(" dependencies:") ||
|
|
1460
|
+
l.startsWith(' "dependencies:')
|
|
1461
|
+
) {
|
|
1424
1462
|
depsMode = true;
|
|
1425
1463
|
optionalDepsMode = false;
|
|
1426
1464
|
} else {
|
|
@@ -1432,9 +1470,17 @@ export async function parseYarnLock(yarnLockFile) {
|
|
|
1432
1470
|
// We need the resolved version from identMap
|
|
1433
1471
|
// Deal with values with space within the quotes. Eg: minimatch "2 || 3"
|
|
1434
1472
|
// vinyl-sourcemaps-apply ">=0.1.1 <0.2.0-0"
|
|
1435
|
-
|
|
1473
|
+
l = l.trim();
|
|
1474
|
+
let splitPattern = ' "';
|
|
1475
|
+
// yarn v7 has a different split pattern
|
|
1476
|
+
if (l.includes('": ')) {
|
|
1477
|
+
splitPattern = '": ';
|
|
1478
|
+
} else if (l.includes(": ")) {
|
|
1479
|
+
splitPattern = ": ";
|
|
1480
|
+
}
|
|
1481
|
+
const tmpA = l.trim().split(splitPattern);
|
|
1436
1482
|
if (tmpA && tmpA.length === 2) {
|
|
1437
|
-
let dgroupname = tmpA[0];
|
|
1483
|
+
let dgroupname = tmpA[0].replace(/"/g, "");
|
|
1438
1484
|
if (dgroupname.endsWith(":")) {
|
|
1439
1485
|
dgroupname = dgroupname.substring(0, dgroupname.length - 1);
|
|
1440
1486
|
}
|
|
@@ -1459,7 +1505,7 @@ export async function parseYarnLock(yarnLockFile) {
|
|
|
1459
1505
|
depsMode = false;
|
|
1460
1506
|
optionalDepsMode = false;
|
|
1461
1507
|
}
|
|
1462
|
-
l = l.trim();
|
|
1508
|
+
l = l.replace(/"/g, "").trim();
|
|
1463
1509
|
const parts = l.split(" ");
|
|
1464
1510
|
if (l.startsWith("version")) {
|
|
1465
1511
|
version = parts[1].replace(/"/g, "");
|
|
@@ -2764,6 +2810,13 @@ export function parseGradleProperties(rawOutput) {
|
|
|
2764
2810
|
* @returns {string} The combined output for all subprojects of the Gradle properties task
|
|
2765
2811
|
*/
|
|
2766
2812
|
export function executeParallelGradleProperties(dir, rootPath, allProjectsStr) {
|
|
2813
|
+
const defaultProps = {
|
|
2814
|
+
rootProject: subProject,
|
|
2815
|
+
projects: [],
|
|
2816
|
+
metadata: {
|
|
2817
|
+
version: "latest",
|
|
2818
|
+
},
|
|
2819
|
+
};
|
|
2767
2820
|
let parallelPropTaskArgs = ["properties"];
|
|
2768
2821
|
for (const spstr of allProjectsStr) {
|
|
2769
2822
|
parallelPropTaskArgs.push(`${spstr}:properties`);
|
|
@@ -6481,6 +6534,170 @@ export function parseCloudBuildData(cbwData) {
|
|
|
6481
6534
|
return pkgList;
|
|
6482
6535
|
}
|
|
6483
6536
|
|
|
6537
|
+
function createConanPurlString(name, version, user, channel, rrev, prev) {
|
|
6538
|
+
// https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst#conan
|
|
6539
|
+
|
|
6540
|
+
const qualifiers = {};
|
|
6541
|
+
|
|
6542
|
+
if (user) qualifiers["user"] = user;
|
|
6543
|
+
if (channel) qualifiers["channel"] = channel;
|
|
6544
|
+
if (rrev) qualifiers["rrev"] = rrev;
|
|
6545
|
+
if (prev) qualifiers["prev"] = prev;
|
|
6546
|
+
|
|
6547
|
+
return new PackageURL(
|
|
6548
|
+
"conan",
|
|
6549
|
+
"",
|
|
6550
|
+
name,
|
|
6551
|
+
version,
|
|
6552
|
+
Object.keys(qualifiers).length ? qualifiers : null,
|
|
6553
|
+
null,
|
|
6554
|
+
).toString();
|
|
6555
|
+
}
|
|
6556
|
+
|
|
6557
|
+
function untilFirst(separator, inputStr) {
|
|
6558
|
+
// untilFirst("/", "a/b") -> ["/", "a", "b"]
|
|
6559
|
+
// untilFirst("/", "abc") -> ["/", "abc", null]
|
|
6560
|
+
|
|
6561
|
+
if (!inputStr || inputStr.length === 0) {
|
|
6562
|
+
return [null, null, null];
|
|
6563
|
+
}
|
|
6564
|
+
|
|
6565
|
+
const separatorIndex = inputStr.search(separator);
|
|
6566
|
+
if (separatorIndex === -1) {
|
|
6567
|
+
return ["", inputStr, null];
|
|
6568
|
+
}
|
|
6569
|
+
return [
|
|
6570
|
+
inputStr[separatorIndex],
|
|
6571
|
+
inputStr.substring(0, separatorIndex),
|
|
6572
|
+
inputStr.substring(separatorIndex + 1),
|
|
6573
|
+
];
|
|
6574
|
+
}
|
|
6575
|
+
|
|
6576
|
+
export function mapConanPkgRefToPurlStringAndNameAndVersion(conanPkgRef) {
|
|
6577
|
+
// A full Conan package reference may be composed of the following segments:
|
|
6578
|
+
// conanPkgRef = "name/version@user/channel#recipe_revision:package_id#package_revision"
|
|
6579
|
+
// See also https://docs.conan.io/1/cheatsheet.html#package-terminology
|
|
6580
|
+
|
|
6581
|
+
// The components 'package_id' and 'package_revision' do not appear in any files processed by cdxgen.
|
|
6582
|
+
// The components 'user' and 'channel' are not mandatory.
|
|
6583
|
+
// 'name/version' is a valid Conan package reference, so is 'name/version@user/channel' or 'name/version@user/channel#recipe_revision'.
|
|
6584
|
+
// pURL for Conan does not recognize 'package_id'.
|
|
6585
|
+
|
|
6586
|
+
const UNABLE_TO_PARSE_CONAN_PKG_REF = [null, null, null];
|
|
6587
|
+
|
|
6588
|
+
if (!conanPkgRef) {
|
|
6589
|
+
if (DEBUG_MODE)
|
|
6590
|
+
console.warn(
|
|
6591
|
+
`Could not parse Conan package reference '${conanPkgRef}', input does not seem valid.`,
|
|
6592
|
+
);
|
|
6593
|
+
|
|
6594
|
+
return UNABLE_TO_PARSE_CONAN_PKG_REF;
|
|
6595
|
+
}
|
|
6596
|
+
|
|
6597
|
+
const separatorRegex = /[@#:\/]/;
|
|
6598
|
+
|
|
6599
|
+
const info = {
|
|
6600
|
+
name: null,
|
|
6601
|
+
version: null,
|
|
6602
|
+
user: null,
|
|
6603
|
+
channel: null,
|
|
6604
|
+
recipe_revision: null,
|
|
6605
|
+
package_id: null,
|
|
6606
|
+
package_revision: null,
|
|
6607
|
+
phase_history: [],
|
|
6608
|
+
};
|
|
6609
|
+
|
|
6610
|
+
const transitions = {
|
|
6611
|
+
["name"]: {
|
|
6612
|
+
"/": "version",
|
|
6613
|
+
"#": "recipe_revision",
|
|
6614
|
+
"": "end",
|
|
6615
|
+
},
|
|
6616
|
+
["version"]: {
|
|
6617
|
+
"@": "user",
|
|
6618
|
+
"#": "recipe_revision",
|
|
6619
|
+
"": "end",
|
|
6620
|
+
},
|
|
6621
|
+
["user"]: {
|
|
6622
|
+
"/": "channel",
|
|
6623
|
+
},
|
|
6624
|
+
["channel"]: {
|
|
6625
|
+
"#": "recipe_revision",
|
|
6626
|
+
"": "end",
|
|
6627
|
+
},
|
|
6628
|
+
["recipe_revision"]: {
|
|
6629
|
+
":": "package_id",
|
|
6630
|
+
"": "end",
|
|
6631
|
+
},
|
|
6632
|
+
["package_id"]: {
|
|
6633
|
+
"#": "package_revision",
|
|
6634
|
+
},
|
|
6635
|
+
["package_revision"]: {
|
|
6636
|
+
"": "end",
|
|
6637
|
+
},
|
|
6638
|
+
};
|
|
6639
|
+
|
|
6640
|
+
let phase = "name";
|
|
6641
|
+
let remainder = conanPkgRef;
|
|
6642
|
+
let separator;
|
|
6643
|
+
let item;
|
|
6644
|
+
|
|
6645
|
+
while (remainder) {
|
|
6646
|
+
[separator, item, remainder] = untilFirst(separatorRegex, remainder);
|
|
6647
|
+
|
|
6648
|
+
if (!item) {
|
|
6649
|
+
if (DEBUG_MODE)
|
|
6650
|
+
console.warn(
|
|
6651
|
+
`Could not parse Conan package reference '${conanPkgRef}', empty item in phase '${phase}', separator=${separator}, remainder=${remainder}, info=${JSON.stringify(info)}`,
|
|
6652
|
+
);
|
|
6653
|
+
return UNABLE_TO_PARSE_CONAN_PKG_REF;
|
|
6654
|
+
}
|
|
6655
|
+
|
|
6656
|
+
info[phase] = item;
|
|
6657
|
+
info.phase_history.push(phase);
|
|
6658
|
+
|
|
6659
|
+
if (!(phase in transitions)) {
|
|
6660
|
+
if (DEBUG_MODE)
|
|
6661
|
+
console.warn(
|
|
6662
|
+
`Could not parse Conan package reference '${conanPkgRef}', no transition from '${phase}', separator=${separator}, item=${item}, remainder=${remainder}, info=${JSON.stringify(info)}`,
|
|
6663
|
+
);
|
|
6664
|
+
return UNABLE_TO_PARSE_CONAN_PKG_REF;
|
|
6665
|
+
}
|
|
6666
|
+
|
|
6667
|
+
const possibleTransitions = transitions[phase];
|
|
6668
|
+
if (!(separator in possibleTransitions)) {
|
|
6669
|
+
if (DEBUG_MODE)
|
|
6670
|
+
console.warn(
|
|
6671
|
+
`Could not parse Conan package reference '${conanPkgRef}', transition '${separator}' not allowed from '${phase}', item=${item}, remainder=${remainder}, info=${JSON.stringify(info)}`,
|
|
6672
|
+
);
|
|
6673
|
+
return UNABLE_TO_PARSE_CONAN_PKG_REF;
|
|
6674
|
+
}
|
|
6675
|
+
|
|
6676
|
+
phase = possibleTransitions[separator];
|
|
6677
|
+
}
|
|
6678
|
+
|
|
6679
|
+
if (phase !== "end") {
|
|
6680
|
+
if (DEBUG_MODE)
|
|
6681
|
+
console.warn(
|
|
6682
|
+
`Could not parse Conan package reference '${conanPkgRef}', end of input string reached unexpectedly in phase '${phase}', info=${JSON.stringify(info)}.`,
|
|
6683
|
+
);
|
|
6684
|
+
return UNABLE_TO_PARSE_CONAN_PKG_REF;
|
|
6685
|
+
}
|
|
6686
|
+
|
|
6687
|
+
if (!info.version) info.version = "latest";
|
|
6688
|
+
|
|
6689
|
+
const purl = createConanPurlString(
|
|
6690
|
+
info.name,
|
|
6691
|
+
info.version,
|
|
6692
|
+
info.user,
|
|
6693
|
+
info.channel,
|
|
6694
|
+
info.recipe_revision,
|
|
6695
|
+
info.package_revision,
|
|
6696
|
+
);
|
|
6697
|
+
|
|
6698
|
+
return [purl, info.name, info.version];
|
|
6699
|
+
}
|
|
6700
|
+
|
|
6484
6701
|
export function parseConanLockData(conanLockData) {
|
|
6485
6702
|
const pkgList = [];
|
|
6486
6703
|
if (!conanLockData) {
|
|
@@ -6493,27 +6710,15 @@ export function parseConanLockData(conanLockData) {
|
|
|
6493
6710
|
const nodes = graphLock.graph_lock.nodes;
|
|
6494
6711
|
for (const nk of Object.keys(nodes)) {
|
|
6495
6712
|
if (nodes[nk].ref) {
|
|
6496
|
-
const
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
version = version.split("@")[0];
|
|
6501
|
-
} else if (tmpA[1].includes("#")) {
|
|
6502
|
-
version = version.split("#")[0];
|
|
6503
|
-
}
|
|
6504
|
-
const purlString = new PackageURL(
|
|
6505
|
-
"conan",
|
|
6506
|
-
"",
|
|
6507
|
-
tmpA[0],
|
|
6508
|
-
version,
|
|
6509
|
-
null,
|
|
6510
|
-
null,
|
|
6511
|
-
).toString();
|
|
6713
|
+
const [purl, name, version] = mapConanPkgRefToPurlStringAndNameAndVersion(
|
|
6714
|
+
nodes[nk].ref,
|
|
6715
|
+
);
|
|
6716
|
+
if (purl !== null) {
|
|
6512
6717
|
pkgList.push({
|
|
6513
|
-
name
|
|
6718
|
+
name,
|
|
6514
6719
|
version,
|
|
6515
|
-
purl
|
|
6516
|
-
"bom-ref": decodeURIComponent(
|
|
6720
|
+
purl,
|
|
6721
|
+
"bom-ref": decodeURIComponent(purl),
|
|
6517
6722
|
});
|
|
6518
6723
|
}
|
|
6519
6724
|
}
|
|
@@ -6535,39 +6740,19 @@ export function parseConanData(conanData) {
|
|
|
6535
6740
|
if (l.includes("[requires]")) {
|
|
6536
6741
|
scope = "required";
|
|
6537
6742
|
}
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
if (tmpA[1].includes("#")) {
|
|
6547
|
-
const tmpB = version.split("#");
|
|
6548
|
-
version = tmpB[0];
|
|
6549
|
-
qualifiers = { revision: tmpB[1] };
|
|
6550
|
-
}
|
|
6551
|
-
if (l.includes("#")) {
|
|
6552
|
-
const tmpB = l.split("#");
|
|
6553
|
-
qualifiers = { revision: tmpB[1] };
|
|
6554
|
-
}
|
|
6555
|
-
if (tmpA[1].includes("@")) {
|
|
6556
|
-
version = version.split("@")[0];
|
|
6557
|
-
}
|
|
6558
|
-
const purlString = new PackageURL(
|
|
6559
|
-
"conan",
|
|
6560
|
-
"",
|
|
6561
|
-
tmpA[0],
|
|
6562
|
-
version,
|
|
6563
|
-
qualifiers,
|
|
6564
|
-
null,
|
|
6565
|
-
).toString();
|
|
6743
|
+
|
|
6744
|
+
// The line must start with sequence non-whitespace characters, followed by a slash,
|
|
6745
|
+
// followed by at least one more non-whitespace character.
|
|
6746
|
+
// Provides a heuristic for locating Conan package references inside conanfile.txt files.
|
|
6747
|
+
if (l.match(/^[^\s\/]+\/\S+/)) {
|
|
6748
|
+
const [purl, name, version] =
|
|
6749
|
+
mapConanPkgRefToPurlStringAndNameAndVersion(l);
|
|
6750
|
+
if (purl !== null) {
|
|
6566
6751
|
pkgList.push({
|
|
6567
|
-
name
|
|
6752
|
+
name,
|
|
6568
6753
|
version,
|
|
6569
|
-
purl
|
|
6570
|
-
"bom-ref": decodeURIComponent(
|
|
6754
|
+
purl,
|
|
6755
|
+
"bom-ref": decodeURIComponent(purl),
|
|
6571
6756
|
scope,
|
|
6572
6757
|
});
|
|
6573
6758
|
}
|
|
@@ -9514,18 +9699,20 @@ function flattenDeps(dependenciesMap, pkgList, reqOrSetupFile, t) {
|
|
|
9514
9699
|
null,
|
|
9515
9700
|
null,
|
|
9516
9701
|
).toString();
|
|
9517
|
-
|
|
9702
|
+
const apkg = {
|
|
9518
9703
|
name: d.name,
|
|
9519
9704
|
version: d.version,
|
|
9520
9705
|
purl: purlString,
|
|
9521
9706
|
"bom-ref": decodeURIComponent(purlString),
|
|
9522
|
-
|
|
9707
|
+
};
|
|
9708
|
+
if (reqOrSetupFile) {
|
|
9709
|
+
apkg.properties = [
|
|
9523
9710
|
{
|
|
9524
9711
|
name: "SrcFile",
|
|
9525
9712
|
value: reqOrSetupFile,
|
|
9526
9713
|
},
|
|
9527
|
-
]
|
|
9528
|
-
evidence
|
|
9714
|
+
];
|
|
9715
|
+
apkg.evidence = {
|
|
9529
9716
|
identity: {
|
|
9530
9717
|
field: "purl",
|
|
9531
9718
|
confidence: 0.8,
|
|
@@ -9537,8 +9724,9 @@ function flattenDeps(dependenciesMap, pkgList, reqOrSetupFile, t) {
|
|
|
9537
9724
|
},
|
|
9538
9725
|
],
|
|
9539
9726
|
},
|
|
9540
|
-
}
|
|
9541
|
-
}
|
|
9727
|
+
};
|
|
9728
|
+
}
|
|
9729
|
+
pkgList.push(apkg);
|
|
9542
9730
|
// Recurse and flatten
|
|
9543
9731
|
if (d.dependencies && d.dependencies) {
|
|
9544
9732
|
flattenDeps(dependenciesMap, pkgList, reqOrSetupFile, d);
|
|
@@ -9914,6 +10102,8 @@ export function getPipFrozenTree(
|
|
|
9914
10102
|
rootList.push({
|
|
9915
10103
|
name,
|
|
9916
10104
|
version,
|
|
10105
|
+
purl: purlString,
|
|
10106
|
+
"bom-ref": decodeURIComponent(purlString),
|
|
9917
10107
|
});
|
|
9918
10108
|
flattenDeps(dependenciesMap, pkgList, reqOrSetupFile, t);
|
|
9919
10109
|
} else {
|
|
@@ -9933,6 +10123,190 @@ export function getPipFrozenTree(
|
|
|
9933
10123
|
};
|
|
9934
10124
|
}
|
|
9935
10125
|
|
|
10126
|
+
/**
|
|
10127
|
+
* The problem: pip installation can fail for a number of reasons such as missing OS dependencies and devel packages.
|
|
10128
|
+
* When it fails, we don't get any dependency tree. As a workaroud, this method would attempt to install one package at a time to the same virtual environment and then attempts to obtain a dependency tree.
|
|
10129
|
+
* Such a tree could be incorrect or quite approximate, but some users might still find it useful to know the names of the indirect dependencies.
|
|
10130
|
+
*
|
|
10131
|
+
* @param {string} basePath Base path
|
|
10132
|
+
* @param {Array} pkgList Existing package list
|
|
10133
|
+
* @param {string} tempVenvDir Temp venv dir
|
|
10134
|
+
* @param {Object} parentComponent Parent component
|
|
10135
|
+
*
|
|
10136
|
+
* @returns List of packages from the virtual env
|
|
10137
|
+
*/
|
|
10138
|
+
export function getPipTreeForPackages(
|
|
10139
|
+
basePath,
|
|
10140
|
+
pkgList,
|
|
10141
|
+
tempVenvDir,
|
|
10142
|
+
parentComponent,
|
|
10143
|
+
) {
|
|
10144
|
+
const failedPkgList = [];
|
|
10145
|
+
const rootList = [];
|
|
10146
|
+
const dependenciesList = [];
|
|
10147
|
+
let result = undefined;
|
|
10148
|
+
const env = {
|
|
10149
|
+
...process.env,
|
|
10150
|
+
};
|
|
10151
|
+
if (!process.env.VIRTUAL_ENV && !process.env.CONDA_PREFIX) {
|
|
10152
|
+
// Create a virtual environment
|
|
10153
|
+
result = spawnSync(PYTHON_CMD, ["-m", "venv", tempVenvDir], {
|
|
10154
|
+
encoding: "utf-8",
|
|
10155
|
+
shell: isWin,
|
|
10156
|
+
});
|
|
10157
|
+
if (result.status !== 0 || result.error) {
|
|
10158
|
+
console.log("Virtual env creation has failed. Unable to continue.");
|
|
10159
|
+
return {};
|
|
10160
|
+
}
|
|
10161
|
+
if (DEBUG_MODE) {
|
|
10162
|
+
console.log("Using the virtual environment", tempVenvDir);
|
|
10163
|
+
}
|
|
10164
|
+
env.VIRTUAL_ENV = tempVenvDir;
|
|
10165
|
+
env.PATH = `${join(
|
|
10166
|
+
tempVenvDir,
|
|
10167
|
+
platform() === "win32" ? "Scripts" : "bin",
|
|
10168
|
+
)}${_delimiter}${process.env.PATH || ""}`;
|
|
10169
|
+
// When cdxgen is invoked with the container image, we seem to be including unnecessary packages from the image.
|
|
10170
|
+
// This workaround, unsets PYTHONPATH to suppress the pre-installed packages
|
|
10171
|
+
if (
|
|
10172
|
+
env?.PYTHONPATH === "/opt/pypi" &&
|
|
10173
|
+
env?.CDXGEN_IN_CONTAINER === "true"
|
|
10174
|
+
) {
|
|
10175
|
+
env.PYTHONPATH = undefined;
|
|
10176
|
+
}
|
|
10177
|
+
}
|
|
10178
|
+
const python_cmd_for_tree = get_python_command_from_env(env);
|
|
10179
|
+
let pipInstallArgs = ["-m", "pip", "install", "--disable-pip-version-check"];
|
|
10180
|
+
// Support for passing additional arguments to pip
|
|
10181
|
+
// Eg: --python-version 3.10 --ignore-requires-python --no-warn-conflicts
|
|
10182
|
+
if (process?.env?.PIP_INSTALL_ARGS) {
|
|
10183
|
+
const addArgs = process.env.PIP_INSTALL_ARGS.split(" ");
|
|
10184
|
+
pipInstallArgs = pipInstallArgs.concat(addArgs);
|
|
10185
|
+
} else {
|
|
10186
|
+
pipInstallArgs = pipInstallArgs.concat([
|
|
10187
|
+
"--ignore-requires-python",
|
|
10188
|
+
"--no-compile",
|
|
10189
|
+
"--no-warn-script-location",
|
|
10190
|
+
"--no-warn-conflicts",
|
|
10191
|
+
]);
|
|
10192
|
+
}
|
|
10193
|
+
if (DEBUG_MODE) {
|
|
10194
|
+
console.log(
|
|
10195
|
+
"Installing",
|
|
10196
|
+
pkgList.length,
|
|
10197
|
+
"using the command",
|
|
10198
|
+
python_cmd_for_tree,
|
|
10199
|
+
pipInstallArgs.join(" "),
|
|
10200
|
+
);
|
|
10201
|
+
}
|
|
10202
|
+
for (const apkg of pkgList) {
|
|
10203
|
+
let pkgSpecifier = apkg.name;
|
|
10204
|
+
if (apkg.version && apkg.version !== "latest") {
|
|
10205
|
+
pkgSpecifier = `${apkg.name}==${apkg.version}`;
|
|
10206
|
+
} else if (apkg.properties) {
|
|
10207
|
+
let versionSpecifierFound = false;
|
|
10208
|
+
for (const aprop of apkg.properties) {
|
|
10209
|
+
if (aprop.name === "cdx:pypi:versionSpecifiers") {
|
|
10210
|
+
pkgSpecifier = `${apkg.name}${aprop.value}`;
|
|
10211
|
+
versionSpecifierFound = true;
|
|
10212
|
+
break;
|
|
10213
|
+
}
|
|
10214
|
+
}
|
|
10215
|
+
if (!versionSpecifierFound) {
|
|
10216
|
+
failedPkgList.push(apkg);
|
|
10217
|
+
continue;
|
|
10218
|
+
}
|
|
10219
|
+
} else {
|
|
10220
|
+
failedPkgList.push(apkg);
|
|
10221
|
+
continue;
|
|
10222
|
+
}
|
|
10223
|
+
if (DEBUG_MODE) {
|
|
10224
|
+
console.log("Installing", pkgSpecifier);
|
|
10225
|
+
}
|
|
10226
|
+
// Attempt to perform pip install for pkgSpecifier
|
|
10227
|
+
const result = spawnSync(
|
|
10228
|
+
python_cmd_for_tree,
|
|
10229
|
+
[...pipInstallArgs, pkgSpecifier],
|
|
10230
|
+
{
|
|
10231
|
+
cwd: basePath,
|
|
10232
|
+
encoding: "utf-8",
|
|
10233
|
+
timeout: TIMEOUT_MS,
|
|
10234
|
+
shell: isWin,
|
|
10235
|
+
env,
|
|
10236
|
+
},
|
|
10237
|
+
);
|
|
10238
|
+
if (result.status !== 0 || result.error) {
|
|
10239
|
+
failedPkgList.push(apkg);
|
|
10240
|
+
if (DEBUG_MODE) {
|
|
10241
|
+
console.log(apkg.name, "failed to install.");
|
|
10242
|
+
}
|
|
10243
|
+
}
|
|
10244
|
+
}
|
|
10245
|
+
// Did any package get installed successfully?
|
|
10246
|
+
if (failedPkgList.length < pkgList.length) {
|
|
10247
|
+
const dependenciesMap = {};
|
|
10248
|
+
const tree = getTreeWithPlugin(env, python_cmd_for_tree, basePath);
|
|
10249
|
+
for (const t of tree) {
|
|
10250
|
+
const name = t.name.replace(/_/g, "-").toLowerCase();
|
|
10251
|
+
// We can ignore excluded components such as build tools
|
|
10252
|
+
if (PYTHON_EXCLUDED_COMPONENTS.includes(name)) {
|
|
10253
|
+
continue;
|
|
10254
|
+
}
|
|
10255
|
+
if (parentComponent && parentComponent.name === t.name) {
|
|
10256
|
+
t.version = parentComponent.version;
|
|
10257
|
+
} else if (t.version && t.version === "latest") {
|
|
10258
|
+
continue;
|
|
10259
|
+
}
|
|
10260
|
+
const version = t.version;
|
|
10261
|
+
const purlString = new PackageURL(
|
|
10262
|
+
"pypi",
|
|
10263
|
+
"",
|
|
10264
|
+
name,
|
|
10265
|
+
version,
|
|
10266
|
+
null,
|
|
10267
|
+
null,
|
|
10268
|
+
).toString();
|
|
10269
|
+
const apkg = {
|
|
10270
|
+
name,
|
|
10271
|
+
version,
|
|
10272
|
+
purl: purlString,
|
|
10273
|
+
type: "library",
|
|
10274
|
+
"bom-ref": decodeURIComponent(purlString),
|
|
10275
|
+
evidence: {
|
|
10276
|
+
identity: {
|
|
10277
|
+
field: "purl",
|
|
10278
|
+
confidence: 0.5,
|
|
10279
|
+
methods: [
|
|
10280
|
+
{
|
|
10281
|
+
technique: "instrumentation",
|
|
10282
|
+
confidence: 0.5,
|
|
10283
|
+
value: env.VIRTUAL_ENV,
|
|
10284
|
+
},
|
|
10285
|
+
],
|
|
10286
|
+
},
|
|
10287
|
+
},
|
|
10288
|
+
};
|
|
10289
|
+
// These packages have lower confidence
|
|
10290
|
+
pkgList.push(apkg);
|
|
10291
|
+
rootList.push({
|
|
10292
|
+
name,
|
|
10293
|
+
version,
|
|
10294
|
+
purl: purlString,
|
|
10295
|
+
"bom-ref": decodeURIComponent(purlString),
|
|
10296
|
+
});
|
|
10297
|
+
flattenDeps(dependenciesMap, pkgList, undefined, t);
|
|
10298
|
+
} // end for
|
|
10299
|
+
for (const k of Object.keys(dependenciesMap)) {
|
|
10300
|
+
dependenciesList.push({ ref: k, dependsOn: dependenciesMap[k] });
|
|
10301
|
+
}
|
|
10302
|
+
} // end if
|
|
10303
|
+
return {
|
|
10304
|
+
failedPkgList,
|
|
10305
|
+
rootList,
|
|
10306
|
+
dependenciesList,
|
|
10307
|
+
};
|
|
10308
|
+
}
|
|
10309
|
+
|
|
9936
10310
|
// taken from a very old package https://github.com/keithamus/parse-packagejson-name/blob/master/index.js
|
|
9937
10311
|
export function parsePackageJsonName(name) {
|
|
9938
10312
|
const nameRegExp = /^(?:@([^/]+)\/)?(([^.]+)(?:\.(.*))?)$/;
|
|
@@ -11236,3 +11610,22 @@ export function isValidIriReference(iri) {
|
|
|
11236
11610
|
}
|
|
11237
11611
|
return false;
|
|
11238
11612
|
}
|
|
11613
|
+
|
|
11614
|
+
/**
|
|
11615
|
+
* Method to check if a given dependency tree is partial or not.
|
|
11616
|
+
*
|
|
11617
|
+
* @param {Array} dependencies List of dependencies
|
|
11618
|
+
* @returns {Boolean} True if the dependency tree lacks any non-root parents without children. False otherwise.
|
|
11619
|
+
*/
|
|
11620
|
+
export function isPartialTree(dependencies) {
|
|
11621
|
+
if (dependencies?.length <= 1) {
|
|
11622
|
+
return true;
|
|
11623
|
+
}
|
|
11624
|
+
let parentsWithChildsCount = 0;
|
|
11625
|
+
for (const adep of dependencies) {
|
|
11626
|
+
if (adep?.dependsOn.length > 0) {
|
|
11627
|
+
parentsWithChildsCount++;
|
|
11628
|
+
}
|
|
11629
|
+
}
|
|
11630
|
+
return parentsWithChildsCount <= 1;
|
|
11631
|
+
}
|
package/utils.test.js
CHANGED
|
@@ -16,7 +16,9 @@ import {
|
|
|
16
16
|
getRepoLicense,
|
|
17
17
|
guessPypiMatchingVersion,
|
|
18
18
|
hasAnyProjectType,
|
|
19
|
+
isPartialTree,
|
|
19
20
|
isValidIriReference,
|
|
21
|
+
mapConanPkgRefToPurlStringAndNameAndVersion,
|
|
20
22
|
parseBazelActionGraph,
|
|
21
23
|
parseBazelBuild,
|
|
22
24
|
parseBazelSkyframe,
|
|
@@ -1552,17 +1554,147 @@ test("parse conan data", () => {
|
|
|
1552
1554
|
dep_list = parseConanData(
|
|
1553
1555
|
readFileSync("./test/data/cmakes/conanfile1.txt", { encoding: "utf-8" }),
|
|
1554
1556
|
);
|
|
1555
|
-
expect(dep_list.length).toEqual(
|
|
1557
|
+
expect(dep_list.length).toEqual(43);
|
|
1556
1558
|
expect(dep_list[0]).toEqual({
|
|
1557
1559
|
"bom-ref":
|
|
1558
|
-
"pkg:conan/7-Zip@19.00?
|
|
1560
|
+
"pkg:conan/7-Zip@19.00?channel=stable&rrev=bb67aa9bc0da3feddc68ca9f334f4c8b&user=iw",
|
|
1559
1561
|
name: "7-Zip",
|
|
1560
|
-
purl: "pkg:conan/7-Zip@19.00?
|
|
1562
|
+
purl: "pkg:conan/7-Zip@19.00?channel=stable&rrev=bb67aa9bc0da3feddc68ca9f334f4c8b&user=iw",
|
|
1561
1563
|
scope: "required",
|
|
1562
1564
|
version: "19.00",
|
|
1563
1565
|
});
|
|
1564
1566
|
});
|
|
1565
1567
|
|
|
1568
|
+
test("conan package reference mapper to pURL", () => {
|
|
1569
|
+
const checkParseResult = (inputPkgRef, expectedPurl) => {
|
|
1570
|
+
const [purl, name, version] =
|
|
1571
|
+
mapConanPkgRefToPurlStringAndNameAndVersion(inputPkgRef);
|
|
1572
|
+
expect(purl).toEqual(expectedPurl);
|
|
1573
|
+
|
|
1574
|
+
const expectedPurlPrefix = `pkg:conan/${name}@${version}`;
|
|
1575
|
+
expect(purl.substring(0, expectedPurlPrefix.length)).toEqual(
|
|
1576
|
+
expectedPurlPrefix,
|
|
1577
|
+
);
|
|
1578
|
+
};
|
|
1579
|
+
|
|
1580
|
+
checkParseResult("testpkg", "pkg:conan/testpkg@latest");
|
|
1581
|
+
|
|
1582
|
+
checkParseResult("testpkg/1.2.3", "pkg:conan/testpkg@1.2.3");
|
|
1583
|
+
|
|
1584
|
+
checkParseResult(
|
|
1585
|
+
"testpkg/1.2.3#recipe_revision",
|
|
1586
|
+
"pkg:conan/testpkg@1.2.3?rrev=recipe_revision",
|
|
1587
|
+
);
|
|
1588
|
+
|
|
1589
|
+
checkParseResult(
|
|
1590
|
+
"testpkg/1.2.3@someuser/somechannel",
|
|
1591
|
+
"pkg:conan/testpkg@1.2.3?channel=somechannel&user=someuser",
|
|
1592
|
+
);
|
|
1593
|
+
|
|
1594
|
+
checkParseResult(
|
|
1595
|
+
"testpkg/1.2.3@someuser/somechannel#recipe_revision",
|
|
1596
|
+
"pkg:conan/testpkg@1.2.3?channel=somechannel&rrev=recipe_revision&user=someuser",
|
|
1597
|
+
);
|
|
1598
|
+
|
|
1599
|
+
checkParseResult(
|
|
1600
|
+
"testpkg/1.2.3@someuser/somechannel#recipe_revision:package_id#package_revision",
|
|
1601
|
+
"pkg:conan/testpkg@1.2.3" +
|
|
1602
|
+
"?channel=somechannel" +
|
|
1603
|
+
"&prev=package_revision" +
|
|
1604
|
+
"&rrev=recipe_revision" +
|
|
1605
|
+
"&user=someuser",
|
|
1606
|
+
);
|
|
1607
|
+
|
|
1608
|
+
const expectParseError = (pkgRef) => {
|
|
1609
|
+
const result = mapConanPkgRefToPurlStringAndNameAndVersion(pkgRef);
|
|
1610
|
+
expect(result[0]).toBe(null);
|
|
1611
|
+
expect(result[1]).toBe(null);
|
|
1612
|
+
expect(result[2]).toBe(null);
|
|
1613
|
+
};
|
|
1614
|
+
|
|
1615
|
+
expectParseError("testpkg/"); // empty version
|
|
1616
|
+
expectParseError("testpkg/1.2.3@"); // empty user
|
|
1617
|
+
expectParseError("testpkg/1.2.3@someuser"); // pkg ref is not allowed to stop here
|
|
1618
|
+
expectParseError("testpkg/1.2.3@someuser/"); // empty channel
|
|
1619
|
+
expectParseError("testpkg/1.2.3@someuser/somechannel#"); // empty recipe revision
|
|
1620
|
+
expectParseError("testpkg/1.2.3@someuser/somechannel#recipe_revision:"); // empty package id
|
|
1621
|
+
expectParseError(
|
|
1622
|
+
"testpkg/1.2.3@someuser/somechannel#recipe_revision:package_id",
|
|
1623
|
+
); // pkg ref is not allowed to stop here
|
|
1624
|
+
expectParseError(
|
|
1625
|
+
"testpkg/1.2.3@someuser/somechannel#recipe_revision:package_id#",
|
|
1626
|
+
); // empty package revision
|
|
1627
|
+
expectParseError("testpkg/1.2.3/unexpected"); // unexpected pkg ref segment separator
|
|
1628
|
+
expectParseError("testpkg/1.2.3@someuser/somechannel/unexpected"); // unexpected pkg ref segment separator
|
|
1629
|
+
expectParseError(
|
|
1630
|
+
"testpkg/1.2.3@someuser/somechannel#recipe_revision/unexpected",
|
|
1631
|
+
); // unexpected pkg ref segment separator
|
|
1632
|
+
expectParseError(
|
|
1633
|
+
"testpkg/1.2.3@someuser/somechannel#recipe_revision:package_id/unexpected",
|
|
1634
|
+
); // unexpected pkg ref segment separator
|
|
1635
|
+
expectParseError(
|
|
1636
|
+
"testpkg/1.2.3@someuser/somechannel#recipe_revision:package_id#package_revision/unexpected",
|
|
1637
|
+
); // unexpected pkg ref segment separator
|
|
1638
|
+
});
|
|
1639
|
+
|
|
1640
|
+
test("parse conan data where packages use custom user/channel", () => {
|
|
1641
|
+
let dep_list = parseConanLockData(
|
|
1642
|
+
readFileSync("./test/data/conan.with_custom_pkg_user_channel.lock", {
|
|
1643
|
+
encoding: "utf-8",
|
|
1644
|
+
}),
|
|
1645
|
+
);
|
|
1646
|
+
expect(dep_list.length).toEqual(4);
|
|
1647
|
+
expect(dep_list[0]).toEqual({
|
|
1648
|
+
name: "libcurl",
|
|
1649
|
+
version: "8.1.2",
|
|
1650
|
+
"bom-ref":
|
|
1651
|
+
"pkg:conan/libcurl@8.1.2?channel=stable&rrev=25215c550633ef0224152bc2c0556698&user=internal",
|
|
1652
|
+
purl: "pkg:conan/libcurl@8.1.2?channel=stable&rrev=25215c550633ef0224152bc2c0556698&user=internal",
|
|
1653
|
+
});
|
|
1654
|
+
expect(dep_list[1]).toEqual({
|
|
1655
|
+
name: "openssl",
|
|
1656
|
+
version: "3.1.0",
|
|
1657
|
+
"bom-ref":
|
|
1658
|
+
"pkg:conan/openssl@3.1.0?channel=stable&rrev=c9c6ab43aa40bafacf8b37c5948cdb1f&user=internal",
|
|
1659
|
+
purl: "pkg:conan/openssl@3.1.0?channel=stable&rrev=c9c6ab43aa40bafacf8b37c5948cdb1f&user=internal",
|
|
1660
|
+
});
|
|
1661
|
+
expect(dep_list[2]).toEqual({
|
|
1662
|
+
name: "zlib",
|
|
1663
|
+
version: "1.2.13",
|
|
1664
|
+
"bom-ref":
|
|
1665
|
+
"pkg:conan/zlib@1.2.13?channel=stable&rrev=aee6a56ff7927dc7261c55eb2db4fc5b&user=internal",
|
|
1666
|
+
purl: "pkg:conan/zlib@1.2.13?channel=stable&rrev=aee6a56ff7927dc7261c55eb2db4fc5b&user=internal",
|
|
1667
|
+
});
|
|
1668
|
+
expect(dep_list[3]).toEqual({
|
|
1669
|
+
name: "fmt",
|
|
1670
|
+
version: "10.0.0",
|
|
1671
|
+
purl: "pkg:conan/fmt@10.0.0?channel=stable&rrev=79e7cc169695bc058fb606f20df6bb10&user=internal",
|
|
1672
|
+
"bom-ref":
|
|
1673
|
+
"pkg:conan/fmt@10.0.0?channel=stable&rrev=79e7cc169695bc058fb606f20df6bb10&user=internal",
|
|
1674
|
+
});
|
|
1675
|
+
|
|
1676
|
+
dep_list = parseConanData(
|
|
1677
|
+
readFileSync("./test/data/conanfile.with_custom_pkg_user_channel.txt", {
|
|
1678
|
+
encoding: "utf-8",
|
|
1679
|
+
}),
|
|
1680
|
+
);
|
|
1681
|
+
expect(dep_list.length).toEqual(2);
|
|
1682
|
+
expect(dep_list[0]).toEqual({
|
|
1683
|
+
name: "libcurl",
|
|
1684
|
+
version: "8.1.2",
|
|
1685
|
+
"bom-ref": "pkg:conan/libcurl@8.1.2?channel=stable&user=internal",
|
|
1686
|
+
purl: "pkg:conan/libcurl@8.1.2?channel=stable&user=internal",
|
|
1687
|
+
scope: "required",
|
|
1688
|
+
});
|
|
1689
|
+
expect(dep_list[1]).toEqual({
|
|
1690
|
+
name: "fmt",
|
|
1691
|
+
version: "10.0.0",
|
|
1692
|
+
purl: "pkg:conan/fmt@10.0.0?channel=stable&user=internal",
|
|
1693
|
+
"bom-ref": "pkg:conan/fmt@10.0.0?channel=stable&user=internal",
|
|
1694
|
+
scope: "optional",
|
|
1695
|
+
});
|
|
1696
|
+
});
|
|
1697
|
+
|
|
1566
1698
|
test("parse clojure data", () => {
|
|
1567
1699
|
expect(parseLeiningenData(null)).toEqual([]);
|
|
1568
1700
|
let dep_list = parseLeiningenData(
|
|
@@ -2249,6 +2381,8 @@ test("parsePomMetadata", async () => {
|
|
|
2249
2381
|
expect(data.length).toEqual(deps.length);
|
|
2250
2382
|
});
|
|
2251
2383
|
|
|
2384
|
+
// These tests are disabled because they are returning undefined
|
|
2385
|
+
/*
|
|
2252
2386
|
test("get repo license", async () => {
|
|
2253
2387
|
let license = await getRepoLicense(
|
|
2254
2388
|
"https://github.com/ShiftLeftSecurity/sast-scan",
|
|
@@ -2271,8 +2405,6 @@ test("get repo license", async () => {
|
|
|
2271
2405
|
url: "https://github.com/CycloneDX/cdxgen/blob/master/LICENSE",
|
|
2272
2406
|
});
|
|
2273
2407
|
|
|
2274
|
-
// These tests are disabled because they are returning undefined
|
|
2275
|
-
/*
|
|
2276
2408
|
license = await getRepoLicense("https://cloud.google.com/go", {
|
|
2277
2409
|
group: "cloud.google.com",
|
|
2278
2410
|
name: "go"
|
|
@@ -2287,8 +2419,8 @@ test("get repo license", async () => {
|
|
|
2287
2419
|
id: "MIT",
|
|
2288
2420
|
url: "https://github.com/ugorji/go/blob/master/LICENSE"
|
|
2289
2421
|
});
|
|
2290
|
-
*/
|
|
2291
2422
|
});
|
|
2423
|
+
*/
|
|
2292
2424
|
|
|
2293
2425
|
test("get go pkg license", async () => {
|
|
2294
2426
|
let license = await getGoPkgLicense({
|
|
@@ -2852,8 +2984,8 @@ test("parsePnpmLock", async () => {
|
|
|
2852
2984
|
expect(parsedList.dependenciesList).toHaveLength(462);
|
|
2853
2985
|
expect(parsedList.pkgList.filter((pkg) => !pkg.scope)).toHaveLength(3);
|
|
2854
2986
|
parsedList = await parsePnpmLock("./pnpm-lock.yaml");
|
|
2855
|
-
expect(parsedList.pkgList.length).toEqual(
|
|
2856
|
-
expect(parsedList.dependenciesList.length).toEqual(
|
|
2987
|
+
expect(parsedList.pkgList.length).toEqual(652);
|
|
2988
|
+
expect(parsedList.dependenciesList.length).toEqual(652);
|
|
2857
2989
|
expect(parsedList.pkgList[0]).toEqual({
|
|
2858
2990
|
group: "@ampproject",
|
|
2859
2991
|
name: "remapping",
|
|
@@ -2919,6 +3051,7 @@ test("parseYarnLock", async () => {
|
|
|
2919
3051
|
},
|
|
2920
3052
|
});
|
|
2921
3053
|
expect(parsedList.dependenciesList.length).toEqual(56);
|
|
3054
|
+
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
|
|
2922
3055
|
identMap = yarnLockToIdentMap(
|
|
2923
3056
|
readFileSync("./test/data/yarn_locks/yarn.lock", "utf8"),
|
|
2924
3057
|
);
|
|
@@ -2962,6 +3095,7 @@ test("parseYarnLock", async () => {
|
|
|
2962
3095
|
parsedList = await parseYarnLock("./test/data/yarn_locks/yarn-multi.lock");
|
|
2963
3096
|
expect(parsedList.pkgList.length).toEqual(1909);
|
|
2964
3097
|
expect(parsedList.dependenciesList.length).toEqual(1909);
|
|
3098
|
+
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
|
|
2965
3099
|
expect(parsedList.pkgList[0]).toEqual({
|
|
2966
3100
|
_integrity:
|
|
2967
3101
|
"sha512-zpruxnFMz6K94gs2pqc3sidzFDbQpKT5D6P/J/I9s8ekHZ5eczgnRp6pqXC86Bh7+44j/btpmOT0kwiboyqTnA==",
|
|
@@ -2994,6 +3128,7 @@ test("parseYarnLock", async () => {
|
|
|
2994
3128
|
parsedList = await parseYarnLock("./test/data/yarn_locks/yarn-light.lock");
|
|
2995
3129
|
expect(parsedList.pkgList.length).toEqual(315);
|
|
2996
3130
|
expect(parsedList.dependenciesList.length).toEqual(315);
|
|
3131
|
+
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
|
|
2997
3132
|
expect(parsedList.pkgList[0]).toEqual({
|
|
2998
3133
|
_integrity:
|
|
2999
3134
|
"sha512-rZ1k9kQvJX21Vwgx1L6kSQ6yeXo9cCMyqURSnjG+MRoJn+Mr3LblxmVdzScHXRzv0N9yzy49oG7Bqxp9Knyv/g==",
|
|
@@ -3026,6 +3161,7 @@ test("parseYarnLock", async () => {
|
|
|
3026
3161
|
parsedList = await parseYarnLock("./test/data/yarn_locks/yarn3.lock");
|
|
3027
3162
|
expect(parsedList.pkgList.length).toEqual(5);
|
|
3028
3163
|
expect(parsedList.dependenciesList.length).toEqual(5);
|
|
3164
|
+
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
|
|
3029
3165
|
expect(parsedList.pkgList[1]).toEqual({
|
|
3030
3166
|
_integrity:
|
|
3031
3167
|
"sha512-+X9Jn4mPI+RYV0ITiiLyJSYlT9um111BocJSaztsxXR+9ZxWErpzdfQqyk+EYZUOklugjJkerQZRtJGLfJeClw==",
|
|
@@ -3058,6 +3194,7 @@ test("parseYarnLock", async () => {
|
|
|
3058
3194
|
parsedList = await parseYarnLock("./test/data/yarn_locks/yarnv2.lock");
|
|
3059
3195
|
expect(parsedList.pkgList.length).toEqual(1088);
|
|
3060
3196
|
expect(parsedList.dependenciesList.length).toEqual(1088);
|
|
3197
|
+
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
|
|
3061
3198
|
expect(parsedList.pkgList[0]).toEqual({
|
|
3062
3199
|
_integrity:
|
|
3063
3200
|
"sha512-G0U5NjBUYIs39l1J1ckgpVfVX2IxpzRAIT4/2An86O2Mcri3k5xNu7/RRkfObo12wN9s7BmnREAMhH7252oZiA==",
|
|
@@ -3089,6 +3226,7 @@ test("parseYarnLock", async () => {
|
|
|
3089
3226
|
parsedList = await parseYarnLock("./test/data/yarn_locks/yarnv3.lock");
|
|
3090
3227
|
expect(parsedList.pkgList.length).toEqual(363);
|
|
3091
3228
|
expect(parsedList.dependenciesList.length).toEqual(363);
|
|
3229
|
+
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
|
|
3092
3230
|
expect(parsedList.pkgList[0]).toEqual({
|
|
3093
3231
|
_integrity:
|
|
3094
3232
|
"sha512-vtU+q0TmdIDmezU7lKub73vObN6nmd3lkcKWz7R9hyNI8gz5o7grDb+FML9nykOLW+09gGIup2xyJ86j5vBKpg==",
|
|
@@ -3120,6 +3258,7 @@ test("parseYarnLock", async () => {
|
|
|
3120
3258
|
parsedList = await parseYarnLock("./test/data/yarn_locks/yarn4.lock");
|
|
3121
3259
|
expect(parsedList.pkgList.length).toEqual(1);
|
|
3122
3260
|
expect(parsedList.dependenciesList.length).toEqual(1);
|
|
3261
|
+
expect(isPartialTree(parsedList.dependenciesList)).toBeTruthy();
|
|
3123
3262
|
parsedList = await parseYarnLock("./test/data/yarn_locks/yarn-at.lock");
|
|
3124
3263
|
expect(parsedList.pkgList.length).toEqual(4);
|
|
3125
3264
|
expect(parsedList.dependenciesList.length).toEqual(4);
|
|
@@ -3151,30 +3290,51 @@ test("parseYarnLock", async () => {
|
|
|
3151
3290
|
parsedList = await parseYarnLock("./test/data/yarn_locks/yarn5.lock");
|
|
3152
3291
|
expect(parsedList.pkgList.length).toEqual(1962);
|
|
3153
3292
|
expect(parsedList.dependenciesList.length).toEqual(1962);
|
|
3293
|
+
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
|
|
3154
3294
|
expect(parsedList.pkgList[0].purl).toEqual(
|
|
3155
3295
|
"pkg:npm/%40ampproject/remapping@2.2.0",
|
|
3156
3296
|
);
|
|
3157
3297
|
expect(parsedList.pkgList[0]["bom-ref"]).toEqual(
|
|
3158
3298
|
"pkg:npm/@ampproject/remapping@2.2.0",
|
|
3159
3299
|
);
|
|
3300
|
+
expect(parsedList.dependenciesList[1]).toEqual({
|
|
3301
|
+
ref: "pkg:npm/@babel/code-frame@7.12.11",
|
|
3302
|
+
dependsOn: ["pkg:npm/@babel/highlight@7.18.6"],
|
|
3303
|
+
});
|
|
3160
3304
|
parsedList = await parseYarnLock("./test/data/yarn_locks/yarn6.lock");
|
|
3161
3305
|
expect(parsedList.pkgList.length).toEqual(1472);
|
|
3162
3306
|
expect(parsedList.dependenciesList.length).toEqual(1472);
|
|
3307
|
+
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
|
|
3163
3308
|
expect(parsedList.pkgList[0].purl).toEqual(
|
|
3164
3309
|
"pkg:npm/%40aashutoshrathi/word-wrap@1.2.6",
|
|
3165
3310
|
);
|
|
3166
3311
|
expect(parsedList.pkgList[0]["bom-ref"]).toEqual(
|
|
3167
3312
|
"pkg:npm/@aashutoshrathi/word-wrap@1.2.6",
|
|
3168
3313
|
);
|
|
3314
|
+
expect(parsedList.dependenciesList[1]).toEqual({
|
|
3315
|
+
ref: "pkg:npm/@ampproject/remapping@2.2.1",
|
|
3316
|
+
dependsOn: [
|
|
3317
|
+
"pkg:npm/@jridgewell/gen-mapping@0.3.3",
|
|
3318
|
+
"pkg:npm/@jridgewell/trace-mapping@0.3.19",
|
|
3319
|
+
],
|
|
3320
|
+
});
|
|
3169
3321
|
parsedList = await parseYarnLock("./test/data/yarn_locks/yarn7.lock");
|
|
3170
3322
|
expect(parsedList.pkgList.length).toEqual(1350);
|
|
3171
3323
|
expect(parsedList.dependenciesList.length).toEqual(1347);
|
|
3324
|
+
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
|
|
3172
3325
|
expect(parsedList.pkgList[0].purl).toEqual(
|
|
3173
3326
|
"pkg:npm/%40aashutoshrathi/word-wrap@1.2.6",
|
|
3174
3327
|
);
|
|
3175
3328
|
expect(parsedList.pkgList[0]["bom-ref"]).toEqual(
|
|
3176
3329
|
"pkg:npm/@aashutoshrathi/word-wrap@1.2.6",
|
|
3177
3330
|
);
|
|
3331
|
+
expect(parsedList.dependenciesList[1]).toEqual({
|
|
3332
|
+
ref: "pkg:npm/@ampproject/remapping@2.2.1",
|
|
3333
|
+
dependsOn: [
|
|
3334
|
+
"pkg:npm/@jridgewell/gen-mapping@0.3.3",
|
|
3335
|
+
"pkg:npm/@jridgewell/trace-mapping@0.3.19",
|
|
3336
|
+
],
|
|
3337
|
+
});
|
|
3178
3338
|
parsedList = await parseYarnLock("./test/data/yarn_locks/yarnv4.lock");
|
|
3179
3339
|
expect(parsedList.pkgList.length).toEqual(1851);
|
|
3180
3340
|
expect(parsedList.dependenciesList.length).toEqual(1851);
|
|
@@ -3184,6 +3344,11 @@ test("parseYarnLock", async () => {
|
|
|
3184
3344
|
expect(parsedList.pkgList[0]["bom-ref"]).toEqual(
|
|
3185
3345
|
"pkg:npm/@aashutoshrathi/word-wrap@1.2.6",
|
|
3186
3346
|
);
|
|
3347
|
+
expect(parsedList.dependenciesList[1]).toEqual({
|
|
3348
|
+
ref: "pkg:npm/@actions/core@1.2.6",
|
|
3349
|
+
dependsOn: [],
|
|
3350
|
+
});
|
|
3351
|
+
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
|
|
3187
3352
|
parsedList = await parseYarnLock("./test/data/yarn_locks/yarnv4.1.lock");
|
|
3188
3353
|
expect(parsedList.pkgList.length).toEqual(861);
|
|
3189
3354
|
expect(parsedList.dependenciesList.length).toEqual(858);
|
|
@@ -3196,11 +3361,27 @@ test("parseYarnLock", async () => {
|
|
|
3196
3361
|
expect(parsedList.pkgList[0]._integrity).toEqual(
|
|
3197
3362
|
"sha512-U8KyMaYaRnkrOaDUO8T093a7RUKqV+4EkwZ2gC5VASgsL8iqwU5M0fESD/i1Jha2/1q1Oa0wqiJ31yZES3Fhnw==",
|
|
3198
3363
|
);
|
|
3199
|
-
|
|
3364
|
+
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
|
|
3200
3365
|
parsedList = await parseYarnLock("./test/data/yarn_locks/yarnv1-fs.lock");
|
|
3201
3366
|
expect(parsedList.pkgList.length).toEqual(882);
|
|
3202
3367
|
expect(parsedList.dependenciesList.length).toEqual(882);
|
|
3203
3368
|
expect(parsedList.pkgList[0].purl).toEqual("pkg:npm/abbrev@1.0.9");
|
|
3369
|
+
expect(parsedList.dependenciesList[1]).toEqual({
|
|
3370
|
+
ref: "pkg:npm/accepts@1.3.3",
|
|
3371
|
+
dependsOn: ["pkg:npm/mime-types@2.1.12", "pkg:npm/negotiator@0.6.1"],
|
|
3372
|
+
});
|
|
3373
|
+
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
|
|
3374
|
+
parsedList = await parseYarnLock("./test/data/yarn_locks/yarnv1-empty.lock");
|
|
3375
|
+
expect(parsedList.pkgList.length).toEqual(770);
|
|
3376
|
+
expect(parsedList.dependenciesList.length).toEqual(770);
|
|
3377
|
+
expect(isPartialTree(parsedList.dependenciesList)).toBeFalsy();
|
|
3378
|
+
expect(parsedList.pkgList[0].purl).toEqual(
|
|
3379
|
+
"pkg:npm/%40ampproject/remapping@2.2.0",
|
|
3380
|
+
);
|
|
3381
|
+
expect(parsedList.dependenciesList[1]).toEqual({
|
|
3382
|
+
ref: "pkg:npm/@aws-sdk/shared-ini-file-loader@3.188.0",
|
|
3383
|
+
dependsOn: ["pkg:npm/@aws-sdk/types@3.188.0", "pkg:npm/tslib@2.4.0"],
|
|
3384
|
+
});
|
|
3204
3385
|
});
|
|
3205
3386
|
|
|
3206
3387
|
test("parseComposerLock", () => {
|
package/validator.js
CHANGED
|
@@ -3,7 +3,7 @@ import { dirname, join } from "node:path";
|
|
|
3
3
|
import Ajv from "ajv";
|
|
4
4
|
import addFormats from "ajv-formats";
|
|
5
5
|
import { PackageURL } from "packageurl-js";
|
|
6
|
-
import { DEBUG_MODE } from "./utils.js";
|
|
6
|
+
import { DEBUG_MODE, isPartialTree } from "./utils.js";
|
|
7
7
|
|
|
8
8
|
import { URL, fileURLToPath } from "node:url";
|
|
9
9
|
let url = import.meta.url;
|
|
@@ -217,6 +217,9 @@ export const validateRefs = (bomJson) => {
|
|
|
217
217
|
const warningsList = [];
|
|
218
218
|
const refMap = buildRefs(bomJson);
|
|
219
219
|
if (bomJson?.dependencies) {
|
|
220
|
+
if (isPartialTree(bomJson.dependencies)) {
|
|
221
|
+
warningsList.push("Dependency tree is partial lacking child nodes.");
|
|
222
|
+
}
|
|
220
223
|
for (const dep of bomJson.dependencies) {
|
|
221
224
|
if (
|
|
222
225
|
dep.ref.includes("%40") ||
|