@cyclonedx/cdxgen 12.3.2 → 12.3.3

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.
Files changed (53) hide show
  1. package/README.md +6 -0
  2. package/data/rules/ci-permissions.yaml +132 -0
  3. package/data/rules/dependency-sources.yaml +65 -5
  4. package/data/rules/package-integrity.yaml +22 -0
  5. package/lib/cli/index.js +141 -39
  6. package/lib/cli/index.poku.js +579 -1
  7. package/lib/helpers/agentFormulationParser.js +6 -2
  8. package/lib/helpers/agentFormulationParser.poku.js +42 -0
  9. package/lib/helpers/analyzer.js +38 -9
  10. package/lib/helpers/analyzer.poku.js +67 -0
  11. package/lib/helpers/chromextutils.js +25 -3
  12. package/lib/helpers/chromextutils.poku.js +68 -0
  13. package/lib/helpers/ciParsers/githubActions.js +79 -0
  14. package/lib/helpers/ciParsers/githubActions.poku.js +103 -0
  15. package/lib/helpers/communityAiConfigParser.js +15 -5
  16. package/lib/helpers/communityAiConfigParser.poku.js +71 -0
  17. package/lib/helpers/depsUtils.js +5 -0
  18. package/lib/helpers/depsUtils.poku.js +55 -0
  19. package/lib/helpers/display.js +45 -22
  20. package/lib/helpers/display.poku.js +47 -60
  21. package/lib/helpers/mcpConfigParser.js +21 -5
  22. package/lib/helpers/mcpConfigParser.poku.js +39 -2
  23. package/lib/helpers/propertySanitizer.js +121 -0
  24. package/lib/helpers/utils.js +951 -40
  25. package/lib/helpers/utils.poku.js +882 -0
  26. package/lib/managers/binary.js +16 -0
  27. package/lib/managers/binary.poku.js +1 -0
  28. package/lib/managers/docker.js +240 -16
  29. package/lib/managers/docker.poku.js +1142 -2
  30. package/lib/server/server.js +7 -4
  31. package/lib/server/server.poku.js +36 -1
  32. package/lib/stages/postgen/auditBom.poku.js +644 -2
  33. package/package.json +2 -1
  34. package/types/lib/cli/index.d.ts.map +1 -1
  35. package/types/lib/helpers/agentFormulationParser.d.ts.map +1 -1
  36. package/types/lib/helpers/analyzer.d.ts.map +1 -1
  37. package/types/lib/helpers/chromextutils.d.ts.map +1 -1
  38. package/types/lib/helpers/ciParsers/githubActions.d.ts.map +1 -1
  39. package/types/lib/helpers/communityAiConfigParser.d.ts.map +1 -1
  40. package/types/lib/helpers/depsUtils.d.ts.map +1 -1
  41. package/types/lib/helpers/display.d.ts +1 -0
  42. package/types/lib/helpers/display.d.ts.map +1 -1
  43. package/types/lib/helpers/mcpConfigParser.d.ts +1 -1
  44. package/types/lib/helpers/mcpConfigParser.d.ts.map +1 -1
  45. package/types/lib/helpers/propertySanitizer.d.ts +3 -0
  46. package/types/lib/helpers/propertySanitizer.d.ts.map +1 -0
  47. package/types/lib/helpers/utils.d.ts +29 -0
  48. package/types/lib/helpers/utils.d.ts.map +1 -1
  49. package/types/lib/managers/binary.d.ts.map +1 -1
  50. package/types/lib/managers/docker.d.ts +3 -0
  51. package/types/lib/managers/docker.d.ts.map +1 -1
  52. package/types/lib/server/server.d.ts +1 -0
  53. package/types/lib/server/server.d.ts.map +1 -1
package/README.md CHANGED
@@ -523,6 +523,12 @@ You can also pass `-t docker` with repository names. Only the `latest` tag would
523
523
  cdxgen shiftleft/scan-slim -o /tmp/bom.json -t docker
524
524
  ```
525
525
 
526
+ For offline or staged scans, point cdxgen at a locally reconstructed root filesystem directory. The container pipeline accepts `-t docker`, `-t rootfs`, or `-t oci-dir` for this mode.
527
+
528
+ ```shell
529
+ cdxgen /tmp/remote_target -o /tmp/bom.json -t rootfs
530
+ ```
531
+
526
532
  You can also pass the .tar file of a container image.
527
533
 
528
534
  ```shell
