@cyclonedx/cdxgen 9.10.0 → 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 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
 
@@ -358,6 +359,7 @@ cdxgen can retain the dependency tree under the `dependencies` attribute for a s
358
359
  | MVN_ARGS | Set to pass additional arguments such as profile or settings to maven |
359
360
  | MAVEN_HOME | Specify maven home |
360
361
  | MAVEN_CENTRAL_URL | Specify URL of Maven Central for metadata fetching (e.g. when private repo is used) |
362
+ | ANDROID_MAVEN_URL | Specify URL of Android Maven Repository for metadata fetching (e.g. when private repo is used) |
361
363
  | BAZEL_TARGET | Bazel target to build. Default :all (Eg: //java-maven) |
362
364
  | BAZEL_STRIP_MAVEN_PREFIX | Strip Maven group prefix (e.g. useful when private repo is used, defaults to `/maven2/`) |
363
365
  | BAZEL_USE_ACTION_GRAPH | SBOM for specific Bazel target, uses `bazel aquery 'outputs(".*.jar", deps(<BAZEL_TARGET>))'` (defaults to `false`) |
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
- const dlist = parseComposerLock(f);
4124
- if (dlist && dlist.length) {
4125
- pkgList = pkgList.concat(dlist);
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.0",
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.0",
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.7.5",
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.0.1",
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
+ };
@@ -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
- workspaceDependsOn.push(depWorkspacePurlString);
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
- childrenDependsOn.push(depChildString);
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
- let edgeToIntegrity = edge.to ? edge.to.integrity : null;
742
- // let packageName = node.packageName;
743
- // let edgeName = edge.name;
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
- continue;
746
- }
747
-
748
- // the edges don't actually contain a version, so we need to search the root node
749
- // children to find the correct version. we check the node children first, then
750
- // we check the root node children
751
- let foundMatch = false;
752
- for (const child of node.children) {
753
- if (child[1].integrity == edgeToIntegrity) {
754
- targetName = child[0].replace(/node_modules\//g, "");
755
- // The package name could be different from the targetName retrieved
756
- // Eg: "string-width-cjs": "npm:string-width@^4.2.0",
757
- if (child[1].packageName && child[1].packageName !== targetName) {
758
- targetName = child[1].packageName;
759
- }
760
- targetVersion = child[1].version;
761
- foundMatch = true;
762
- break;
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
- pkgDependsOn.push(depPurlString);
796
- if (edge.to == null) continue;
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,
@@ -1351,6 +1368,8 @@ export const parsePnpmLock = async function (pnpmLock, parentComponent = null) {
1351
1368
  group: group,
1352
1369
  name: name,
1353
1370
  version: version,
1371
+ purl: purlString,
1372
+ "bom-ref": decodeURIComponent(purlString),
1354
1373
  scope,
1355
1374
  _integrity: integrity,
1356
1375
  properties: [
@@ -2366,7 +2385,8 @@ export const guessLicenseId = function (content) {
2366
2385
  export const getMvnMetadata = async function (pkgList, jarNSMapping = {}) {
2367
2386
  const MAVEN_CENTRAL_URL =
2368
2387
  process.env.MAVEN_CENTRAL_URL || "https://repo1.maven.org/maven2/";
2369
- const ANDROID_MAVEN = "https://maven.google.com/";
2388
+ const ANDROID_MAVEN_URL =
2389
+ process.env.ANDROID_MAVEN_URL || "https://maven.google.com/";
2370
2390
  const cdepList = [];
2371
2391
  if (!pkgList || !pkgList.length) {
2372
2392
  return pkgList;
@@ -2414,7 +2434,7 @@ export const getMvnMetadata = async function (pkgList, jarNSMapping = {}) {
2414
2434
  let urlPrefix = MAVEN_CENTRAL_URL;
2415
2435
  // Ideally we should try one resolver after the other. But it increases the time taken
2416
2436
  if (group.indexOf("android") !== -1) {
2417
- urlPrefix = ANDROID_MAVEN;
2437
+ urlPrefix = ANDROID_MAVEN_URL;
2418
2438
  }
2419
2439
  // Querying maven requires a valid group name
2420
2440
  if (!group || group === "") {
@@ -5574,7 +5594,7 @@ export const parseCsPkgLockData = async function (csLockData) {
5574
5594
  return pkgList;
5575
5595
  };
5576
5596
 
5577
- export const parsePaketLockData = async function (paketLockData) {
5597
+ export const parsePaketLockData = async function (paketLockData, pkgLockFile) {
5578
5598
  const pkgList = [];
5579
5599
  const dependenciesList = [];
5580
5600
  const dependenciesMap = {};
@@ -5602,14 +5622,39 @@ export const parsePaketLockData = async function (paketLockData) {
5602
5622
  if (match) {
5603
5623
  const name = match[1];
5604
5624
  const version = match[2];
5605
- const purl = decodeURIComponent(
5606
- new PackageURL("nuget", "", name, version, null, null).toString()
5607
- );
5625
+ const purl = new PackageURL(
5626
+ "nuget",
5627
+ "",
5628
+ name,
5629
+ version,
5630
+ null,
5631
+ null
5632
+ ).toString();
5608
5633
  pkg = {
5609
5634
  group: "",
5610
- name: name,
5611
- version: version,
5612
- purl: 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
+ }
5613
5658
  };
5614
5659
  pkgList.push(pkg);
5615
5660
  dependenciesMap[purl] = new Set();
@@ -5661,6 +5706,7 @@ export const parsePaketLockData = async function (paketLockData) {
5661
5706
  dependenciesList
5662
5707
  };
5663
5708
  };
5709
+
5664
5710
  /**
5665
5711
  * Parse composer lock file
5666
5712
  *
@@ -5668,6 +5714,9 @@ export const parsePaketLockData = async function (paketLockData) {
5668
5714
  */
5669
5715
  export const parseComposerLock = function (pkgLockFile) {
5670
5716
  const pkgList = [];
5717
+ const dependenciesList = [];
5718
+ const dependenciesMap = {};
5719
+ const pkgNamePurlMap = {};
5671
5720
  if (existsSync(pkgLockFile)) {
5672
5721
  let lockData = {};
5673
5722
  try {
@@ -5684,6 +5733,7 @@ export const parseComposerLock = function (pkgLockFile) {
5684
5733
  if (lockData["packages-dev"]) {
5685
5734
  packages["optional"] = lockData["packages-dev"];
5686
5735
  }
5736
+ // Pass 1: Collect all packages
5687
5737
  for (const compScope in packages) {
5688
5738
  for (const i in packages[compScope]) {
5689
5739
  const pkg = packages[compScope][i];
@@ -5696,14 +5746,20 @@ export const parseComposerLock = function (pkgLockFile) {
5696
5746
  group = "";
5697
5747
  }
5698
5748
  const name = basename(pkg.name);
5699
- pkgList.push({
5749
+ const purl = new PackageURL(
5750
+ "composer",
5751
+ group,
5752
+ name,
5753
+ pkg.version,
5754
+ null,
5755
+ null
5756
+ ).toString();
5757
+ const apkg = {
5700
5758
  group: group,
5701
5759
  name: name,
5702
- // Remove leading v from version to work around bug
5703
- // https://github.com/OSSIndex/vulns/issues/231
5704
- // @TODO: remove workaround when DependencyTrack v4.4 is released,
5705
- // which has it's own workaround. Or when the 231 bug is fixed.
5706
- version: pkg.version.replace(/^v/, ""),
5760
+ purl,
5761
+ "bom-ref": decodeURIComponent(purl),
5762
+ version: pkg.version,
5707
5763
  repository: pkg.source,
5708
5764
  license: pkg.license,
5709
5765
  description: pkg.description,
@@ -5727,12 +5783,58 @@ export const parseComposerLock = function (pkgLockFile) {
5727
5783
  ]
5728
5784
  }
5729
5785
  }
5730
- });
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
+ }
5731
5824
  }
5732
5825
  }
5733
5826
  }
5734
5827
  }
5735
- return pkgList;
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
+ };
5736
5838
  };
5737
5839
 
5738
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(1032);
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(1205);
1750
- expect(parsedList.dependenciesList.length).toEqual(1205);
1765
+ expect(deps.length).toEqual(1195);
1766
+ expect(parsedList.dependenciesList.length).toEqual(1195);
1751
1767
  });
1752
1768
 
1753
1769
  test("parseBowerJson", async () => {
@@ -1807,6 +1823,8 @@ test("parsePnpmLock", async () => {
1807
1823
  "sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==",
1808
1824
  group: "@babel",
1809
1825
  name: "code-frame",
1826
+ "bom-ref": "pkg:npm/@babel/code-frame@7.10.1",
1827
+ purl: "pkg:npm/%40babel/code-frame@7.10.1",
1810
1828
  scope: undefined,
1811
1829
  version: "7.10.1",
1812
1830
  properties: [
@@ -1837,6 +1855,8 @@ test("parsePnpmLock", async () => {
1837
1855
  "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==",
1838
1856
  group: "@babel",
1839
1857
  name: "code-frame",
1858
+ "bom-ref": "pkg:npm/@babel/code-frame@7.16.7",
1859
+ purl: "pkg:npm/%40babel/code-frame@7.16.7",
1840
1860
  scope: "optional",
1841
1861
  version: "7.16.7",
1842
1862
  properties: [
@@ -1866,6 +1886,8 @@ test("parsePnpmLock", async () => {
1866
1886
  group: "",
1867
1887
  name: "ansi-regex",
1868
1888
  version: "2.1.1",
1889
+ "bom-ref": "pkg:npm/ansi-regex@2.1.1",
1890
+ purl: "pkg:npm/ansi-regex@2.1.1",
1869
1891
  scope: undefined,
1870
1892
  _integrity: "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
1871
1893
  properties: [{ name: "SrcFile", value: "./test/data/pnpm-lock2.yaml" }],
@@ -1900,6 +1922,8 @@ test("parsePnpmLock", async () => {
1900
1922
  group: "@nodelib",
1901
1923
  name: "fs.scandir",
1902
1924
  version: "2.1.5",
1925
+ "bom-ref": "pkg:npm/@nodelib/fs.scandir@2.1.5",
1926
+ purl: "pkg:npm/%40nodelib/fs.scandir@2.1.5",
1903
1927
  scope: undefined,
1904
1928
  _integrity:
1905
1929
  "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
@@ -1933,6 +1957,8 @@ test("parsePnpmLock", async () => {
1933
1957
  group: "@babel",
1934
1958
  name: "code-frame",
1935
1959
  version: "7.18.6",
1960
+ "bom-ref": "pkg:npm/@babel/code-frame@7.18.6",
1961
+ purl: "pkg:npm/%40babel/code-frame@7.18.6",
1936
1962
  scope: "optional",
1937
1963
  _integrity:
1938
1964
  "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
@@ -1955,6 +1981,8 @@ test("parsePnpmLock", async () => {
1955
1981
  group: "",
1956
1982
  name: "yargs",
1957
1983
  version: "17.7.1",
1984
+ "bom-ref": "pkg:npm/yargs@17.7.1",
1985
+ purl: "pkg:npm/yargs@17.7.1",
1958
1986
  scope: "optional",
1959
1987
  _integrity:
1960
1988
  "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==",
@@ -1980,6 +2008,8 @@ test("parsePnpmLock", async () => {
1980
2008
  group: "@babel",
1981
2009
  name: "code-frame",
1982
2010
  version: "7.18.6",
2011
+ "bom-ref": "pkg:npm/@babel/code-frame@7.18.6",
2012
+ purl: "pkg:npm/%40babel/code-frame@7.18.6",
1983
2013
  scope: "optional",
1984
2014
  _integrity:
1985
2015
  "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
@@ -2301,13 +2331,16 @@ test("parseYarnLock", async () => {
2301
2331
  });
2302
2332
 
2303
2333
  test("parseComposerLock", () => {
2304
- let deps = parseComposerLock("./test/data/composer.lock");
2305
- expect(deps.length).toEqual(1);
2306
- expect(deps[0]).toEqual({
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({
2307
2338
  group: "quickbooks",
2308
2339
  name: "v3-php-sdk",
2309
2340
  scope: "required",
2310
- version: "4.0.6.1",
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",
2311
2344
  repository: {
2312
2345
  type: "git",
2313
2346
  url: "https://github.com/intuit/QuickBooks-V3-PHP-SDK.git",
@@ -2319,6 +2352,10 @@ test("parseComposerLock", () => {
2319
2352
  {
2320
2353
  name: "SrcFile",
2321
2354
  value: "./test/data/composer.lock"
2355
+ },
2356
+ {
2357
+ name: "Namespaces",
2358
+ value: "QuickBooksOnline\\API\\"
2322
2359
  }
2323
2360
  ],
2324
2361
  evidence: {
@@ -2336,13 +2373,16 @@ test("parseComposerLock", () => {
2336
2373
  }
2337
2374
  });
2338
2375
 
2339
- deps = parseComposerLock("./test/data/composer-2.lock");
2340
- expect(deps.length).toEqual(73);
2341
- expect(deps[0]).toEqual({
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({
2342
2380
  group: "amphp",
2343
2381
  name: "amp",
2344
2382
  scope: "required",
2345
- version: "2.4.4",
2383
+ version: "v2.4.4",
2384
+ purl: "pkg:composer/amphp/amp@v2.4.4",
2385
+ "bom-ref": "pkg:composer/amphp/amp@v2.4.4",
2346
2386
  repository: {
2347
2387
  type: "git",
2348
2388
  url: "https://github.com/amphp/amp.git",
@@ -2354,6 +2394,10 @@ test("parseComposerLock", () => {
2354
2394
  {
2355
2395
  name: "SrcFile",
2356
2396
  value: "./test/data/composer-2.lock"
2397
+ },
2398
+ {
2399
+ name: "Namespaces",
2400
+ value: "Amp\\"
2357
2401
  }
2358
2402
  ],
2359
2403
  evidence: {
@@ -2371,12 +2415,15 @@ test("parseComposerLock", () => {
2371
2415
  }
2372
2416
  });
2373
2417
 
2374
- deps = parseComposerLock("./test/data/composer-3.lock");
2375
- expect(deps.length).toEqual(62);
2376
- expect(deps[0]).toEqual({
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({
2377
2422
  group: "amphp",
2378
2423
  name: "amp",
2379
- version: "2.6.2",
2424
+ version: "v2.6.2",
2425
+ purl: "pkg:composer/amphp/amp@v2.6.2",
2426
+ "bom-ref": "pkg:composer/amphp/amp@v2.6.2",
2380
2427
  repository: {
2381
2428
  type: "git",
2382
2429
  url: "https://github.com/amphp/amp.git",
@@ -2385,7 +2432,13 @@ test("parseComposerLock", () => {
2385
2432
  license: ["MIT"],
2386
2433
  description: "A non-blocking concurrency framework for PHP applications.",
2387
2434
  scope: "required",
2388
- properties: [{ name: "SrcFile", value: "./test/data/composer-3.lock" }],
2435
+ properties: [
2436
+ { name: "SrcFile", value: "./test/data/composer-3.lock" },
2437
+ {
2438
+ name: "Namespaces",
2439
+ value: "Amp\\"
2440
+ }
2441
+ ],
2389
2442
  evidence: {
2390
2443
  identity: {
2391
2444
  field: "purl",
@@ -2400,6 +2453,42 @@ test("parseComposerLock", () => {
2400
2453
  }
2401
2454
  }
2402
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
+ });
2403
2492
  });
2404
2493
 
2405
2494
  test("parseGemfileLockData", async () => {