@cyclonedx/cdxgen 9.10.1 → 9.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/bin/cdxgen.js +16 -1
- package/index.js +46 -5
- package/package.json +5 -4
- package/protobom.js +36 -0
- package/protobom.test.js +32 -0
- package/utils.js +141 -42
- package/utils.test.js +93 -18
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/index.js
CHANGED
|
@@ -4048,6 +4048,8 @@ 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",
|
|
@@ -4117,17 +4119,56 @@ export const createPHPBom = (path, options) => {
|
|
|
4117
4119
|
);
|
|
4118
4120
|
if (composerLockFiles.length) {
|
|
4119
4121
|
for (const f of composerLockFiles) {
|
|
4122
|
+
const basePath = dirname(f);
|
|
4120
4123
|
if (DEBUG_MODE) {
|
|
4121
4124
|
console.log(`Parsing ${f}`);
|
|
4122
4125
|
}
|
|
4123
|
-
|
|
4124
|
-
if (
|
|
4125
|
-
|
|
4126
|
+
// Is there a composer.json to find the parent component
|
|
4127
|
+
if (
|
|
4128
|
+
!Object.keys(parentComponent).length &&
|
|
4129
|
+
existsSync(join(basePath, "composer.json"))
|
|
4130
|
+
) {
|
|
4131
|
+
const composerData = JSON.parse(
|
|
4132
|
+
readFileSync(join(basePath, "composer.json"), { encoding: "utf-8" })
|
|
4133
|
+
);
|
|
4134
|
+
const pkgName = composerData.name;
|
|
4135
|
+
if (pkgName) {
|
|
4136
|
+
parentComponent.group = dirname(pkgName);
|
|
4137
|
+
if (parentComponent.group === ".") {
|
|
4138
|
+
parentComponent.group = "";
|
|
4139
|
+
}
|
|
4140
|
+
parentComponent.name = basename(pkgName);
|
|
4141
|
+
parentComponent.type = "application";
|
|
4142
|
+
parentComponent.version = composerData.version || "latest";
|
|
4143
|
+
parentComponent["bom-ref"] = decodeURIComponent(
|
|
4144
|
+
new PackageURL(
|
|
4145
|
+
"composer",
|
|
4146
|
+
parentComponent.group,
|
|
4147
|
+
parentComponent.name,
|
|
4148
|
+
parentComponent.version,
|
|
4149
|
+
null,
|
|
4150
|
+
null
|
|
4151
|
+
).toString()
|
|
4152
|
+
);
|
|
4153
|
+
}
|
|
4154
|
+
}
|
|
4155
|
+
const retMap = parseComposerLock(f);
|
|
4156
|
+
if (retMap.pkgList && retMap.pkgList.length) {
|
|
4157
|
+
pkgList = pkgList.concat(retMap.pkgList);
|
|
4158
|
+
}
|
|
4159
|
+
if (retMap.dependenciesList) {
|
|
4160
|
+
dependencies = mergeDependencies(
|
|
4161
|
+
dependencies,
|
|
4162
|
+
retMap.dependenciesList,
|
|
4163
|
+
parentComponent
|
|
4164
|
+
);
|
|
4126
4165
|
}
|
|
4127
4166
|
}
|
|
4128
4167
|
return buildBomNSData(options, pkgList, "composer", {
|
|
4129
4168
|
src: path,
|
|
4130
|
-
filename: composerLockFiles.join(", ")
|
|
4169
|
+
filename: composerLockFiles.join(", "),
|
|
4170
|
+
dependencies,
|
|
4171
|
+
parentComponent
|
|
4131
4172
|
});
|
|
4132
4173
|
}
|
|
4133
4174
|
return {};
|
|
@@ -4331,7 +4372,7 @@ export const createCsharpBom = async (
|
|
|
4331
4372
|
console.log(`Parsing ${f}`);
|
|
4332
4373
|
}
|
|
4333
4374
|
pkgData = readFileSync(f, { encoding: "utf-8" });
|
|
4334
|
-
const results = await parsePaketLockData(pkgData);
|
|
4375
|
+
const results = await parsePaketLockData(pkgData, f);
|
|
4335
4376
|
const dlist = results.pkgList;
|
|
4336
4377
|
const deps = results.dependenciesList;
|
|
4337
4378
|
if (dlist && dlist.length) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyclonedx/cdxgen",
|
|
3
|
-
"version": "9.10.
|
|
3
|
+
"version": "9.10.2",
|
|
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>",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@babel/parser": "^7.23.6",
|
|
59
59
|
"@babel/traverse": "^7.23.6",
|
|
60
|
-
"@npmcli/arborist": "7.2.
|
|
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.0",
|
|
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",
|
|
@@ -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,
|
|
@@ -5577,7 +5594,7 @@ export const parseCsPkgLockData = async function (csLockData) {
|
|
|
5577
5594
|
return pkgList;
|
|
5578
5595
|
};
|
|
5579
5596
|
|
|
5580
|
-
export const parsePaketLockData = async function (paketLockData) {
|
|
5597
|
+
export const parsePaketLockData = async function (paketLockData, pkgLockFile) {
|
|
5581
5598
|
const pkgList = [];
|
|
5582
5599
|
const dependenciesList = [];
|
|
5583
5600
|
const dependenciesMap = {};
|
|
@@ -5605,14 +5622,39 @@ export const parsePaketLockData = async function (paketLockData) {
|
|
|
5605
5622
|
if (match) {
|
|
5606
5623
|
const name = match[1];
|
|
5607
5624
|
const version = match[2];
|
|
5608
|
-
const purl =
|
|
5609
|
-
|
|
5610
|
-
|
|
5625
|
+
const purl = new PackageURL(
|
|
5626
|
+
"nuget",
|
|
5627
|
+
"",
|
|
5628
|
+
name,
|
|
5629
|
+
version,
|
|
5630
|
+
null,
|
|
5631
|
+
null
|
|
5632
|
+
).toString();
|
|
5611
5633
|
pkg = {
|
|
5612
5634
|
group: "",
|
|
5613
|
-
name
|
|
5614
|
-
version
|
|
5615
|
-
purl
|
|
5635
|
+
name,
|
|
5636
|
+
version,
|
|
5637
|
+
purl,
|
|
5638
|
+
"bom-ref": decodeURIComponent(purl),
|
|
5639
|
+
properties: [
|
|
5640
|
+
{
|
|
5641
|
+
name: "SrcFile",
|
|
5642
|
+
value: pkgLockFile
|
|
5643
|
+
}
|
|
5644
|
+
],
|
|
5645
|
+
evidence: {
|
|
5646
|
+
identity: {
|
|
5647
|
+
field: "purl",
|
|
5648
|
+
confidence: 1,
|
|
5649
|
+
methods: [
|
|
5650
|
+
{
|
|
5651
|
+
technique: "manifest-analysis",
|
|
5652
|
+
confidence: 1,
|
|
5653
|
+
value: pkgLockFile
|
|
5654
|
+
}
|
|
5655
|
+
]
|
|
5656
|
+
}
|
|
5657
|
+
}
|
|
5616
5658
|
};
|
|
5617
5659
|
pkgList.push(pkg);
|
|
5618
5660
|
dependenciesMap[purl] = new Set();
|
|
@@ -5664,6 +5706,7 @@ export const parsePaketLockData = async function (paketLockData) {
|
|
|
5664
5706
|
dependenciesList
|
|
5665
5707
|
};
|
|
5666
5708
|
};
|
|
5709
|
+
|
|
5667
5710
|
/**
|
|
5668
5711
|
* Parse composer lock file
|
|
5669
5712
|
*
|
|
@@ -5671,6 +5714,9 @@ export const parsePaketLockData = async function (paketLockData) {
|
|
|
5671
5714
|
*/
|
|
5672
5715
|
export const parseComposerLock = function (pkgLockFile) {
|
|
5673
5716
|
const pkgList = [];
|
|
5717
|
+
const dependenciesList = [];
|
|
5718
|
+
const dependenciesMap = {};
|
|
5719
|
+
const pkgNamePurlMap = {};
|
|
5674
5720
|
if (existsSync(pkgLockFile)) {
|
|
5675
5721
|
let lockData = {};
|
|
5676
5722
|
try {
|
|
@@ -5687,6 +5733,7 @@ export const parseComposerLock = function (pkgLockFile) {
|
|
|
5687
5733
|
if (lockData["packages-dev"]) {
|
|
5688
5734
|
packages["optional"] = lockData["packages-dev"];
|
|
5689
5735
|
}
|
|
5736
|
+
// Pass 1: Collect all packages
|
|
5690
5737
|
for (const compScope in packages) {
|
|
5691
5738
|
for (const i in packages[compScope]) {
|
|
5692
5739
|
const pkg = packages[compScope][i];
|
|
@@ -5699,14 +5746,20 @@ export const parseComposerLock = function (pkgLockFile) {
|
|
|
5699
5746
|
group = "";
|
|
5700
5747
|
}
|
|
5701
5748
|
const name = basename(pkg.name);
|
|
5702
|
-
|
|
5749
|
+
const purl = new PackageURL(
|
|
5750
|
+
"composer",
|
|
5751
|
+
group,
|
|
5752
|
+
name,
|
|
5753
|
+
pkg.version,
|
|
5754
|
+
null,
|
|
5755
|
+
null
|
|
5756
|
+
).toString();
|
|
5757
|
+
const apkg = {
|
|
5703
5758
|
group: group,
|
|
5704
5759
|
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/, ""),
|
|
5760
|
+
purl,
|
|
5761
|
+
"bom-ref": decodeURIComponent(purl),
|
|
5762
|
+
version: pkg.version,
|
|
5710
5763
|
repository: pkg.source,
|
|
5711
5764
|
license: pkg.license,
|
|
5712
5765
|
description: pkg.description,
|
|
@@ -5730,12 +5783,58 @@ export const parseComposerLock = function (pkgLockFile) {
|
|
|
5730
5783
|
]
|
|
5731
5784
|
}
|
|
5732
5785
|
}
|
|
5733
|
-
}
|
|
5786
|
+
};
|
|
5787
|
+
if (pkg.autoload && Object.keys(pkg.autoload).length) {
|
|
5788
|
+
const namespaces = [];
|
|
5789
|
+
for (const aaload of Object.keys(pkg.autoload)) {
|
|
5790
|
+
if (aaload.startsWith("psr")) {
|
|
5791
|
+
for (const ans of Object.keys(pkg.autoload[aaload])) {
|
|
5792
|
+
namespaces.push(ans);
|
|
5793
|
+
}
|
|
5794
|
+
}
|
|
5795
|
+
}
|
|
5796
|
+
if (namespaces.length) {
|
|
5797
|
+
apkg.properties.push({
|
|
5798
|
+
name: "Namespaces",
|
|
5799
|
+
value: namespaces.join(", ")
|
|
5800
|
+
});
|
|
5801
|
+
}
|
|
5802
|
+
}
|
|
5803
|
+
pkgList.push(apkg);
|
|
5804
|
+
dependenciesMap[purl] = new Set();
|
|
5805
|
+
pkgNamePurlMap[pkg.name] = purl;
|
|
5806
|
+
}
|
|
5807
|
+
}
|
|
5808
|
+
// Pass 2: Construct dependency tree
|
|
5809
|
+
for (const compScope in packages) {
|
|
5810
|
+
for (const i in packages[compScope]) {
|
|
5811
|
+
const pkg = packages[compScope][i];
|
|
5812
|
+
if (!pkg || !pkg.name || !pkg.version) {
|
|
5813
|
+
continue;
|
|
5814
|
+
}
|
|
5815
|
+
if (!pkg.require || !Object.keys(pkg.require).length) {
|
|
5816
|
+
continue;
|
|
5817
|
+
}
|
|
5818
|
+
const purl = pkgNamePurlMap[pkg.name];
|
|
5819
|
+
for (const adepName of Object.keys(pkg.require)) {
|
|
5820
|
+
if (pkgNamePurlMap[adepName]) {
|
|
5821
|
+
dependenciesMap[purl].add(pkgNamePurlMap[adepName]);
|
|
5822
|
+
}
|
|
5823
|
+
}
|
|
5734
5824
|
}
|
|
5735
5825
|
}
|
|
5736
5826
|
}
|
|
5737
5827
|
}
|
|
5738
|
-
|
|
5828
|
+
for (const ref in dependenciesMap) {
|
|
5829
|
+
dependenciesList.push({
|
|
5830
|
+
ref: ref,
|
|
5831
|
+
dependsOn: Array.from(dependenciesMap[ref])
|
|
5832
|
+
});
|
|
5833
|
+
}
|
|
5834
|
+
return {
|
|
5835
|
+
pkgList,
|
|
5836
|
+
dependenciesList
|
|
5837
|
+
};
|
|
5739
5838
|
};
|
|
5740
5839
|
|
|
5741
5840
|
export const parseSbtTree = (sbtTreeFile) => {
|
package/utils.test.js
CHANGED
|
@@ -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(1195);
|
|
1766
|
+
expect(parsedList.dependenciesList.length).toEqual(1195);
|
|
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 () => {
|