@@ -642,5 +642,137 @@
642
642
  "sensitiveOperations": $prop($, 'cdx:github:step:sensitiveOperations'),
643
643
  "sensitiveContextRefs": $prop($, 'cdx:github:step:sensitiveContextRefs'),
644
644
  "dispatchKinds": $prop($, 'cdx:github:step:dispatchKinds')
645
+ }
646
+
647
+ - id: CI-022
648
+ name: "npm setup action disables build cache despite resolved package distributions"
649
+ description: "Explicitly disabling setup-node caching reduces tamper resistance and reviewability when npm dependencies are resolved from remote package distributions"
650
+ severity: medium
651
+ category: ci-permission
652
+ attack:
653
+ tactics: [TA0005]
654
+ techniques: [T1195.001]
655
+ condition: |
656
+ $auditComponents($)[
657
+ $prop($, 'cdx:github:action:disablesBuildCache') = 'true'
658
+ and $prop($, 'cdx:github:action:buildCacheEcosystem') = 'npm'
659
+ and $count($$.components[
660
+ $startsWith(purl, 'pkg:npm/')
661
+ and (
662
+ $contains($lowercase($nullSafeProp($, 'cdx:npm:manifestSourceType')), 'git')
663
+ or $contains($lowercase($nullSafeProp($, 'cdx:npm:manifestSourceType')), 'url')
664
+ )
665
+ and $count(externalReferences[
666
+ type = 'distribution'
667
+ and (
668
+ $startsWith($lowercase(url), 'git+')
669
+ or $startsWith($lowercase(url), 'http://')
670
+ or $startsWith($lowercase(url), 'https://')
671
+ )
672
+ ]) > 0
673
+ ]) > 0
674
+ ]
675
+ location: |
676
+ {
677
+ "bomRef": $."bom-ref",
678
+ "purl": purl,
679
+ "file": $prop($, 'cdx:github:workflow:file')
680
+ }
681
+ message: "GitHub Action '{{ $prop($, 'cdx:github:action:uses') }}' explicitly disables npm build caching while resolved npm package distributions are present in the BOM"
682
+ mitigation: "Keep setup-node caching enabled unless you have a reviewed exception; disabling cache can weaken integrity checks and provenance review for resolved npm artifacts"
683
+ evidence: |
684
+ {
685
+ "cacheDisableInput": $prop($, 'cdx:github:action:buildCacheDisableInput'),
686
+ "cacheDisableValue": $prop($, 'cdx:github:action:buildCacheDisableValue'),
687
+ "matchingPackages": $$.components[
688
+ $startsWith(purl, 'pkg:npm/')
689
+ and (
690
+ $contains($lowercase($nullSafeProp($, 'cdx:npm:manifestSourceType')), 'git')
691
+ or $contains($lowercase($nullSafeProp($, 'cdx:npm:manifestSourceType')), 'url')
692
+ )
693
+ and $count(externalReferences[
694
+ type = 'distribution'
695
+ and (
696
+ $startsWith($lowercase(url), 'git+')
697
+ or $startsWith($lowercase(url), 'http://')
698
+ or $startsWith($lowercase(url), 'https://')
699
+ )
700
+ ]) > 0
701
+ ].purl
702
+ }
703
+
704
+ - id: CI-023
705
+ name: "Python setup action disables build cache despite resolved package distributions"
706
+ description: "Explicitly disabling setup-python caching reduces tamper resistance and reviewability when PyPI dependencies are resolved from remote archives or VCS sources"
707
+ severity: medium
708
+ category: ci-permission
709
+ attack:
710
+ tactics: [TA0005]
711
+ techniques: [T1195.001]
712
+ condition: |
713
+ $auditComponents($)[
714
+ $prop($, 'cdx:github:action:disablesBuildCache') = 'true'
715
+ and $prop($, 'cdx:github:action:buildCacheEcosystem') = 'pypi'
716
+ and $count($$.components[
717
+ $startsWith(purl, 'pkg:pypi/')
718
+ and (
719
+ $contains($lowercase($nullSafeProp($, 'cdx:pypi:manifestSourceType')), 'git')
720
+ or $contains($lowercase($nullSafeProp($, 'cdx:pypi:manifestSourceType')), 'url')
721
+ )
722
+ ]) > 0
723
+ ]
724
+ location: |
725
+ {
726
+ "bomRef": $."bom-ref",
727
+ "purl": purl,
728
+ "file": $prop($, 'cdx:github:workflow:file')
729
+ }
730
+ message: "GitHub Action '{{ $prop($, 'cdx:github:action:uses') }}' explicitly disables Python build caching while resolved PyPI package distributions are present in the BOM"
731
+ mitigation: "Keep setup-python caching enabled when lockfiles resolve remote archives or VCS sources unless you have a reviewed exception"
732
+ evidence: |
733
+ {
734
+ "cacheDisableInput": $prop($, 'cdx:github:action:buildCacheDisableInput'),
735
+ "cacheDisableValue": $prop($, 'cdx:github:action:buildCacheDisableValue'),
736
+ "matchingPackages": $$.components[
737
+ $startsWith(purl, 'pkg:pypi/')
738
+ and (
739
+ $contains($lowercase($nullSafeProp($, 'cdx:pypi:manifestSourceType')), 'git')
740
+ or $contains($lowercase($nullSafeProp($, 'cdx:pypi:manifestSourceType')), 'url')
741
+ )
742
+ ].purl
645
743
  }
