@cyclonedx/cdxgen 9.10.1 → 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/README.md +2 -1
- package/bin/cdxgen.js +16 -1
- package/bin/evinse.js +2 -1
- package/data/frameworks-list.json +26 -1
- package/evinser.js +90 -33
- package/index.js +70 -5
- package/package.json +7 -6
- package/protobom.js +36 -0
- package/protobom.test.js +32 -0
- package/utils.js +161 -44
- package/utils.test.js +94 -19
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ Most SBOM tools are like barcode scanners. They can scan a few package manifest
|
|
|
22
22
|
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------- |
|
|
23
23
|
| Node.js | npm-shrinkwrap.json, package-lock.json, pnpm-lock.yaml, yarn.lock, rush.js, bower.json, .min.js | Yes except .min.js | Yes |
|
|
24
24
|
| Java | maven (pom.xml [1]), gradle (build.gradle, .kts), scala (sbt), bazel | Yes unless pom.xml is manually parsed due to unavailability of maven or errors | Yes |
|
|
25
|
-
| PHP | composer.lock | Yes |
|
|
25
|
+
| PHP | composer.lock | Yes | Yes |
|
|
26
26
|
| Python | pyproject.toml, setup.py, requirements.txt [2], Pipfile.lock, poetry.lock, pdm.lock, bdist_wheel, .whl, .egg-info | Yes using the automatic pip install/freeze. When disabled, only with Pipfile.lock and poetry.lock | Yes |
|
|
27
27
|
| Go | binary, go.mod, go.sum, Gopkg.lock | Yes except binary | Yes |
|
|
28
28
|
| Ruby | Gemfile.lock, gemspec | Only for Gemfile.lock | |
|
|
@@ -347,6 +347,7 @@ cdxgen can retain the dependency tree under the `dependencies` attribute for a s
|
|
|
347
347
|
- Python (requirements.txt, setup.py, pyproject.toml, poetry.lock)
|
|
348
348
|
- .NET (project.assets.json, paket.lock)
|
|
349
349
|
- Go (go.mod)
|
|
350
|
+
- PHP (composer.lock)
|
|
350
351
|
|
|
351
352
|
## Environment variables
|
|
352
353
|
|
package/bin/cdxgen.js
CHANGED
|
@@ -220,6 +220,17 @@ const args = yargs(hideBin(process.argv))
|
|
|
220
220
|
description: "Additional glob pattern(s) to ignore",
|
|
221
221
|
hidden: true
|
|
222
222
|
})
|
|
223
|
+
.option("export-proto", {
|
|
224
|
+
type: "boolean",
|
|
225
|
+
default: false,
|
|
226
|
+
description: "Serialize and export BOM as protobuf binary.",
|
|
227
|
+
hidden: true
|
|
228
|
+
})
|
|
229
|
+
.option("proto-bin-file", {
|
|
230
|
+
description: "Path for the serialized protobuf binary.",
|
|
231
|
+
default: "bom.cdx",
|
|
232
|
+
hidden: true
|
|
233
|
+
})
|
|
223
234
|
.completion("completion", "Generate bash/zsh completion")
|
|
224
235
|
.array("filter")
|
|
225
236
|
.array("only")
|
|
@@ -582,7 +593,11 @@ const checkPermissions = (filePath) => {
|
|
|
582
593
|
console.log(err);
|
|
583
594
|
}
|
|
584
595
|
}
|
|
585
|
-
|
|
596
|
+
// Protobuf serialization
|
|
597
|
+
if (options.exportProto) {
|
|
598
|
+
const protobomModule = await import("../protobom.js");
|
|
599
|
+
protobomModule.writeBinary(bomNSData.bomJson, options.protoBinFile);
|
|
600
|
+
}
|
|
586
601
|
if (options.print && bomNSData.bomJson && bomNSData.bomJson.components) {
|
|
587
602
|
printDependencyTree(bomNSData.bomJson);
|
|
588
603
|
printTable(bomNSData.bomJson);
|
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
|
@@ -4048,11 +4048,18 @@ export const createContainerSpecLikeBom = async (path, options) => {
|
|
|
4048
4048
|
* @param options Parse options from the cli
|
|
4049
4049
|
*/
|
|
4050
4050
|
export const createPHPBom = (path, options) => {
|
|
4051
|
+
let dependencies = [];
|
|
4052
|
+
let parentComponent = {};
|
|
4051
4053
|
const composerJsonFiles = getAllFiles(
|
|
4052
4054
|
path,
|
|
4053
4055
|
(options.multiProject ? "**/" : "") + "composer.json",
|
|
4054
4056
|
options
|
|
4055
4057
|
);
|
|
4058
|
+
if (!options.exclude) {
|
|
4059
|
+
options.exclude = [];
|
|
4060
|
+
}
|
|
4061
|
+
// Ignore vendor directories for lock files
|
|
4062
|
+
options.exclude.push("**/vendor/**");
|
|
4056
4063
|
let composerLockFiles = getAllFiles(
|
|
4057
4064
|
path,
|
|
4058
4065
|
(options.multiProject ? "**/" : "") + "composer.lock",
|
|
@@ -4117,17 +4124,75 @@ export const createPHPBom = (path, options) => {
|
|
|
4117
4124
|
);
|
|
4118
4125
|
if (composerLockFiles.length) {
|
|
4119
4126
|
for (const f of composerLockFiles) {
|
|
4127
|
+
const basePath = dirname(f);
|
|
4120
4128
|
if (DEBUG_MODE) {
|
|
4121
4129
|
console.log(`Parsing ${f}`);
|
|
4122
4130
|
}
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4131
|
+
let rootRequires = [];
|
|
4132
|
+
// Is there a composer.json to find the parent component
|
|
4133
|
+
if (
|
|
4134
|
+
!Object.keys(parentComponent).length &&
|
|
4135
|
+
existsSync(join(basePath, "composer.json"))
|
|
4136
|
+
) {
|
|
4137
|
+
const composerData = JSON.parse(
|
|
4138
|
+
readFileSync(join(basePath, "composer.json"), { encoding: "utf-8" })
|
|
4139
|
+
);
|
|
4140
|
+
rootRequires = composerData.require;
|
|
4141
|
+
const pkgName = composerData.name;
|
|
4142
|
+
if (pkgName) {
|
|
4143
|
+
parentComponent.group = dirname(pkgName);
|
|
4144
|
+
if (parentComponent.group === ".") {
|
|
4145
|
+
parentComponent.group = "";
|
|
4146
|
+
}
|
|
4147
|
+
parentComponent.name = basename(pkgName);
|
|
4148
|
+
parentComponent.type = "application";
|
|
4149
|
+
parentComponent.version = composerData.version || "latest";
|
|
4150
|
+
parentComponent["bom-ref"] = decodeURIComponent(
|
|
4151
|
+
new PackageURL(
|
|
4152
|
+
"composer",
|
|
4153
|
+
parentComponent.group,
|
|
4154
|
+
parentComponent.name,
|
|
4155
|
+
parentComponent.version,
|
|
4156
|
+
null,
|
|
4157
|
+
null
|
|
4158
|
+
).toString()
|
|
4159
|
+
);
|
|
4160
|
+
}
|
|
4161
|
+
}
|
|
4162
|
+
const retMap = parseComposerLock(f, rootRequires);
|
|
4163
|
+
if (retMap.pkgList && retMap.pkgList.length) {
|
|
4164
|
+
pkgList = pkgList.concat(retMap.pkgList);
|
|
4165
|
+
}
|
|
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
|
+
};
|
|
4183
|
+
dependencies = mergeDependencies(
|
|
4184
|
+
dependencies,
|
|
4185
|
+
retMap.dependenciesList,
|
|
4186
|
+
parentComponent
|
|
4187
|
+
);
|
|
4188
|
+
dependencies.splice(0, 0, pdependencies);
|
|
4126
4189
|
}
|
|
4127
4190
|
}
|
|
4128
4191
|
return buildBomNSData(options, pkgList, "composer", {
|
|
4129
4192
|
src: path,
|
|
4130
|
-
filename: composerLockFiles.join(", ")
|
|
4193
|
+
filename: composerLockFiles.join(", "),
|
|
4194
|
+
dependencies,
|
|
4195
|
+
parentComponent
|
|
4131
4196
|
});
|
|
4132
4197
|
}
|
|
4133
4198
|
return {};
|
|
@@ -4331,7 +4396,7 @@ export const createCsharpBom = async (
|
|
|
4331
4396
|
console.log(`Parsing ${f}`);
|
|
4332
4397
|
}
|
|
4333
4398
|
pkgData = readFileSync(f, { encoding: "utf-8" });
|
|
4334
|
-
const results = await parsePaketLockData(pkgData);
|
|
4399
|
+
const results = await parsePaketLockData(pkgData, f);
|
|
4335
4400
|
const dlist = results.pkgList;
|
|
4336
4401
|
const deps = results.dependenciesList;
|
|
4337
4402
|
if (dlist && dlist.length) {
|
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,8 +56,8 @@
|
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@babel/parser": "^7.23.6",
|
|
59
|
-
"@babel/traverse": "^7.23.
|
|
60
|
-
"@npmcli/arborist": "7.2.
|
|
59
|
+
"@babel/traverse": "^7.23.7",
|
|
60
|
+
"@npmcli/arborist": "7.2.2",
|
|
61
61
|
"ajv": "^8.12.0",
|
|
62
62
|
"ajv-formats": "^2.1.1",
|
|
63
63
|
"cheerio": "^1.0.0-rc.12",
|
|
@@ -83,7 +83,8 @@
|
|
|
83
83
|
"yargs": "^17.7.2"
|
|
84
84
|
},
|
|
85
85
|
"optionalDependencies": {
|
|
86
|
-
"@appthreat/atom": "1.
|
|
86
|
+
"@appthreat/atom": "1.8.3",
|
|
87
|
+
"@appthreat/cdx-proto": "^0.0.4",
|
|
87
88
|
"@cyclonedx/cdxgen-plugins-bin": "^1.5.4",
|
|
88
89
|
"@cyclonedx/cdxgen-plugins-bin-windows-amd64": "^1.5.4",
|
|
89
90
|
"@cyclonedx/cdxgen-plugins-bin-arm64": "^1.5.4",
|
|
@@ -93,7 +94,7 @@
|
|
|
93
94
|
"connect": "^3.7.0",
|
|
94
95
|
"jsonata": "^2.0.3",
|
|
95
96
|
"sequelize": "^6.35.2",
|
|
96
|
-
"sqlite3": "^5.1.
|
|
97
|
+
"sqlite3": "^5.1.7"
|
|
97
98
|
},
|
|
98
99
|
"files": [
|
|
99
100
|
"*.js",
|
|
@@ -105,7 +106,7 @@
|
|
|
105
106
|
"docsify-cli": "^4.4.4",
|
|
106
107
|
"eslint": "^8.56.0",
|
|
107
108
|
"eslint-config-prettier": "^9.1.0",
|
|
108
|
-
"eslint-plugin-prettier": "^5.
|
|
109
|
+
"eslint-plugin-prettier": "^5.1.2",
|
|
109
110
|
"jest": "^29.7.0",
|
|
110
111
|
"prettier": "3.1.1"
|
|
111
112
|
}
|
package/protobom.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Bom } from "@appthreat/cdx-proto";
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
|
|
4
|
+
const stringifyIfNeeded = (bomJson) => {
|
|
5
|
+
if (typeof bomJson === "string" || bomJson instanceof String) {
|
|
6
|
+
return bomJson;
|
|
7
|
+
}
|
|
8
|
+
return JSON.stringify(bomJson);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const writeBinary = (bomJson, binFile) => {
|
|
12
|
+
if (bomJson && binFile) {
|
|
13
|
+
const bomObject = new Bom();
|
|
14
|
+
writeFileSync(
|
|
15
|
+
binFile,
|
|
16
|
+
bomObject
|
|
17
|
+
.fromJsonString(stringifyIfNeeded(bomJson), {
|
|
18
|
+
ignoreUnknownFields: true
|
|
19
|
+
})
|
|
20
|
+
.toBinary({ writeUnknownFields: true })
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const readBinary = (binFile, asJson = true) => {
|
|
26
|
+
if (!existsSync(binFile)) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
const bomObject = new Bom().fromBinary(readFileSync(binFile), {
|
|
30
|
+
readUnknownFields: true
|
|
31
|
+
});
|
|
32
|
+
if (asJson) {
|
|
33
|
+
return bomObject.toJson({ emitDefaultValues: true });
|
|
34
|
+
}
|
|
35
|
+
return bomObject;
|
|
36
|
+
};
|
package/protobom.test.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { expect, test } from "@jest/globals";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { existsSync, rmSync, mkdtempSync, readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
import { writeBinary, readBinary } from "./protobom.js";
|
|
7
|
+
|
|
8
|
+
const tempDir = mkdtempSync(join(tmpdir(), "bin-tests-"));
|
|
9
|
+
const testBom = JSON.parse(
|
|
10
|
+
readFileSync("./test/data/bom-java.json", { encoding: "utf-8" })
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
test("proto binary tests", async () => {
|
|
14
|
+
const binFile = join(tempDir, "test.cdx.bin");
|
|
15
|
+
writeBinary({}, binFile);
|
|
16
|
+
expect(existsSync(binFile)).toBeTruthy();
|
|
17
|
+
writeBinary(testBom, binFile);
|
|
18
|
+
expect(existsSync(binFile)).toBeTruthy();
|
|
19
|
+
let bomObject = readBinary(binFile);
|
|
20
|
+
expect(bomObject).toBeDefined();
|
|
21
|
+
expect(bomObject.serialNumber).toEqual(
|
|
22
|
+
"urn:uuid:cc8b5a04-2698-4375-b04c-cedfa4317fee"
|
|
23
|
+
);
|
|
24
|
+
bomObject = readBinary(binFile, false);
|
|
25
|
+
expect(bomObject).toBeDefined();
|
|
26
|
+
expect(bomObject.serialNumber).toEqual(
|
|
27
|
+
"urn:uuid:cc8b5a04-2698-4375-b04c-cedfa4317fee"
|
|
28
|
+
);
|
|
29
|
+
if (tempDir && tempDir.startsWith(tmpdir()) && rmSync) {
|
|
30
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
31
|
+
}
|
|
32
|
+
});
|
package/utils.js
CHANGED
|
@@ -666,6 +666,12 @@ export const parsePkgLock = async (pkgLockFile, options = {}) => {
|
|
|
666
666
|
purl: purlString,
|
|
667
667
|
"bom-ref": decodeURIComponent(purlString)
|
|
668
668
|
};
|
|
669
|
+
if (node.resolved) {
|
|
670
|
+
pkg.properties.push({
|
|
671
|
+
name: "ResolvedUrl",
|
|
672
|
+
value: node.resolved
|
|
673
|
+
});
|
|
674
|
+
}
|
|
669
675
|
}
|
|
670
676
|
const packageLicense = node.package.license;
|
|
671
677
|
if (packageLicense) {
|
|
@@ -694,8 +700,9 @@ export const parsePkgLock = async (pkgLockFile, options = {}) => {
|
|
|
694
700
|
null
|
|
695
701
|
).toString()
|
|
696
702
|
);
|
|
697
|
-
|
|
698
|
-
|
|
703
|
+
if (decodeURIComponent(purlString) !== depWorkspacePurlString) {
|
|
704
|
+
workspaceDependsOn.push(depWorkspacePurlString);
|
|
705
|
+
}
|
|
699
706
|
}
|
|
700
707
|
}
|
|
701
708
|
|
|
@@ -726,7 +733,9 @@ export const parsePkgLock = async (pkgLockFile, options = {}) => {
|
|
|
726
733
|
null
|
|
727
734
|
).toString()
|
|
728
735
|
);
|
|
729
|
-
|
|
736
|
+
if (decodeURIComponent(purlString) !== depChildString) {
|
|
737
|
+
childrenDependsOn.push(depChildString);
|
|
738
|
+
}
|
|
730
739
|
}
|
|
731
740
|
}
|
|
732
741
|
|
|
@@ -735,31 +744,35 @@ export const parsePkgLock = async (pkgLockFile, options = {}) => {
|
|
|
735
744
|
for (const edge of node.edgesOut.values()) {
|
|
736
745
|
let targetVersion;
|
|
737
746
|
let targetName;
|
|
738
|
-
|
|
747
|
+
let foundMatch = false;
|
|
739
748
|
// if the edge doesn't have an integrity, it's likely a peer dependency
|
|
740
749
|
// which isn't installed
|
|
741
|
-
|
|
742
|
-
//
|
|
743
|
-
|
|
750
|
+
// Bug #795. At times, npm loses the integrity node completely and such packages are getting missed out
|
|
751
|
+
// To keep things safe, we include these packages.
|
|
752
|
+
let edgeToIntegrity = edge.to ? edge.to.integrity : undefined;
|
|
744
753
|
if (!edgeToIntegrity) {
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
754
|
+
// This hack is required to fix the package name
|
|
755
|
+
targetName = node.name.replace(/-cjs$/, "");
|
|
756
|
+
targetVersion = node.version;
|
|
757
|
+
foundMatch = true;
|
|
758
|
+
} else {
|
|
759
|
+
// the edges don't actually contain a version, so we need to search the root node
|
|
760
|
+
// children to find the correct version. we check the node children first, then
|
|
761
|
+
// we check the root node children
|
|
762
|
+
for (const child of node.children) {
|
|
763
|
+
if (edgeToIntegrity) {
|
|
764
|
+
if (child[1].integrity == edgeToIntegrity) {
|
|
765
|
+
targetName = child[0].replace(/node_modules\//g, "");
|
|
766
|
+
// The package name could be different from the targetName retrieved
|
|
767
|
+
// Eg: "string-width-cjs": "npm:string-width@^4.2.0",
|
|
768
|
+
if (child[1].packageName && child[1].packageName !== targetName) {
|
|
769
|
+
targetName = child[1].packageName;
|
|
770
|
+
}
|
|
771
|
+
targetVersion = child[1].version;
|
|
772
|
+
foundMatch = true;
|
|
773
|
+
break;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
763
776
|
}
|
|
764
777
|
}
|
|
765
778
|
if (!foundMatch) {
|
|
@@ -792,8 +805,12 @@ export const parsePkgLock = async (pkgLockFile, options = {}) => {
|
|
|
792
805
|
null
|
|
793
806
|
).toString()
|
|
794
807
|
);
|
|
795
|
-
|
|
796
|
-
|
|
808
|
+
if (decodeURIComponent(purlString) !== depPurlString) {
|
|
809
|
+
pkgDependsOn.push(depPurlString);
|
|
810
|
+
}
|
|
811
|
+
if (edge.to == null) {
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
797
814
|
const { pkgList: childPkgList, dependenciesList: childDependenciesList } =
|
|
798
815
|
parseArboristNode(
|
|
799
816
|
edge.to,
|
|
@@ -4042,6 +4059,9 @@ export const parseGemfileLockData = async function (gemLockData) {
|
|
|
4042
4059
|
const tmpA = l.split(" ");
|
|
4043
4060
|
if (tmpA && tmpA.length == 2) {
|
|
4044
4061
|
const name = tmpA[0];
|
|
4062
|
+
if (name === "remote:") {
|
|
4063
|
+
return;
|
|
4064
|
+
}
|
|
4045
4065
|
if (!pkgnames[name]) {
|
|
4046
4066
|
let version = tmpA[1].split(", ")[0];
|
|
4047
4067
|
version = version.replace(/[(>=<)~ ]/g, "");
|
|
@@ -4060,7 +4080,8 @@ export const parseGemfileLockData = async function (gemLockData) {
|
|
|
4060
4080
|
l === "PLATFORMS" ||
|
|
4061
4081
|
l === "DEPENDENCIES" ||
|
|
4062
4082
|
l === "RUBY VERSION" ||
|
|
4063
|
-
l === "BUNDLED WITH"
|
|
4083
|
+
l === "BUNDLED WITH" ||
|
|
4084
|
+
l === "PATH"
|
|
4064
4085
|
) {
|
|
4065
4086
|
specsFound = false;
|
|
4066
4087
|
}
|
|
@@ -5577,7 +5598,7 @@ export const parseCsPkgLockData = async function (csLockData) {
|
|
|
5577
5598
|
return pkgList;
|
|
5578
5599
|
};
|
|
5579
5600
|
|
|
5580
|
-
export const parsePaketLockData = async function (paketLockData) {
|
|
5601
|
+
export const parsePaketLockData = async function (paketLockData, pkgLockFile) {
|
|
5581
5602
|
const pkgList = [];
|
|
5582
5603
|
const dependenciesList = [];
|
|
5583
5604
|
const dependenciesMap = {};
|
|
@@ -5605,14 +5626,39 @@ export const parsePaketLockData = async function (paketLockData) {
|
|
|
5605
5626
|
if (match) {
|
|
5606
5627
|
const name = match[1];
|
|
5607
5628
|
const version = match[2];
|
|
5608
|
-
const purl =
|
|
5609
|
-
|
|
5610
|
-
|
|
5629
|
+
const purl = new PackageURL(
|
|
5630
|
+
"nuget",
|
|
5631
|
+
"",
|
|
5632
|
+
name,
|
|
5633
|
+
version,
|
|
5634
|
+
null,
|
|
5635
|
+
null
|
|
5636
|
+
).toString();
|
|
5611
5637
|
pkg = {
|
|
5612
5638
|
group: "",
|
|
5613
|
-
name
|
|
5614
|
-
version
|
|
5615
|
-
purl
|
|
5639
|
+
name,
|
|
5640
|
+
version,
|
|
5641
|
+
purl,
|
|
5642
|
+
"bom-ref": decodeURIComponent(purl),
|
|
5643
|
+
properties: [
|
|
5644
|
+
{
|
|
5645
|
+
name: "SrcFile",
|
|
5646
|
+
value: pkgLockFile
|
|
5647
|
+
}
|
|
5648
|
+
],
|
|
5649
|
+
evidence: {
|
|
5650
|
+
identity: {
|
|
5651
|
+
field: "purl",
|
|
5652
|
+
confidence: 1,
|
|
5653
|
+
methods: [
|
|
5654
|
+
{
|
|
5655
|
+
technique: "manifest-analysis",
|
|
5656
|
+
confidence: 1,
|
|
5657
|
+
value: pkgLockFile
|
|
5658
|
+
}
|
|
5659
|
+
]
|
|
5660
|
+
}
|
|
5661
|
+
}
|
|
5616
5662
|
};
|
|
5617
5663
|
pkgList.push(pkg);
|
|
5618
5664
|
dependenciesMap[purl] = new Set();
|
|
@@ -5664,13 +5710,25 @@ export const parsePaketLockData = async function (paketLockData) {
|
|
|
5664
5710
|
dependenciesList
|
|
5665
5711
|
};
|
|
5666
5712
|
};
|
|
5713
|
+
|
|
5667
5714
|
/**
|
|
5668
5715
|
* Parse composer lock file
|
|
5669
5716
|
*
|
|
5670
5717
|
* @param {string} pkgLockFile composer.lock file
|
|
5718
|
+
* @param {array} rootRequires require section from composer.json
|
|
5671
5719
|
*/
|
|
5672
|
-
export const parseComposerLock = function (pkgLockFile) {
|
|
5720
|
+
export const parseComposerLock = function (pkgLockFile, rootRequires) {
|
|
5673
5721
|
const pkgList = [];
|
|
5722
|
+
const dependenciesList = [];
|
|
5723
|
+
const dependenciesMap = {};
|
|
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
|
+
}
|
|
5674
5732
|
if (existsSync(pkgLockFile)) {
|
|
5675
5733
|
let lockData = {};
|
|
5676
5734
|
try {
|
|
@@ -5687,6 +5745,7 @@ export const parseComposerLock = function (pkgLockFile) {
|
|
|
5687
5745
|
if (lockData["packages-dev"]) {
|
|
5688
5746
|
packages["optional"] = lockData["packages-dev"];
|
|
5689
5747
|
}
|
|
5748
|
+
// Pass 1: Collect all packages
|
|
5690
5749
|
for (const compScope in packages) {
|
|
5691
5750
|
for (const i in packages[compScope]) {
|
|
5692
5751
|
const pkg = packages[compScope][i];
|
|
@@ -5694,19 +5753,26 @@ export const parseComposerLock = function (pkgLockFile) {
|
|
|
5694
5753
|
if (!pkg || !pkg.name || !pkg.version) {
|
|
5695
5754
|
continue;
|
|
5696
5755
|
}
|
|
5756
|
+
|
|
5697
5757
|
let group = dirname(pkg.name);
|
|
5698
5758
|
if (group === ".") {
|
|
5699
5759
|
group = "";
|
|
5700
5760
|
}
|
|
5701
5761
|
const name = basename(pkg.name);
|
|
5702
|
-
|
|
5762
|
+
const purl = new PackageURL(
|
|
5763
|
+
"composer",
|
|
5764
|
+
group,
|
|
5765
|
+
name,
|
|
5766
|
+
pkg.version,
|
|
5767
|
+
null,
|
|
5768
|
+
null
|
|
5769
|
+
).toString();
|
|
5770
|
+
const apkg = {
|
|
5703
5771
|
group: group,
|
|
5704
5772
|
name: name,
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
// which has it's own workaround. Or when the 231 bug is fixed.
|
|
5709
|
-
version: pkg.version.replace(/^v/, ""),
|
|
5773
|
+
purl,
|
|
5774
|
+
"bom-ref": decodeURIComponent(purl),
|
|
5775
|
+
version: pkg.version,
|
|
5710
5776
|
repository: pkg.source,
|
|
5711
5777
|
license: pkg.license,
|
|
5712
5778
|
description: pkg.description,
|
|
@@ -5730,12 +5796,63 @@ export const parseComposerLock = function (pkgLockFile) {
|
|
|
5730
5796
|
]
|
|
5731
5797
|
}
|
|
5732
5798
|
}
|
|
5733
|
-
}
|
|
5799
|
+
};
|
|
5800
|
+
if (pkg.autoload && Object.keys(pkg.autoload).length) {
|
|
5801
|
+
const namespaces = [];
|
|
5802
|
+
for (const aaload of Object.keys(pkg.autoload)) {
|
|
5803
|
+
if (aaload.startsWith("psr")) {
|
|
5804
|
+
for (const ans of Object.keys(pkg.autoload[aaload])) {
|
|
5805
|
+
namespaces.push(ans.trim());
|
|
5806
|
+
}
|
|
5807
|
+
}
|
|
5808
|
+
}
|
|
5809
|
+
if (namespaces.length) {
|
|
5810
|
+
apkg.properties.push({
|
|
5811
|
+
name: "Namespaces",
|
|
5812
|
+
value: namespaces.join(", ")
|
|
5813
|
+
});
|
|
5814
|
+
}
|
|
5815
|
+
}
|
|
5816
|
+
pkgList.push(apkg);
|
|
5817
|
+
dependenciesMap[purl] = new Set();
|
|
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
|
+
}
|
|
5823
|
+
}
|
|
5824
|
+
}
|
|
5825
|
+
// Pass 2: Construct dependency tree
|
|
5826
|
+
for (const compScope in packages) {
|
|
5827
|
+
for (const i in packages[compScope]) {
|
|
5828
|
+
const pkg = packages[compScope][i];
|
|
5829
|
+
if (!pkg || !pkg.name || !pkg.version) {
|
|
5830
|
+
continue;
|
|
5831
|
+
}
|
|
5832
|
+
if (!pkg.require || !Object.keys(pkg.require).length) {
|
|
5833
|
+
continue;
|
|
5834
|
+
}
|
|
5835
|
+
const purl = pkgNamePurlMap[pkg.name];
|
|
5836
|
+
for (const adepName of Object.keys(pkg.require)) {
|
|
5837
|
+
if (pkgNamePurlMap[adepName]) {
|
|
5838
|
+
dependenciesMap[purl].add(pkgNamePurlMap[adepName]);
|
|
5839
|
+
}
|
|
5840
|
+
}
|
|
5734
5841
|
}
|
|
5735
5842
|
}
|
|
5736
5843
|
}
|
|
5737
5844
|
}
|
|
5738
|
-
|
|
5845
|
+
for (const ref in dependenciesMap) {
|
|
5846
|
+
dependenciesList.push({
|
|
5847
|
+
ref: ref,
|
|
5848
|
+
dependsOn: Array.from(dependenciesMap[ref])
|
|
5849
|
+
});
|
|
5850
|
+
}
|
|
5851
|
+
return {
|
|
5852
|
+
pkgList,
|
|
5853
|
+
dependenciesList,
|
|
5854
|
+
rootList
|
|
5855
|
+
};
|
|
5739
5856
|
};
|
|
5740
5857
|
|
|
5741
5858
|
export const parseSbtTree = (sbtTreeFile) => {
|
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",
|
|
@@ -1351,14 +1351,30 @@ test("parse paket.lock", async () => {
|
|
|
1351
1351
|
dependenciesList: []
|
|
1352
1352
|
});
|
|
1353
1353
|
const dep_list = await parsePaketLockData(
|
|
1354
|
-
readFileSync("./test/data/paket.lock", { encoding: "utf-8" })
|
|
1354
|
+
readFileSync("./test/data/paket.lock", { encoding: "utf-8" }),
|
|
1355
|
+
"./test/data/paket.lock"
|
|
1355
1356
|
);
|
|
1356
1357
|
expect(dep_list.pkgList.length).toEqual(13);
|
|
1357
1358
|
expect(dep_list.pkgList[0]).toEqual({
|
|
1358
1359
|
group: "",
|
|
1359
1360
|
name: "0x53A.ReferenceAssemblies.Paket",
|
|
1360
1361
|
version: "0.2",
|
|
1361
|
-
purl: "pkg:nuget/0x53A.ReferenceAssemblies.Paket@0.2"
|
|
1362
|
+
purl: "pkg:nuget/0x53A.ReferenceAssemblies.Paket@0.2",
|
|
1363
|
+
"bom-ref": "pkg:nuget/0x53A.ReferenceAssemblies.Paket@0.2",
|
|
1364
|
+
properties: [{ name: "SrcFile", value: "./test/data/paket.lock" }],
|
|
1365
|
+
evidence: {
|
|
1366
|
+
identity: {
|
|
1367
|
+
field: "purl",
|
|
1368
|
+
confidence: 1,
|
|
1369
|
+
methods: [
|
|
1370
|
+
{
|
|
1371
|
+
technique: "manifest-analysis",
|
|
1372
|
+
confidence: 1,
|
|
1373
|
+
value: "./test/data/paket.lock"
|
|
1374
|
+
}
|
|
1375
|
+
]
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1362
1378
|
});
|
|
1363
1379
|
expect(dep_list.dependenciesList.length).toEqual(13);
|
|
1364
1380
|
expect(dep_list.dependenciesList[2]).toEqual({
|
|
@@ -1694,7 +1710,7 @@ test("parsePkgLock v2 workspace", async () => {
|
|
|
1694
1710
|
);
|
|
1695
1711
|
let pkgs = parsedList.pkgList;
|
|
1696
1712
|
let deps = parsedList.dependenciesList;
|
|
1697
|
-
expect(pkgs.length).toEqual(
|
|
1713
|
+
expect(pkgs.length).toEqual(1034);
|
|
1698
1714
|
expect(pkgs[0].license).toEqual("MIT");
|
|
1699
1715
|
let hasAppWorkspacePkg = pkgs.some(
|
|
1700
1716
|
(obj) => obj["bom-ref"] === "pkg:npm/app@0.0.0"
|
|
@@ -1746,8 +1762,8 @@ test("parsePkgLock v3", async () => {
|
|
|
1746
1762
|
projectName: "cdxgen"
|
|
1747
1763
|
});
|
|
1748
1764
|
deps = parsedList.pkgList;
|
|
1749
|
-
expect(deps.length).toEqual(
|
|
1750
|
-
expect(parsedList.dependenciesList.length).toEqual(
|
|
1765
|
+
expect(deps.length).toEqual(1199);
|
|
1766
|
+
expect(parsedList.dependenciesList.length).toEqual(1199);
|
|
1751
1767
|
});
|
|
1752
1768
|
|
|
1753
1769
|
test("parseBowerJson", async () => {
|
|
@@ -2315,13 +2331,16 @@ test("parseYarnLock", async () => {
|
|
|
2315
2331
|
});
|
|
2316
2332
|
|
|
2317
2333
|
test("parseComposerLock", () => {
|
|
2318
|
-
let
|
|
2319
|
-
expect(
|
|
2320
|
-
expect(
|
|
2334
|
+
let retMap = parseComposerLock("./test/data/composer.lock");
|
|
2335
|
+
expect(retMap.pkgList.length).toEqual(1);
|
|
2336
|
+
expect(retMap.dependenciesList.length).toEqual(1);
|
|
2337
|
+
expect(retMap.pkgList[0]).toEqual({
|
|
2321
2338
|
group: "quickbooks",
|
|
2322
2339
|
name: "v3-php-sdk",
|
|
2323
2340
|
scope: "required",
|
|
2324
|
-
version: "
|
|
2341
|
+
version: "v4.0.6.1",
|
|
2342
|
+
purl: "pkg:composer/quickbooks/v3-php-sdk@v4.0.6.1",
|
|
2343
|
+
"bom-ref": "pkg:composer/quickbooks/v3-php-sdk@v4.0.6.1",
|
|
2325
2344
|
repository: {
|
|
2326
2345
|
type: "git",
|
|
2327
2346
|
url: "https://github.com/intuit/QuickBooks-V3-PHP-SDK.git",
|
|
@@ -2333,6 +2352,10 @@ test("parseComposerLock", () => {
|
|
|
2333
2352
|
{
|
|
2334
2353
|
name: "SrcFile",
|
|
2335
2354
|
value: "./test/data/composer.lock"
|
|
2355
|
+
},
|
|
2356
|
+
{
|
|
2357
|
+
name: "Namespaces",
|
|
2358
|
+
value: "QuickBooksOnline\\API\\"
|
|
2336
2359
|
}
|
|
2337
2360
|
],
|
|
2338
2361
|
evidence: {
|
|
@@ -2350,13 +2373,16 @@ test("parseComposerLock", () => {
|
|
|
2350
2373
|
}
|
|
2351
2374
|
});
|
|
2352
2375
|
|
|
2353
|
-
|
|
2354
|
-
expect(
|
|
2355
|
-
expect(
|
|
2376
|
+
retMap = parseComposerLock("./test/data/composer-2.lock");
|
|
2377
|
+
expect(retMap.pkgList.length).toEqual(73);
|
|
2378
|
+
expect(retMap.dependenciesList.length).toEqual(73);
|
|
2379
|
+
expect(retMap.pkgList[0]).toEqual({
|
|
2356
2380
|
group: "amphp",
|
|
2357
2381
|
name: "amp",
|
|
2358
2382
|
scope: "required",
|
|
2359
|
-
version: "
|
|
2383
|
+
version: "v2.4.4",
|
|
2384
|
+
purl: "pkg:composer/amphp/amp@v2.4.4",
|
|
2385
|
+
"bom-ref": "pkg:composer/amphp/amp@v2.4.4",
|
|
2360
2386
|
repository: {
|
|
2361
2387
|
type: "git",
|
|
2362
2388
|
url: "https://github.com/amphp/amp.git",
|
|
@@ -2368,6 +2394,10 @@ test("parseComposerLock", () => {
|
|
|
2368
2394
|
{
|
|
2369
2395
|
name: "SrcFile",
|
|
2370
2396
|
value: "./test/data/composer-2.lock"
|
|
2397
|
+
},
|
|
2398
|
+
{
|
|
2399
|
+
name: "Namespaces",
|
|
2400
|
+
value: "Amp\\"
|
|
2371
2401
|
}
|
|
2372
2402
|
],
|
|
2373
2403
|
evidence: {
|
|
@@ -2385,12 +2415,15 @@ test("parseComposerLock", () => {
|
|
|
2385
2415
|
}
|
|
2386
2416
|
});
|
|
2387
2417
|
|
|
2388
|
-
|
|
2389
|
-
expect(
|
|
2390
|
-
expect(
|
|
2418
|
+
retMap = parseComposerLock("./test/data/composer-3.lock");
|
|
2419
|
+
expect(retMap.pkgList.length).toEqual(62);
|
|
2420
|
+
expect(retMap.dependenciesList.length).toEqual(62);
|
|
2421
|
+
expect(retMap.pkgList[0]).toEqual({
|
|
2391
2422
|
group: "amphp",
|
|
2392
2423
|
name: "amp",
|
|
2393
|
-
version: "
|
|
2424
|
+
version: "v2.6.2",
|
|
2425
|
+
purl: "pkg:composer/amphp/amp@v2.6.2",
|
|
2426
|
+
"bom-ref": "pkg:composer/amphp/amp@v2.6.2",
|
|
2394
2427
|
repository: {
|
|
2395
2428
|
type: "git",
|
|
2396
2429
|
url: "https://github.com/amphp/amp.git",
|
|
@@ -2399,7 +2432,13 @@ test("parseComposerLock", () => {
|
|
|
2399
2432
|
license: ["MIT"],
|
|
2400
2433
|
description: "A non-blocking concurrency framework for PHP applications.",
|
|
2401
2434
|
scope: "required",
|
|
2402
|
-
properties: [
|
|
2435
|
+
properties: [
|
|
2436
|
+
{ name: "SrcFile", value: "./test/data/composer-3.lock" },
|
|
2437
|
+
{
|
|
2438
|
+
name: "Namespaces",
|
|
2439
|
+
value: "Amp\\"
|
|
2440
|
+
}
|
|
2441
|
+
],
|
|
2403
2442
|
evidence: {
|
|
2404
2443
|
identity: {
|
|
2405
2444
|
field: "purl",
|
|
@@ -2414,6 +2453,42 @@ test("parseComposerLock", () => {
|
|
|
2414
2453
|
}
|
|
2415
2454
|
}
|
|
2416
2455
|
});
|
|
2456
|
+
retMap = parseComposerLock("./test/data/composer-4.lock");
|
|
2457
|
+
expect(retMap.pkgList.length).toEqual(50);
|
|
2458
|
+
expect(retMap.dependenciesList.length).toEqual(50);
|
|
2459
|
+
expect(retMap.pkgList[0]).toEqual({
|
|
2460
|
+
group: "apache",
|
|
2461
|
+
name: "log4php",
|
|
2462
|
+
purl: "pkg:composer/apache/log4php@2.3.0",
|
|
2463
|
+
"bom-ref": "pkg:composer/apache/log4php@2.3.0",
|
|
2464
|
+
version: "2.3.0",
|
|
2465
|
+
repository: {
|
|
2466
|
+
type: "git",
|
|
2467
|
+
url: "https://git-wip-us.apache.org/repos/asf/logging-log4php.git",
|
|
2468
|
+
reference: "8c6df2481cd68d0d211d38f700406c5f0a9de0c2"
|
|
2469
|
+
},
|
|
2470
|
+
license: ["Apache-2.0"],
|
|
2471
|
+
description: "A versatile logging framework for PHP",
|
|
2472
|
+
scope: "required",
|
|
2473
|
+
properties: [{ name: "SrcFile", value: "./test/data/composer-4.lock" }],
|
|
2474
|
+
evidence: {
|
|
2475
|
+
identity: {
|
|
2476
|
+
field: "purl",
|
|
2477
|
+
confidence: 1,
|
|
2478
|
+
methods: [
|
|
2479
|
+
{
|
|
2480
|
+
confidence: 1,
|
|
2481
|
+
technique: "manifest-analysis",
|
|
2482
|
+
value: "./test/data/composer-4.lock"
|
|
2483
|
+
}
|
|
2484
|
+
]
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
});
|
|
2488
|
+
expect(retMap.dependenciesList[1]).toEqual({
|
|
2489
|
+
ref: "pkg:composer/doctrine/annotations@v1.2.1",
|
|
2490
|
+
dependsOn: ["pkg:composer/doctrine/lexer@v1.0"]
|
|
2491
|
+
});
|
|
2417
2492
|
});
|
|
2418
2493
|
|
|
2419
2494
|
test("parseGemfileLockData", async () => {
|