@cyclonedx/cdxgen 9.10.2 → 9.11.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/evinse.js +2 -1
- package/data/frameworks-list.json +26 -1
- package/evinser.js +90 -33
- package/index.js +25 -1
- package/package.json +4 -4
- package/utils.js +22 -4
- package/utils.test.js +3 -3
package/bin/evinse.js
CHANGED
|
@@ -160,6 +160,31 @@
|
|
|
160
160
|
"pkg:generic/userver",
|
|
161
161
|
"pkg:generic/Wt/",
|
|
162
162
|
"pkg:generic/klone",
|
|
163
|
-
"pkg:generic/kcgi"
|
|
163
|
+
"pkg:generic/kcgi",
|
|
164
|
+
"pkg:composer/laravel",
|
|
165
|
+
"pkg:composer/symfony",
|
|
166
|
+
"pkg:composer/codeigniter",
|
|
167
|
+
"pkg:composer/cakephp",
|
|
168
|
+
"pkg:composer/zend",
|
|
169
|
+
"pkg:composer/yii",
|
|
170
|
+
"pkg:composer/aura",
|
|
171
|
+
"pkg:composer/phalcon",
|
|
172
|
+
"pkg:composer/fatfree",
|
|
173
|
+
"pkg:composer/qcodo",
|
|
174
|
+
"pkg:composer/fuelphp",
|
|
175
|
+
"pkg:composer/slim",
|
|
176
|
+
"pkg:composer/phpixie",
|
|
177
|
+
"pkg:composer/ice",
|
|
178
|
+
"pkg:composer/modx",
|
|
179
|
+
"pkg:composer/typo",
|
|
180
|
+
"pkg:composer/cappuccino",
|
|
181
|
+
"pkg:composer/pop",
|
|
182
|
+
"pkg:composer/zest",
|
|
183
|
+
"pkg:composer/silverstripe",
|
|
184
|
+
"pkg:composer/dash",
|
|
185
|
+
"pkg:composer/roducks",
|
|
186
|
+
"pkg:composer/queryphp",
|
|
187
|
+
"pkg:composer/silex",
|
|
188
|
+
"pkg:composer/psr"
|
|
164
189
|
]
|
|
165
190
|
}
|
package/evinser.js
CHANGED
|
@@ -207,7 +207,9 @@ export const createSlice = (
|
|
|
207
207
|
if (!filePath) {
|
|
208
208
|
return;
|
|
209
209
|
}
|
|
210
|
-
console.log(
|
|
210
|
+
console.log(
|
|
211
|
+
`Create ${sliceType} slice for ${path.resolve(filePath)}. Please wait ...`
|
|
212
|
+
);
|
|
211
213
|
const language = purlOrLanguage.startsWith("pkg:")
|
|
212
214
|
? purlToLanguage(purlOrLanguage, filePath)
|
|
213
215
|
: purlOrLanguage;
|
|
@@ -271,22 +273,35 @@ export const purlToLanguage = (purl, filePath) => {
|
|
|
271
273
|
case "pypi":
|
|
272
274
|
language = "python";
|
|
273
275
|
break;
|
|
276
|
+
case "composer":
|
|
277
|
+
language = "php";
|
|
278
|
+
break;
|
|
279
|
+
case "generic":
|
|
280
|
+
language = "c";
|
|
274
281
|
}
|
|
275
282
|
return language;
|
|
276
283
|
};
|
|
277
284
|
|
|
278
|
-
export const initFromSbom = (components) => {
|
|
285
|
+
export const initFromSbom = (components, language) => {
|
|
279
286
|
const purlLocationMap = {};
|
|
280
287
|
const purlImportsMap = {};
|
|
281
288
|
for (const comp of components) {
|
|
282
289
|
if (!comp || !comp.evidence) {
|
|
283
290
|
continue;
|
|
284
291
|
}
|
|
285
|
-
(
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
292
|
+
if (language === "php") {
|
|
293
|
+
(comp.properties || [])
|
|
294
|
+
.filter((v) => v.name === "Namespaces")
|
|
295
|
+
.forEach((v) => {
|
|
296
|
+
purlImportsMap[comp.purl] = (v.value || "").split(", ");
|
|
297
|
+
});
|
|
298
|
+
} else {
|
|
299
|
+
(comp.properties || [])
|
|
300
|
+
.filter((v) => v.name === "ImportedModules")
|
|
301
|
+
.forEach((v) => {
|
|
302
|
+
purlImportsMap[comp.purl] = (v.value || "").split(",");
|
|
303
|
+
});
|
|
304
|
+
}
|
|
290
305
|
if (comp.evidence.occurrences) {
|
|
291
306
|
purlLocationMap[comp.purl] = new Set(
|
|
292
307
|
comp.evidence.occurrences.map((v) => v.location)
|
|
@@ -323,7 +338,7 @@ export const analyzeProject = async (dbObjMap, options) => {
|
|
|
323
338
|
const components = bomJson.components || [];
|
|
324
339
|
// Load any existing purl-location information from the sbom.
|
|
325
340
|
// For eg: cdxgen populates this information for javascript projects
|
|
326
|
-
let { purlLocationMap, purlImportsMap } = initFromSbom(components);
|
|
341
|
+
let { purlLocationMap, purlImportsMap } = initFromSbom(components, language);
|
|
327
342
|
// Do reachables first so that usages slicing can reuse the atom file
|
|
328
343
|
if (options.withReachables) {
|
|
329
344
|
if (
|
|
@@ -487,6 +502,11 @@ export const parseSliceUsages = async (
|
|
|
487
502
|
typesToLookup.add(slice.fullName);
|
|
488
503
|
addToOverrides(lKeyOverrides, slice.fullName, fileName, slice.lineNumber);
|
|
489
504
|
}
|
|
505
|
+
// PHP imports from usages
|
|
506
|
+
if (slice.code && slice.code.startsWith("use") && !usages.length) {
|
|
507
|
+
typesToLookup.add(slice.fullName);
|
|
508
|
+
addToOverrides(lKeyOverrides, slice.fullName, fileName, slice.lineNumber);
|
|
509
|
+
}
|
|
490
510
|
for (const ausage of usages) {
|
|
491
511
|
const ausageLine =
|
|
492
512
|
ausage?.targetObj?.lineNumber || ausage?.definedBy?.lineNumber;
|
|
@@ -616,12 +636,25 @@ export const parseSliceUsages = async (
|
|
|
616
636
|
if (purlImportsMap && Object.keys(purlImportsMap).length) {
|
|
617
637
|
for (const apurl of Object.keys(purlImportsMap)) {
|
|
618
638
|
const apurlImports = purlImportsMap[apurl];
|
|
619
|
-
if (
|
|
620
|
-
|
|
621
|
-
|
|
639
|
+
if (language === "php") {
|
|
640
|
+
for (const aimp of apurlImports) {
|
|
641
|
+
if (atype.startsWith(aimp)) {
|
|
642
|
+
if (!purlLocationMap[apurl]) {
|
|
643
|
+
purlLocationMap[apurl] = new Set();
|
|
644
|
+
}
|
|
645
|
+
if (lKeyOverrides[atype]) {
|
|
646
|
+
purlLocationMap[apurl].add(...lKeyOverrides[atype]);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
622
649
|
}
|
|
623
|
-
|
|
624
|
-
|
|
650
|
+
} else {
|
|
651
|
+
if (apurlImports && apurlImports.includes(atype)) {
|
|
652
|
+
if (!purlLocationMap[apurl]) {
|
|
653
|
+
purlLocationMap[apurl] = new Set();
|
|
654
|
+
}
|
|
655
|
+
if (lKeyOverrides[atype]) {
|
|
656
|
+
purlLocationMap[apurl].add(...lKeyOverrides[atype]);
|
|
657
|
+
}
|
|
625
658
|
}
|
|
626
659
|
}
|
|
627
660
|
}
|
|
@@ -715,6 +748,11 @@ export const isFilterableType = (
|
|
|
715
748
|
return true;
|
|
716
749
|
}
|
|
717
750
|
}
|
|
751
|
+
if (["php"].includes(language)) {
|
|
752
|
+
if (!typeFullName.includes("\\") && !typeFullName.startsWith("use")) {
|
|
753
|
+
return true;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
718
756
|
if (userDefinedTypesMap[typeFullName]) {
|
|
719
757
|
return true;
|
|
720
758
|
}
|
|
@@ -739,12 +777,16 @@ export const detectServicesFromUsages = (language, slice, servicesMap = {}) => {
|
|
|
739
777
|
let endpoints = [];
|
|
740
778
|
let authenticated = undefined;
|
|
741
779
|
if (targetObj && targetObj?.resolvedMethod) {
|
|
742
|
-
|
|
780
|
+
if (language != "php") {
|
|
781
|
+
endpoints = extractEndpoints(language, targetObj?.resolvedMethod);
|
|
782
|
+
}
|
|
743
783
|
if (targetObj?.resolvedMethod.toLowerCase().includes("auth")) {
|
|
744
784
|
authenticated = true;
|
|
745
785
|
}
|
|
746
786
|
} else if (definedBy && definedBy?.resolvedMethod) {
|
|
747
|
-
|
|
787
|
+
if (language != "php") {
|
|
788
|
+
endpoints = extractEndpoints(language, definedBy?.resolvedMethod);
|
|
789
|
+
}
|
|
748
790
|
if (definedBy?.resolvedMethod.toLowerCase().includes("auth")) {
|
|
749
791
|
authenticated = true;
|
|
750
792
|
}
|
|
@@ -752,12 +794,17 @@ export const detectServicesFromUsages = (language, slice, servicesMap = {}) => {
|
|
|
752
794
|
if (usage.invokedCalls) {
|
|
753
795
|
for (const acall of usage.invokedCalls) {
|
|
754
796
|
if (acall.resolvedMethod) {
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
797
|
+
if (language != "php") {
|
|
798
|
+
const tmpEndpoints = extractEndpoints(
|
|
799
|
+
language,
|
|
800
|
+
acall.resolvedMethod
|
|
801
|
+
);
|
|
802
|
+
if (acall.resolvedMethod.toLowerCase().includes("auth")) {
|
|
803
|
+
authenticated = true;
|
|
804
|
+
}
|
|
805
|
+
if (tmpEndpoints && tmpEndpoints.length) {
|
|
806
|
+
endpoints = (endpoints || []).concat(tmpEndpoints);
|
|
807
|
+
}
|
|
761
808
|
}
|
|
762
809
|
}
|
|
763
810
|
}
|
|
@@ -791,7 +838,7 @@ export const detectServicesFromUDT = (
|
|
|
791
838
|
servicesMap
|
|
792
839
|
) => {
|
|
793
840
|
if (
|
|
794
|
-
["python", "py", "c", "cpp", "c++"].includes(language) &&
|
|
841
|
+
["python", "py", "c", "cpp", "c++", "php"].includes(language) &&
|
|
795
842
|
userDefinedTypes &&
|
|
796
843
|
userDefinedTypes.length
|
|
797
844
|
) {
|
|
@@ -803,7 +850,15 @@ export const detectServicesFromUDT = (
|
|
|
803
850
|
audt.name.toLowerCase().includes("registerhandler") ||
|
|
804
851
|
audt.name.toLowerCase().includes("endpoint") ||
|
|
805
852
|
audt.name.toLowerCase().includes("api") ||
|
|
806
|
-
audt.name.toLowerCase().includes("add_method")
|
|
853
|
+
audt.name.toLowerCase().includes("add_method") ||
|
|
854
|
+
audt.name.toLowerCase().includes("get") ||
|
|
855
|
+
audt.name.toLowerCase().includes("post") ||
|
|
856
|
+
audt.name.toLowerCase().includes("delete") ||
|
|
857
|
+
audt.name.toLowerCase().includes("put") ||
|
|
858
|
+
audt.name.toLowerCase().includes("head") ||
|
|
859
|
+
audt.name.toLowerCase().includes("options") ||
|
|
860
|
+
audt.name.toLowerCase().includes("addRoute") ||
|
|
861
|
+
audt.name.toLowerCase().includes("connect")
|
|
807
862
|
) {
|
|
808
863
|
const fields = audt.fields || [];
|
|
809
864
|
if (
|
|
@@ -819,14 +874,14 @@ export const detectServicesFromUDT = (
|
|
|
819
874
|
audt.fileName.replace(".py", "")
|
|
820
875
|
)}-service`;
|
|
821
876
|
}
|
|
822
|
-
if (
|
|
823
|
-
servicesMap[serviceName]
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
877
|
+
if (endpoints && endpoints.length) {
|
|
878
|
+
if (!servicesMap[serviceName]) {
|
|
879
|
+
servicesMap[serviceName] = {
|
|
880
|
+
endpoints: new Set(),
|
|
881
|
+
authenticated: false,
|
|
882
|
+
xTrustBoundary: undefined
|
|
883
|
+
};
|
|
884
|
+
}
|
|
830
885
|
for (const endpoint of endpoints) {
|
|
831
886
|
servicesMap[serviceName].endpoints.add(endpoint);
|
|
832
887
|
}
|
|
@@ -897,7 +952,7 @@ export const extractEndpoints = (language, code) => {
|
|
|
897
952
|
default:
|
|
898
953
|
endpoints = (code.match(/['"](.*?)['"]/gi) || [])
|
|
899
954
|
.map((v) => v.replace(/["']/g, "").replace("\n", ""))
|
|
900
|
-
.filter((v) => v.length > 2);
|
|
955
|
+
.filter((v) => v.length > 2 && v.includes("/"));
|
|
901
956
|
break;
|
|
902
957
|
}
|
|
903
958
|
return endpoints;
|
|
@@ -1015,7 +1070,7 @@ export const createEvinseFile = (sliceArtefacts, options) => {
|
|
|
1015
1070
|
console.log(evinseOutFile, "created successfully.");
|
|
1016
1071
|
} else {
|
|
1017
1072
|
console.log(
|
|
1018
|
-
"Unable to identify component evidence for the input SBOM. Only java, javascript and
|
|
1073
|
+
"Unable to identify component evidence for the input SBOM. Only java, javascript, python, and php projects are supported by evinse."
|
|
1019
1074
|
);
|
|
1020
1075
|
}
|
|
1021
1076
|
if (tempDir && tempDir.startsWith(tmpdir())) {
|
|
@@ -1249,6 +1304,8 @@ export const getClassTypeFromSignature = (language, typeFullName) => {
|
|
|
1249
1304
|
.replace(".<body>", "")
|
|
1250
1305
|
.replace(".__iter__", "")
|
|
1251
1306
|
.replace(".__init__", "");
|
|
1307
|
+
} else if (["php"].includes(language)) {
|
|
1308
|
+
typeFullName = typeFullName.split("->")[0].split("::")[0];
|
|
1252
1309
|
}
|
|
1253
1310
|
if (
|
|
1254
1311
|
typeFullName.startsWith("<unresolved") ||
|
package/index.js
CHANGED
|
@@ -4055,6 +4055,11 @@ export const createPHPBom = (path, options) => {
|
|
|
4055
4055
|
(options.multiProject ? "**/" : "") + "composer.json",
|
|
4056
4056
|
options
|
|
4057
4057
|
);
|
|
4058
|
+
if (!options.exclude) {
|
|
4059
|
+
options.exclude = [];
|
|
4060
|
+
}
|
|
4061
|
+
// Ignore vendor directories for lock files
|
|
4062
|
+
options.exclude.push("**/vendor/**");
|
|
4058
4063
|
let composerLockFiles = getAllFiles(
|
|
4059
4064
|
path,
|
|
4060
4065
|
(options.multiProject ? "**/" : "") + "composer.lock",
|
|
@@ -4123,6 +4128,7 @@ export const createPHPBom = (path, options) => {
|
|
|
4123
4128
|
if (DEBUG_MODE) {
|
|
4124
4129
|
console.log(`Parsing ${f}`);
|
|
4125
4130
|
}
|
|
4131
|
+
let rootRequires = [];
|
|
4126
4132
|
// Is there a composer.json to find the parent component
|
|
4127
4133
|
if (
|
|
4128
4134
|
!Object.keys(parentComponent).length &&
|
|
@@ -4131,6 +4137,7 @@ export const createPHPBom = (path, options) => {
|
|
|
4131
4137
|
const composerData = JSON.parse(
|
|
4132
4138
|
readFileSync(join(basePath, "composer.json"), { encoding: "utf-8" })
|
|
4133
4139
|
);
|
|
4140
|
+
rootRequires = composerData.require;
|
|
4134
4141
|
const pkgName = composerData.name;
|
|
4135
4142
|
if (pkgName) {
|
|
4136
4143
|
parentComponent.group = dirname(pkgName);
|
|
@@ -4152,16 +4159,33 @@ export const createPHPBom = (path, options) => {
|
|
|
4152
4159
|
);
|
|
4153
4160
|
}
|
|
4154
4161
|
}
|
|
4155
|
-
const retMap = parseComposerLock(f);
|
|
4162
|
+
const retMap = parseComposerLock(f, rootRequires);
|
|
4156
4163
|
if (retMap.pkgList && retMap.pkgList.length) {
|
|
4157
4164
|
pkgList = pkgList.concat(retMap.pkgList);
|
|
4158
4165
|
}
|
|
4159
4166
|
if (retMap.dependenciesList) {
|
|
4167
|
+
if (!Object.keys(parentComponent).length) {
|
|
4168
|
+
parentComponent = createDefaultParentComponent(
|
|
4169
|
+
path,
|
|
4170
|
+
"composer",
|
|
4171
|
+
options
|
|
4172
|
+
);
|
|
4173
|
+
}
|
|
4174
|
+
// Complete the dependency tree by making parent component depend on the first level
|
|
4175
|
+
const parentDependsOn = [];
|
|
4176
|
+
for (const p of retMap.rootList) {
|
|
4177
|
+
parentDependsOn.push(p["bom-ref"]);
|
|
4178
|
+
}
|
|
4179
|
+
const pdependencies = {
|
|
4180
|
+
ref: parentComponent["bom-ref"],
|
|
4181
|
+
dependsOn: parentDependsOn
|
|
4182
|
+
};
|
|
4160
4183
|
dependencies = mergeDependencies(
|
|
4161
4184
|
dependencies,
|
|
4162
4185
|
retMap.dependenciesList,
|
|
4163
4186
|
parentComponent
|
|
4164
4187
|
);
|
|
4188
|
+
dependencies.splice(0, 0, pdependencies);
|
|
4165
4189
|
}
|
|
4166
4190
|
}
|
|
4167
4191
|
return buildBomNSData(options, pkgList, "composer", {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyclonedx/cdxgen",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.11.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>",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@babel/parser": "^7.23.6",
|
|
59
|
-
"@babel/traverse": "^7.23.
|
|
59
|
+
"@babel/traverse": "^7.23.7",
|
|
60
60
|
"@npmcli/arborist": "7.2.2",
|
|
61
61
|
"ajv": "^8.12.0",
|
|
62
62
|
"ajv-formats": "^2.1.1",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"yargs": "^17.7.2"
|
|
84
84
|
},
|
|
85
85
|
"optionalDependencies": {
|
|
86
|
-
"@appthreat/atom": "1.8.
|
|
86
|
+
"@appthreat/atom": "1.8.3",
|
|
87
87
|
"@appthreat/cdx-proto": "^0.0.4",
|
|
88
88
|
"@cyclonedx/cdxgen-plugins-bin": "^1.5.4",
|
|
89
89
|
"@cyclonedx/cdxgen-plugins-bin-windows-amd64": "^1.5.4",
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"connect": "^3.7.0",
|
|
95
95
|
"jsonata": "^2.0.3",
|
|
96
96
|
"sequelize": "^6.35.2",
|
|
97
|
-
"sqlite3": "^5.1.
|
|
97
|
+
"sqlite3": "^5.1.7"
|
|
98
98
|
},
|
|
99
99
|
"files": [
|
|
100
100
|
"*.js",
|
package/utils.js
CHANGED
|
@@ -4059,6 +4059,9 @@ export const parseGemfileLockData = async function (gemLockData) {
|
|
|
4059
4059
|
const tmpA = l.split(" ");
|
|
4060
4060
|
if (tmpA && tmpA.length == 2) {
|
|
4061
4061
|
const name = tmpA[0];
|
|
4062
|
+
if (name === "remote:") {
|
|
4063
|
+
return;
|
|
4064
|
+
}
|
|
4062
4065
|
if (!pkgnames[name]) {
|
|
4063
4066
|
let version = tmpA[1].split(", ")[0];
|
|
4064
4067
|
version = version.replace(/[(>=<)~ ]/g, "");
|
|
@@ -4077,7 +4080,8 @@ export const parseGemfileLockData = async function (gemLockData) {
|
|
|
4077
4080
|
l === "PLATFORMS" ||
|
|
4078
4081
|
l === "DEPENDENCIES" ||
|
|
4079
4082
|
l === "RUBY VERSION" ||
|
|
4080
|
-
l === "BUNDLED WITH"
|
|
4083
|
+
l === "BUNDLED WITH" ||
|
|
4084
|
+
l === "PATH"
|
|
4081
4085
|
) {
|
|
4082
4086
|
specsFound = false;
|
|
4083
4087
|
}
|
|
@@ -5711,12 +5715,20 @@ export const parsePaketLockData = async function (paketLockData, pkgLockFile) {
|
|
|
5711
5715
|
* Parse composer lock file
|
|
5712
5716
|
*
|
|
5713
5717
|
* @param {string} pkgLockFile composer.lock file
|
|
5718
|
+
* @param {array} rootRequires require section from composer.json
|
|
5714
5719
|
*/
|
|
5715
|
-
export const parseComposerLock = function (pkgLockFile) {
|
|
5720
|
+
export const parseComposerLock = function (pkgLockFile, rootRequires) {
|
|
5716
5721
|
const pkgList = [];
|
|
5717
5722
|
const dependenciesList = [];
|
|
5718
5723
|
const dependenciesMap = {};
|
|
5719
5724
|
const pkgNamePurlMap = {};
|
|
5725
|
+
const rootList = [];
|
|
5726
|
+
const rootRequiresMap = {};
|
|
5727
|
+
if (rootRequires) {
|
|
5728
|
+
for (const rr of Object.keys(rootRequires)) {
|
|
5729
|
+
rootRequiresMap[rr] = true;
|
|
5730
|
+
}
|
|
5731
|
+
}
|
|
5720
5732
|
if (existsSync(pkgLockFile)) {
|
|
5721
5733
|
let lockData = {};
|
|
5722
5734
|
try {
|
|
@@ -5741,6 +5753,7 @@ export const parseComposerLock = function (pkgLockFile) {
|
|
|
5741
5753
|
if (!pkg || !pkg.name || !pkg.version) {
|
|
5742
5754
|
continue;
|
|
5743
5755
|
}
|
|
5756
|
+
|
|
5744
5757
|
let group = dirname(pkg.name);
|
|
5745
5758
|
if (group === ".") {
|
|
5746
5759
|
group = "";
|
|
@@ -5789,7 +5802,7 @@ export const parseComposerLock = function (pkgLockFile) {
|
|
|
5789
5802
|
for (const aaload of Object.keys(pkg.autoload)) {
|
|
5790
5803
|
if (aaload.startsWith("psr")) {
|
|
5791
5804
|
for (const ans of Object.keys(pkg.autoload[aaload])) {
|
|
5792
|
-
namespaces.push(ans);
|
|
5805
|
+
namespaces.push(ans.trim());
|
|
5793
5806
|
}
|
|
5794
5807
|
}
|
|
5795
5808
|
}
|
|
@@ -5803,6 +5816,10 @@ export const parseComposerLock = function (pkgLockFile) {
|
|
|
5803
5816
|
pkgList.push(apkg);
|
|
5804
5817
|
dependenciesMap[purl] = new Set();
|
|
5805
5818
|
pkgNamePurlMap[pkg.name] = purl;
|
|
5819
|
+
// Add this package to the root list if needed
|
|
5820
|
+
if (rootRequiresMap[pkg.name]) {
|
|
5821
|
+
rootList.push(apkg);
|
|
5822
|
+
}
|
|
5806
5823
|
}
|
|
5807
5824
|
}
|
|
5808
5825
|
// Pass 2: Construct dependency tree
|
|
@@ -5833,7 +5850,8 @@ export const parseComposerLock = function (pkgLockFile) {
|
|
|
5833
5850
|
}
|
|
5834
5851
|
return {
|
|
5835
5852
|
pkgList,
|
|
5836
|
-
dependenciesList
|
|
5853
|
+
dependenciesList,
|
|
5854
|
+
rootList
|
|
5837
5855
|
};
|
|
5838
5856
|
};
|
|
5839
5857
|
|
package/utils.test.js
CHANGED
|
@@ -1214,7 +1214,7 @@ test("parse github actions workflow data", async () => {
|
|
|
1214
1214
|
dep_list = parseGitHubWorkflowData(
|
|
1215
1215
|
readFileSync("./.github/workflows/repotests.yml", { encoding: "utf-8" })
|
|
1216
1216
|
);
|
|
1217
|
-
expect(dep_list.length).toEqual(
|
|
1217
|
+
expect(dep_list.length).toEqual(7);
|
|
1218
1218
|
expect(dep_list[0]).toEqual({
|
|
1219
1219
|
group: "actions",
|
|
1220
1220
|
name: "checkout",
|
|
@@ -1762,8 +1762,8 @@ test("parsePkgLock v3", async () => {
|
|
|
1762
1762
|
projectName: "cdxgen"
|
|
1763
1763
|
});
|
|
1764
1764
|
deps = parsedList.pkgList;
|
|
1765
|
-
expect(deps.length).toEqual(
|
|
1766
|
-
expect(parsedList.dependenciesList.length).toEqual(
|
|
1765
|
+
expect(deps.length).toEqual(1199);
|
|
1766
|
+
expect(parsedList.dependenciesList.length).toEqual(1199);
|
|
1767
1767
|
});
|
|
1768
1768
|
|
|
1769
1769
|
test("parseBowerJson", async () => {
|