646
744
 
745
+ - id: CI-024
746
+ name: "Cargo setup action disables build cache despite manifest-declared git dependencies"
747
+ description: "Explicitly disabling Cargo setup caching reduces tamper resistance and reviewability when Cargo manifests rely on git dependencies"
748
+ severity: medium
749
+ category: ci-permission
750
+ attack:
751
+ tactics: [TA0005]
752
+ techniques: [T1195.001]
753
+ condition: |
754
+ $auditComponents($)[
755
+ $prop($, 'cdx:github:action:disablesBuildCache') = 'true'
756
+ and $prop($, 'cdx:github:action:buildCacheEcosystem') = 'cargo'
757
+ and $count($$.components[
758
+ $startsWith(purl, 'pkg:cargo/')
759
+ and $hasProp($, 'cdx:cargo:git')
760
+ ]) > 0
761
+ ]
762
+ location: |
763
+ {
764
+ "bomRef": $."bom-ref",
765
+ "purl": purl,
766
+ "file": $prop($, 'cdx:github:workflow:file')
767
+ }
768
+ message: "GitHub Action '{{ $prop($, 'cdx:github:action:uses') }}' explicitly disables Cargo build caching while manifest-declared Cargo git dependencies are present in the BOM"
769
+ mitigation: "Keep Cargo setup caching enabled when manifests rely on git dependencies unless you have a reviewed exception"
770
+ evidence: |
771
+ {
772
+ "cacheDisableInput": $prop($, 'cdx:github:action:buildCacheDisableInput'),
773
+ "cacheDisableValue": $prop($, 'cdx:github:action:buildCacheDisableValue'),
774
+ "matchingPackages": $$.components[
775
+ $startsWith(purl, 'pkg:cargo/')
776
+ and $hasProp($, 'cdx:cargo:git')
777
+ ].purl
778
+ }
@@ -2,21 +2,23 @@
2
2
  # Category: dependency-source
3
3
  # Evaluates package manager data for non-registry, local, or mutable sources
4
4
  - id: PKG-001
5
- name: "Install script from non-registry source"
6
- description: "npm packages with install scripts from git/file/local sources increase supply chain attack surface"
5
+ name: "Install script from direct manifest source"
6
+ description: "npm packages with install scripts declared from git, URL, or local path sources in the manifest increase supply chain attack surface"
7
7
  severity: high
8
8
  category: dependency-source
9
9
  condition: |
10
10
  components[
11
11
  $prop($, 'cdx:npm:hasInstallScript') = 'true'
12
- and $prop($, 'cdx:npm:isRegistryDependency') = 'false'
12
+ and $hasProp($, 'cdx:npm:manifestSourceType')
13
13
  ]
14
14
  location: |
15
15
  { "bomRef": $."bom-ref", "purl": purl }
16
- message: "npm package '{{ name }}@{{ version }}' executes install script from non-registry source"
17
- mitigation: "Avoid git/file dependencies with lifecycle hooks; use registry dependencies or vendor explicitly"
16
+ message: "npm package '{{ name }}@{{ version }}' executes install script from manifest-declared source type(s): {{ $prop($, 'cdx:npm:manifestSourceType') }}"
17
+ mitigation: "Avoid git, URL, or local-path dependencies with lifecycle hooks; use registry-published dependencies or vendor explicitly"
18
18
  evidence: |
19
19
  {
20
+ "manifestSourceType": $prop($, 'cdx:npm:manifestSourceType'),
21
+ "manifestSource": $prop($, 'cdx:npm:manifestSource'),
20
22
  "riskyScripts": $prop($, 'cdx:npm:risky_scripts'),
21
23
  "resolvedPath": $prop($, 'cdx:npm:resolvedPath'),
22
24
  "isLink": $prop($, 'cdx:npm:isLink')
@@ -162,3 +164,61 @@
162
164
  "dependencyKind": $prop($, 'cdx:cargo:dependencyKind'),
163
165
  "target": $prop($, 'cdx:cargo:target')
164
166
  }
167
+ - id: PKG-009
168
+ name: "Collider package resolved from insecure HTTP origin"
169
+ description: "Collider lock entries that resolve from HTTP origins can be observed or modified in transit before wrap-hash verification occurs"
170
+ severity: medium
171
+ category: dependency-source
172
+ condition: |
173
+ components[
174
+ $prop($, 'cdx:collider:originScheme') = 'http'
175
+ ]
176
+ location: |
177
+ { "bomRef": $."bom-ref", "purl": purl }
178
+ message: "Collider package '{{ name }}@{{ version }}' resolves from insecure origin '{{ $prop($, 'cdx:collider:origin') }}'"
179
+ mitigation: "Prefer HTTPS, trusted file:// repositories, or an authenticated internal mirror for Collider package origins"
180
+ evidence: |
181
+ {
182
+ "origin": $prop($, 'cdx:collider:origin'),
183
+ "originHost": $prop($, 'cdx:collider:originHost'),
184
+ "dependencyKind": $prop($, 'cdx:collider:dependencyKind')
185
+ }
186
+ - id: PKG-010
187
+ name: "Collider origin required sanitization before BOM emission"
188
+ description: "Collider lock origin URLs should not carry credentials, query strings, or fragments because those values may embed secrets or unstable signed URLs"
189
+ severity: low
190
+ category: dependency-source
191
+ condition: |
192
+ components[
193
+ $prop($, 'cdx:collider:originSanitized') = 'true'
194
+ ]
195
+ location: |
196
+ { "bomRef": $."bom-ref", "purl": purl }
197
+ message: "Collider package '{{ name }}@{{ version }}' had sensitive origin fields stripped before BOM emission"
198
+ mitigation: "Avoid embedding credentials or signed query parameters in Collider repository origin URLs; prefer stable repository base URLs"
199
+ evidence: |
200
+ {
201
+ "origin": $prop($, 'cdx:collider:origin'),
202
+ "originHost": $prop($, 'cdx:collider:originHost'),
203
+ "dependencyKind": $prop($, 'cdx:collider:dependencyKind')
204
+ }
205
+ - id: PKG-011
206
+ name: "Python dependency uses direct manifest source"
207
+ description: "Python dependencies declared via git, direct URL, or local path in requirements or pyproject files bypass normal registry version mediation"
208
+ severity: high
209
+ category: dependency-source
210
+ condition: |
211
+ components[
212
+ $hasProp($, 'cdx:pypi:manifestSourceType')
213
+ ]
214
+ location: |
215
+ { "bomRef": $."bom-ref", "purl": purl }
216
+ message: "Python package '{{ name }}@{{ version }}' is declared from manifest {{ $prop($, 'cdx:pypi:manifestSourceType') }} source '{{ $prop($, 'cdx:pypi:manifestSource') }}'"
217
+ mitigation: "Prefer registry-published releases for production builds, or pin and review direct git/URL/path sources explicitly"
218
+ evidence: |
219
+ {
220
+ "manifestSourceType": $prop($, 'cdx:pypi:manifestSourceType'),
221
+ "manifestSource": $prop($, 'cdx:pypi:manifestSource'),
222
+ "registry": $prop($, 'cdx:pypi:registry'),
223
+ "resolvedFrom": $prop($, 'cdx:pypi:resolved_from')
224
+ }
@@ -305,3 +305,25 @@
305
305
  )
306
306
  ].$prop($, 'cdx:github:step:command')
307
307
  }
308
+
309
+ - id: INT-014
310
+ name: "Collider package missing valid wrap hash pin"
311
+ description: "Collider lock entries should carry a SHA-256 wrap_hash so the selected wrap file remains integrity-pinned and reproducible"
312
+ severity: high
313
+ category: package-integrity
314
+ condition: |
315
+ components[
316
+ $hasProp($, 'cdx:collider:dependencyKind')
317
+ and $prop($, 'cdx:collider:hasWrapHash') = 'false'
318
+ ]
319
+ location: |
320
+ { "bomRef": $."bom-ref", "purl": purl }
321
+ message: "Collider package '{{ name }}@{{ version }}' is missing a valid wrap hash integrity pin"
322
+ mitigation: "Recreate collider.lock with valid wrap_hash values and verify the lockfile against the repository before release"
323
+ evidence: |
324
+ {
325
+ "wrapHash": $prop($, 'cdx:collider:wrapHash'),
326
+ "wrapHashInvalid": $prop($, 'cdx:collider:wrapHashInvalid'),
327
+ "origin": $prop($, 'cdx:collider:origin'),
328
+ "dependencyKind": $prop($, 'cdx:collider:dependencyKind')
329
+ }
package/lib/cli/index.js CHANGED
@@ -64,6 +64,7 @@ import {
64
64
  addEvidenceForDotnet,
65
65
  addEvidenceForImports,
66
66
  addPlugin,
67
+ attachIdentityTools,
67
68
  buildGradleCommandArguments,
68
69
  buildObjectForCocoaPod,
69
70
  buildObjectForGradleModule,
@@ -88,6 +89,7 @@ import {
88
89
  executeParallelGradleProperties,
89
90
  executePodCommand,
90
91
  extractJarArchive,
92
+ extractToolRefs,
91
93
  frameworksList,
92
94
  generatePixiLockFile,
93
95
  getAllFiles,
@@ -134,6 +136,7 @@ import {
134
136
  parseCloudBuildData,
135
137
  parseCmakeLikeFile,
136
138
  parseCocoaDependency,
139
+ parseColliderLockData,
137
140
  parseComposerJson,
138
141
  parseComposerLock,
139
142
  parseConanData,
@@ -329,7 +332,10 @@ const createDefaultParentComponent = (
329
332
  group: options.projectGroup || "",
330
333
  name: compName,
331
334
  version: `${options.projectVersion}` || "latest",
332
- type: compName.endsWith(".tar") ? "container" : "application",
335
+ type:
336
+ type === "container" || compName.endsWith(".tar")
337
+ ? "container"
338
+ : "application",
333
339
  };
334
340
  const ppurl = new PackageURL(
335
341
  type,
@@ -344,6 +350,25 @@ const createDefaultParentComponent = (
344
350
  return parentComponent;
345
351
  };
346
352
 
353
+ const shouldIncludeNodeModulesDir = (options = {}, baseProjectTypes = []) => {
354
+ if (options.deep) {
355
+ return true;
356
+ }
357
+ const projectTypes = Array.isArray(options.projectType)
358
+ ? options.projectType
359
+ : options.projectType
360
+ ? [options.projectType]
361
+ : [];
362
+ if (!projectTypes.length) {
363
+ return true;
364
+ }
365
+ return baseProjectTypes.some((projectType) =>
366
+ projectTypes.every((selectedProjectType) =>
367
+ PROJECT_TYPE_ALIASES[projectType]?.includes(selectedProjectType),
368
+ ),
369
+ );
370
+ };
371
+
347
372
  const determineParentComponent = (options) => {
348
373
  let parentComponent;
349
374
  if (options.parentComponent && Object.keys(options.parentComponent).length) {
@@ -1368,17 +1393,21 @@ export async function createJarBom(path, options) {
1368
1393
  let pkgList = [];
1369
1394
  let jarFiles;
1370
1395
  let nsMapping = {};
1371
- if (!options.exclude) {
1372
- options.exclude = [];
1373
- }
1374
- // We can look for jar files within node_modules directory
1375
- if (typeof options.includeNodeModulesDir === "undefined" || options.deep) {
1376
- options.includeNodeModulesDir = true;
1396
+ const searchOptions = {
1397
+ ...options,
1398
+ exclude: [...(options.exclude || [])],
1399
+ };
1400
+ if (typeof searchOptions.includeNodeModulesDir === "undefined") {
1401
+ searchOptions.includeNodeModulesDir = shouldIncludeNodeModulesDir(options, [
1402
+ "jar",
1403
+ "war",
1404
+ "ear",
1405
+ ]);
1377
1406
  }
1378
1407
  // Exclude certain directories during oci sbom generation
1379
1408
  if (hasAnyProjectType(["oci"], options, false)) {
1380
- options.exclude.push("**/android-sdk*/**");
1381
- options.exclude.push("**/.sdkman/**");
1409
+ searchOptions.exclude.push("**/android-sdk*/**");
1410
+ searchOptions.exclude.push("**/.sdkman/**");
1382
1411
  }
1383
1412
  const parentComponent = createDefaultParentComponent(path, "maven", options);
1384
1413
  if (options.useGradleCache) {
@@ -1402,14 +1431,14 @@ export async function createJarBom(path, options) {
1402
1431
  jarFiles = getAllFiles(
1403
1432
  path,
1404
1433
  `${options.multiProject ? "**/" : ""}*.[jw]ar`,
1405
- options,
1434
+ searchOptions,
1406
1435
  );
1407
1436
  }
1408
1437
  // Jenkins plugins
1409
1438
  const hpiFiles = getAllFiles(
1410
1439
  path,
1411
1440
  `${options.multiProject ? "**/" : ""}*.hpi`,
1412
- options,
1441
+ searchOptions,
1413
1442
  );
1414
1443
  if (hpiFiles.length) {
1415
1444
  jarFiles = jarFiles.concat(hpiFiles);
@@ -1464,6 +1493,13 @@ export function createBinaryBom(path, options) {
1464
1493
  const binaryBom = JSON.parse(
1465
1494
  readFileSync(binaryBomFile, { encoding: "utf-8" }),
1466
1495
  );
1496
+ attachIdentityTools(
1497
+ binaryBom?.components,
1498
+ extractToolRefs(
1499
+ binaryBom?.metadata?.tools,
1500
+ (tool) => tool?.name !== "cdxgen",
1501
+ ),
1502
+ );
1467
1503
  return {
1468
1504
  bomJson: binaryBom,
1469
1505
  dependencies: binaryBom.dependencies,
@@ -1891,6 +1927,10 @@ export async function createJavaBom(path, options) {
1891
1927
  ) {
1892
1928
  tools = bomJsonObj.metadata.tools;
1893
1929
  }
1930
+ const toolRefs = extractToolRefs(
1931
+ bomJsonObj?.metadata?.tools,
1932
+ (tool) => tool?.name !== "cdxgen",
1933
+ );
1894
1934
  if (
1895
1935
  bomJsonObj.metadata?.component &&
1896
1936
  !Object.keys(parentComponent).length
@@ -1927,6 +1967,7 @@ export async function createJavaBom(path, options) {
1927
1967
  name: "SrcFile",
1928
1968
  value: srcPomFile,
1929
1969
  });
1970
+ attachIdentityTools(acomp, toolRefs);
1930
1971
  }
1931
1972
  }
1932
1973
  pkgList = pkgList.concat(bomJsonObj.components);
@@ -5416,6 +5457,11 @@ export function createCppBom(path, options) {
5416
5457
  let parentComponent;
5417
5458
  let dependencies = [];
5418
5459
  const addedParentComponentsMap = {};
5460
+ const colliderLockFiles = getAllFiles(
5461
+ path,
5462
+ `${options.multiProject ? "**/" : ""}collider.lock`,
5463
+ options,
5464
+ );
5419
5465
  const conanLockFiles = getAllFiles(
5420
5466
  path,
5421
5467
  `${options.multiProject ? "**/" : ""}conan.lock`,
@@ -5492,6 +5538,39 @@ export function createCppBom(path, options) {
5492
5538
  }
5493
5539
  }
5494
5540
  }
5541
+ if (colliderLockFiles.length) {
5542
+ for (const f of colliderLockFiles) {
5543
+ if (DEBUG_MODE) {
5544
+ console.log(`Parsing ${f}`);
5545
+ }
5546
+ const colliderLockData = readFileSync(f, { encoding: "utf-8" });
5547
+ const {
5548
+ pkgList: colliderPkgList,
5549
+ dependencies: colliderDependencies,
5550
+ parentComponentDependencies: parentCompDeps,
5551
+ } = parseColliderLockData(colliderLockData, f);
5552
+
5553
+ if (colliderPkgList.length) {
5554
+ pkgList = pkgList.concat(colliderPkgList);
5555
+ }
5556
+
5557
+ if (Object.keys(colliderDependencies).length) {
5558
+ dependencies = mergeDependencies(
5559
+ dependencies,
5560
+ Object.keys(colliderDependencies).map((dependentBomRef) => ({
5561
+ ref: dependentBomRef,
5562
+ dependsOn: colliderDependencies[dependentBomRef],
5563
+ })),
5564
+ );
5565
+ }
5566
+
5567
+ if (parentCompDeps.length) {
5568
+ parentComponentDependencies = [
5569
+ ...new Set(parentComponentDependencies.concat(parentCompDeps)),
5570
+ ];
5571
+ }
5572
+ }
5573
+ }
5495
5574
  if (cmakeLikeFiles.length) {
5496
5575
  for (const f of cmakeLikeFiles) {
5497
5576
  if (DEBUG_MODE) {
@@ -6830,24 +6909,26 @@ export async function createContainerSpecLikeBom(path, options) {
6830
6909
  export function createPHPBom(path, options) {
6831
6910
  let dependencies = [];
6832
6911
  let parentComponent = {};
6833
- // We can look for composer files within node_modules directory
6834
- if (typeof options.includeNodeModulesDir === "undefined" || options.deep) {
6835
- options.includeNodeModulesDir = true;
6836
- }
6912
+ const searchOptions = {
6913
+ ...options,
6914
+ includeNodeModulesDir:
6915
+ typeof options.includeNodeModulesDir === "undefined"
6916
+ ? shouldIncludeNodeModulesDir(options, ["php"])
6917
+ : options.includeNodeModulesDir,
6918
+ };
6919
+ const composerLockSearchOptions = {
6920
+ ...searchOptions,
6921
+ exclude: [...(options.exclude || []), "**/vendor/**"],
6922
+ };
6837
6923
  const composerJsonFiles = getAllFiles(
6838
6924
  path,
6839
6925
  `${options.multiProject ? "**/" : ""}composer.json`,
6840
- options,
6926
+ searchOptions,
6841
6927
  );
6842
- if (!options.exclude) {
6843
- options.exclude = [];
6844
- }
6845
- // Ignore vendor directories for lock files
6846
- options.exclude.push("**/vendor/**");
6847
6928
  let composerLockFiles = getAllFiles(
6848
6929
  path,
6849
6930
  `${options.multiProject ? "**/" : ""}composer.lock`,
6850
- options,
6931
+ composerLockSearchOptions,
6851
6932
  );
6852
6933
  let pkgList = [];
6853
6934
  const composerJsonMode = composerJsonFiles.length;
@@ -6910,7 +6991,7 @@ export function createPHPBom(path, options) {
6910
6991
  composerLockFiles = getAllFiles(
6911
6992
  path,
6912
6993
  `${options.multiProject ? "**/" : ""}composer.lock`,
6913
- options,
6994
+ composerLockSearchOptions,
6914
6995
  );
6915
6996
  if (composerLockFiles.length) {
6916
6997
  // Look for any root composer.json to capture the parentComponent
@@ -8058,6 +8139,7 @@ export async function createMultiXBom(pathList, options) {
8058
8139
  binPaths,
8059
8140
  executables,
8060
8141
  sharedLibs,
8142
+ tools,
8061
8143
  } = await getOSPackages(
8062
8144
  options.allLayersExplodedDir,
8063
8145
  options.exportData?.inspectData?.Config,
@@ -8088,6 +8170,13 @@ export async function createMultiXBom(pathList, options) {
8088
8170
  if (allTypes?.length) {
8089
8171
  options.allOSComponentTypes = allTypes;
8090
8172
  }
8173
+ if (tools?.length) {
8174
+ options.tools = (
8175
+ Array.isArray(options.tools)
8176
+ ? options.tools
8177
+ : options.tools?.components || []
8178
+ ).concat(tools);
8179
+ }
8091
8180
  components = components.concat(osPackages);
8092
8181
  components = components.concat(executables);
8093
8182
  components = components.concat(sharedLibs);
@@ -9162,6 +9251,11 @@ export async function createXBom(path, options) {
9162
9251
  `${options.multiProject ? "**/" : ""}conan.lock`,
9163
9252
  options,
9164
9253
  );
9254
+ const colliderLockFiles = getAllFiles(
9255
+ path,
9256
+ `${options.multiProject ? "**/" : ""}collider.lock`,
9257
+ options,
9258
+ );
9165
9259
  const conanFiles = getAllFiles(
9166
9260
  path,
9167
9261
  `${options.multiProject ? "**/" : ""}conanfile.txt`,
@@ -9178,6 +9272,7 @@ export async function createXBom(path, options) {
9178
9272
  options,
9179
9273
  );
9180
9274
  if (
9275
+ colliderLockFiles.length ||
9181
9276
  conanLockFiles.length ||
9182
9277
  conanFiles.length ||
9183
9278
  cmakeListFiles.length ||
@@ -9347,6 +9442,10 @@ export async function createBom(path, options) {
9347
9442
  }
9348
9443
  let exportData;
9349
9444
  let isContainerMode = false;
9445
+ const isLocalDirectoryInput =
9446
+ !options.projectType?.includes("universal") &&
9447
+ safeExistsSync(path) &&
9448
+ lstatSync(path).isDirectory();
9350
9449
  // Docker and image archive support
9351
9450
  // TODO: Support any source archive
9352
9451
  if (path.endsWith(".tar") || path.endsWith(".tar.gz")) {
@@ -9359,6 +9458,22 @@ export async function createBom(path, options) {
9359
9458
  return {};
9360
9459
  }
9361
9460
  isContainerMode = true;
9461
+ } else if (
9462
+ isLocalDirectoryInput &&
9463
+ (hasAnyProjectType(["oci-dir"], options, false) ||
9464
+ hasAnyProjectType(["oci"], options, false))
9465
+ ) {
9466
+ isContainerMode = true;
9467
+ exportData = {
9468
+ inspectData: undefined,
9469
+ lastWorkingDir: "",
9470
+ allLayersDir: path,
9471
+ allLayersExplodedDir: path,
9472
+ };
9473
+ if (safeExistsSync(join(path, "all-layers"))) {
9474
+ exportData.allLayersExplodedDir = join(path, "all-layers");
9475
+ }
9476
+ exportData.pkgPathList = getPkgPathList(exportData, undefined);
9362
9477
  } else if (
9363
9478
  (options.projectType &&
9364
9479
  !options.projectType?.includes("universal") &&
@@ -9386,21 +9501,6 @@ export async function createBom(path, options) {
9386
9501
  console.log(path, "doesn't appear to be a valid container image.");
9387
9502
  }
9388
9503
  }
9389
- } else if (
9390
- !options.projectType?.includes("universal") &&
9391
- hasAnyProjectType(["oci-dir"], options, false)
9392
- ) {
9393
- isContainerMode = true;
9394
- exportData = {
9395
- inspectData: undefined,
9396
- lastWorkingDir: "",
9397
- allLayersDir: path,
9398
- allLayersExplodedDir: path,
9399
- };
9400
- if (safeExistsSync(join(path, "all-layers"))) {
9401
- exportData.allLayersDir = join(path, "all-layers");
9402
- }
9403
- exportData.pkgPathList = getPkgPathList(exportData, undefined);
9404
9504
  }
9405
9505
  if (isContainerMode) {
9406
9506
  options.multiProject = true;
@@ -9680,6 +9780,7 @@ export async function submitBom(args, bomContents) {
9680
9780
  // See issue #1963 regarding CRLF hardening
9681
9781
  return await got(serverUrl, {
9682
9782
  method: "PUT",
9783
+ followRedirect: false,
9683
9784
  https: {
9684
9785
  rejectUnauthorized: !args.skipDtTlsCheck,
9685
9786
  },
@@ -9705,11 +9806,12 @@ export async function submitBom(args, bomContents) {
9705
9806
  try {
9706
9807
  return await got(serverUrl, {
9707
9808
  method: "POST",
9809
+ followRedirect: false,
9708
9810
  https: {
9709
9811
  rejectUnauthorized: !args.skipDtTlsCheck,
9710
9812
  },
9711
9813
  headers: {
9712
- "X-Api-Key": args.apiKey,
9814
+ "X-Api-Key": (args.apiKey || "").replace(/[\r\n]/g, ""),
9713
9815
  "Content-Type": "application/json",
9714
9816
  "user-agent": `@CycloneDX/cdxgen ${CDXGEN_VERSION}`,
9715
9817
  },