@cyclonedx/cdxgen 11.2.0 → 11.2.1
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/lib/cli/index.js +199 -21
- package/lib/evinser/evinser.js +10 -1
- package/lib/evinser/swiftsem.js +3 -0
- package/lib/helpers/logger.js +1 -0
- package/lib/helpers/utils.js +702 -17
- package/lib/helpers/utils.test.js +77 -0
- package/lib/stages/postgen/postgen.js +6 -2
- package/package.json +4 -4
- package/types/lib/cli/index.d.ts +7 -0
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/evinser/swiftsem.d.ts.map +1 -1
- package/types/lib/helpers/logger.d.ts.map +1 -1
- package/types/lib/helpers/utils.d.ts +44 -1
- package/types/lib/helpers/utils.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
package/lib/cli/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { spawnSync } from "node:child_process";
|
|
|
3
3
|
import {
|
|
4
4
|
constants,
|
|
5
5
|
accessSync,
|
|
6
|
+
existsSync,
|
|
6
7
|
lstatSync,
|
|
7
8
|
mkdtempSync,
|
|
8
9
|
readFileSync,
|
|
@@ -16,6 +17,7 @@ import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
|
16
17
|
import process from "node:process";
|
|
17
18
|
import { URL } from "node:url";
|
|
18
19
|
import got from "got";
|
|
20
|
+
import { load as loadYaml } from "js-yaml";
|
|
19
21
|
import { PackageURL } from "packageurl-js";
|
|
20
22
|
import { gte, lte } from "semver";
|
|
21
23
|
import { parse } from "ssri";
|
|
@@ -47,6 +49,7 @@ import {
|
|
|
47
49
|
addEvidenceForImports,
|
|
48
50
|
addPlugin,
|
|
49
51
|
buildGradleCommandArguments,
|
|
52
|
+
buildObjectForCocoaPod,
|
|
50
53
|
buildObjectForGradleModule,
|
|
51
54
|
checksumFile,
|
|
52
55
|
cleanupPlugin,
|
|
@@ -61,6 +64,7 @@ import {
|
|
|
61
64
|
dirNameStr,
|
|
62
65
|
encodeForPurl,
|
|
63
66
|
executeParallelGradleProperties,
|
|
67
|
+
executePodCommand,
|
|
64
68
|
extractJarArchive,
|
|
65
69
|
frameworksList,
|
|
66
70
|
generatePixiLockFile,
|
|
@@ -98,6 +102,7 @@ import {
|
|
|
98
102
|
parseCljDep,
|
|
99
103
|
parseCloudBuildData,
|
|
100
104
|
parseCmakeLikeFile,
|
|
105
|
+
parseCocoaDependency,
|
|
101
106
|
parseComposerJson,
|
|
102
107
|
parseComposerLock,
|
|
103
108
|
parseConanData,
|
|
@@ -139,6 +144,8 @@ import {
|
|
|
139
144
|
parsePkgLock,
|
|
140
145
|
parsePnpmLock,
|
|
141
146
|
parsePnpmWorkspace,
|
|
147
|
+
parsePodfileLock,
|
|
148
|
+
parsePodfileTargets,
|
|
142
149
|
parsePom,
|
|
143
150
|
parsePrivadoFile,
|
|
144
151
|
parsePubLockData,
|
|
@@ -5012,6 +5019,158 @@ export async function createSwiftBom(path, options) {
|
|
|
5012
5019
|
});
|
|
5013
5020
|
}
|
|
5014
5021
|
|
|
5022
|
+
/**
|
|
5023
|
+
* Function to create bom string for cocoa projects
|
|
5024
|
+
*
|
|
5025
|
+
* @param {string} path to the project
|
|
5026
|
+
* @param {Object} options Parse options from the cli
|
|
5027
|
+
*/
|
|
5028
|
+
export async function createCocoaBom(path, options) {
|
|
5029
|
+
const cocoaFiles = getAllFiles(
|
|
5030
|
+
path,
|
|
5031
|
+
`${options.multiProject ? "**/" : ""}Podfile`,
|
|
5032
|
+
options,
|
|
5033
|
+
);
|
|
5034
|
+
if (cocoaFiles.length > 1) {
|
|
5035
|
+
thoughtLog(
|
|
5036
|
+
`There are ${cocoaFiles.length} pod files. I will carefully process each one.`,
|
|
5037
|
+
);
|
|
5038
|
+
}
|
|
5039
|
+
let excludeMessageShown = false;
|
|
5040
|
+
for (const podFile of cocoaFiles) {
|
|
5041
|
+
const projectPath = dirname(podFile);
|
|
5042
|
+
const lockFile = `${podFile}.lock`;
|
|
5043
|
+
if (!existsSync(lockFile) || options.deep) {
|
|
5044
|
+
if (options.installDeps) {
|
|
5045
|
+
executePodCommand(["install"], projectPath, options);
|
|
5046
|
+
} else {
|
|
5047
|
+
console.log(
|
|
5048
|
+
"No 'Podfile.lock' found and '--no-install-deps' is set -- A Podfile.lock is needed to parse dependencies!",
|
|
5049
|
+
);
|
|
5050
|
+
options.failOnError && process.exit(1);
|
|
5051
|
+
}
|
|
5052
|
+
}
|
|
5053
|
+
const parentComponent = await buildObjectForCocoaPod(
|
|
5054
|
+
{
|
|
5055
|
+
name: basename(projectPath),
|
|
5056
|
+
version: "latest",
|
|
5057
|
+
},
|
|
5058
|
+
undefined,
|
|
5059
|
+
"application",
|
|
5060
|
+
);
|
|
5061
|
+
const podfileLock = loadYaml(readFileSync(lockFile, "utf-8"));
|
|
5062
|
+
const pods = await parsePodfileLock(podfileLock, projectPath);
|
|
5063
|
+
const allObjects = new Map();
|
|
5064
|
+
for (const [name, pod] of pods) {
|
|
5065
|
+
allObjects.set(name, await buildObjectForCocoaPod(pod.metadata, options));
|
|
5066
|
+
}
|
|
5067
|
+
const allDependencies = new Map();
|
|
5068
|
+
for (const [name, pod] of pods) {
|
|
5069
|
+
const podDependencies = new Set();
|
|
5070
|
+
if (pod.dependencies) {
|
|
5071
|
+
for (const podDependency of pod.dependencies) {
|
|
5072
|
+
podDependencies.add(podDependency.name);
|
|
5073
|
+
}
|
|
5074
|
+
}
|
|
5075
|
+
allDependencies.set(name, podDependencies);
|
|
5076
|
+
}
|
|
5077
|
+
const targetDependencies = new Map();
|
|
5078
|
+
if (
|
|
5079
|
+
!process.env.COCOA_INCLUDED_TARGETS &&
|
|
5080
|
+
!process.env.COCOA_EXCLUDED_TARGETS
|
|
5081
|
+
) {
|
|
5082
|
+
targetDependencies.set("Pods", podfileLock["DEPENDENCIES"]);
|
|
5083
|
+
} else {
|
|
5084
|
+
const result = executePodCommand(
|
|
5085
|
+
["ipc", "podfile-json", "--silent", podFile],
|
|
5086
|
+
projectPath,
|
|
5087
|
+
options,
|
|
5088
|
+
);
|
|
5089
|
+
const resolvedPodFile = JSON.parse(result.stdout);
|
|
5090
|
+
parsePodfileTargets(
|
|
5091
|
+
resolvedPodFile["target_definitions"][0],
|
|
5092
|
+
targetDependencies,
|
|
5093
|
+
);
|
|
5094
|
+
}
|
|
5095
|
+
const usedTargets = new Set(
|
|
5096
|
+
process.env.COCOA_INCLUDED_TARGETS
|
|
5097
|
+
? process.env.COCOA_INCLUDED_TARGETS.split(",")
|
|
5098
|
+
: targetDependencies.keys(),
|
|
5099
|
+
);
|
|
5100
|
+
if (process.env.COCOA_EXCLUDED_TARGETS) {
|
|
5101
|
+
process.env.COCOA_EXCLUDED_TARGETS.split(",").forEach((excludedTarget) =>
|
|
5102
|
+
usedTargets.delete(excludedTarget),
|
|
5103
|
+
);
|
|
5104
|
+
if (!excludeMessageShown) {
|
|
5105
|
+
thoughtLog(
|
|
5106
|
+
"Wait, the user wants me to exclude certain targets from this CocoaPods project. Perhaps they don't want dev and test projects included in the SBOM 🤔?",
|
|
5107
|
+
);
|
|
5108
|
+
excludeMessageShown = true;
|
|
5109
|
+
}
|
|
5110
|
+
}
|
|
5111
|
+
let addedObjects = new Set();
|
|
5112
|
+
for (const target of usedTargets) {
|
|
5113
|
+
if (targetDependencies.has(target)) {
|
|
5114
|
+
for (const dependency of targetDependencies.get(target)) {
|
|
5115
|
+
let dependencyName = parseCocoaDependency(dependency, false).name;
|
|
5116
|
+
if (!["false", "0"].includes(process.env.COCOA_MERGE_SUBSPECS)) {
|
|
5117
|
+
dependencyName = dependencyName.split("/")[0];
|
|
5118
|
+
}
|
|
5119
|
+
addedObjects.add(dependencyName);
|
|
5120
|
+
}
|
|
5121
|
+
}
|
|
5122
|
+
}
|
|
5123
|
+
let includedDependencies = [
|
|
5124
|
+
{
|
|
5125
|
+
ref: parentComponent["bom-ref"],
|
|
5126
|
+
dependsOn: [
|
|
5127
|
+
...new Set(
|
|
5128
|
+
[...addedObjects].map((obj) => allObjects.get(obj)["bom-ref"]),
|
|
5129
|
+
),
|
|
5130
|
+
],
|
|
5131
|
+
},
|
|
5132
|
+
];
|
|
5133
|
+
const includedObjects = new Set(addedObjects);
|
|
5134
|
+
while (addedObjects.size !== 0) {
|
|
5135
|
+
const newlyAddedObjects = new Set();
|
|
5136
|
+
for (const addedObject of addedObjects) {
|
|
5137
|
+
for (const dependency of allDependencies.get(addedObject)) {
|
|
5138
|
+
if (!includedObjects.has(dependency)) {
|
|
5139
|
+
includedObjects.add(dependency);
|
|
5140
|
+
newlyAddedObjects.add(dependency);
|
|
5141
|
+
}
|
|
5142
|
+
}
|
|
5143
|
+
}
|
|
5144
|
+
addedObjects = newlyAddedObjects;
|
|
5145
|
+
}
|
|
5146
|
+
for (const object of includedObjects) {
|
|
5147
|
+
includedDependencies = mergeDependencies(includedDependencies, [
|
|
5148
|
+
{
|
|
5149
|
+
ref: allObjects.get(object)["bom-ref"],
|
|
5150
|
+
dependsOn: [
|
|
5151
|
+
...new Set(
|
|
5152
|
+
[...allDependencies.get(object)].map(
|
|
5153
|
+
(obj) => allObjects.get(obj)["bom-ref"],
|
|
5154
|
+
),
|
|
5155
|
+
),
|
|
5156
|
+
],
|
|
5157
|
+
},
|
|
5158
|
+
]);
|
|
5159
|
+
}
|
|
5160
|
+
return buildBomNSData(
|
|
5161
|
+
options,
|
|
5162
|
+
[...includedObjects].map((obj) => allObjects.get(obj)),
|
|
5163
|
+
"cocoapods",
|
|
5164
|
+
{
|
|
5165
|
+
src: path,
|
|
5166
|
+
filename: lockFile,
|
|
5167
|
+
dependencies: includedDependencies,
|
|
5168
|
+
parentComponent,
|
|
5169
|
+
},
|
|
5170
|
+
);
|
|
5171
|
+
}
|
|
5172
|
+
}
|
|
5173
|
+
|
|
5015
5174
|
/**
|
|
5016
5175
|
* Function to create bom string for docker compose
|
|
5017
5176
|
*
|
|
@@ -5727,20 +5886,11 @@ export async function createCsharpBom(path, options) {
|
|
|
5727
5886
|
`${options.multiProject ? "**/" : ""}*.sln`,
|
|
5728
5887
|
options,
|
|
5729
5888
|
);
|
|
5730
|
-
|
|
5889
|
+
const csProjFiles = getAllFiles(
|
|
5731
5890
|
path,
|
|
5732
|
-
`${options.multiProject ? "**/" : ""}*.
|
|
5891
|
+
`${options.multiProject ? "**/" : ""}*.{cs,vb,fs,ts,hmi,plc}proj`,
|
|
5733
5892
|
options,
|
|
5734
5893
|
);
|
|
5735
|
-
csProjFiles = csProjFiles.concat(
|
|
5736
|
-
getAllFiles(path, `${options.multiProject ? "**/" : ""}*.vbproj`, options),
|
|
5737
|
-
);
|
|
5738
|
-
csProjFiles = csProjFiles.concat(
|
|
5739
|
-
getAllFiles(path, `${options.multiProject ? "**/" : ""}*.vcxproj`, options),
|
|
5740
|
-
);
|
|
5741
|
-
csProjFiles = csProjFiles.concat(
|
|
5742
|
-
getAllFiles(path, `${options.multiProject ? "**/" : ""}*.fsproj`, options),
|
|
5743
|
-
);
|
|
5744
5894
|
const pkgConfigFiles = getAllFiles(
|
|
5745
5895
|
path,
|
|
5746
5896
|
`${options.multiProject ? "**/" : ""}packages.config`,
|
|
@@ -6095,6 +6245,9 @@ export async function createCsharpBom(path, options) {
|
|
|
6095
6245
|
);
|
|
6096
6246
|
// Create the slices file if it doesn't exist
|
|
6097
6247
|
if (!safeExistsSync(slicesFile)) {
|
|
6248
|
+
thoughtLog(
|
|
6249
|
+
"Alright, the next step is to invoke the dosai command to identify evidence of occurrences for various components.",
|
|
6250
|
+
);
|
|
6098
6251
|
const sliceResult = getDotnetSlices(resolve(path), resolve(slicesFile));
|
|
6099
6252
|
if (!sliceResult && DEBUG_MODE) {
|
|
6100
6253
|
console.log(
|
|
@@ -6523,7 +6676,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
6523
6676
|
// and removing them from metadata.component.components -- the components are merged later
|
|
6524
6677
|
if (bomData.parentComponent.components?.length) {
|
|
6525
6678
|
let bomSubComponents = bomData.parentComponent.components;
|
|
6526
|
-
if (["
|
|
6679
|
+
if (!["false", "0"].includes(process.env.GRADLE_RESOLVE_FROM_NODE)) {
|
|
6527
6680
|
thoughtLog(
|
|
6528
6681
|
"Wait, the user wants me to resolve gradle projects from npm.",
|
|
6529
6682
|
);
|
|
@@ -6977,6 +7130,24 @@ export async function createMultiXBom(pathList, options) {
|
|
|
6977
7130
|
}
|
|
6978
7131
|
}
|
|
6979
7132
|
}
|
|
7133
|
+
if (hasAnyProjectType(["oci", "cocoa"], options)) {
|
|
7134
|
+
bomData = await createCocoaBom(path, options);
|
|
7135
|
+
if (bomData?.bomJson?.components?.length) {
|
|
7136
|
+
if (DEBUG_MODE) {
|
|
7137
|
+
console.log(
|
|
7138
|
+
`Found ${bomData.bomJson.components.length} Cocoa packages at ${path}`,
|
|
7139
|
+
);
|
|
7140
|
+
}
|
|
7141
|
+
components = components.concat(bomData.bomJson.components);
|
|
7142
|
+
dependencies = dependencies.concat(bomData.bomJson.dependencies);
|
|
7143
|
+
if (
|
|
7144
|
+
bomData.parentComponent &&
|
|
7145
|
+
Object.keys(bomData.parentComponent).length
|
|
7146
|
+
) {
|
|
7147
|
+
parentSubComponents.push(bomData.parentComponent);
|
|
7148
|
+
}
|
|
7149
|
+
}
|
|
7150
|
+
}
|
|
6980
7151
|
// Collect any crypto keys
|
|
6981
7152
|
if (options.specVersion >= 1.6 && options.includeCrypto) {
|
|
6982
7153
|
thoughtLog(
|
|
@@ -7207,17 +7378,11 @@ export async function createXBom(path, options) {
|
|
|
7207
7378
|
}
|
|
7208
7379
|
|
|
7209
7380
|
// .Net
|
|
7210
|
-
|
|
7381
|
+
const csProjFiles = getAllFiles(
|
|
7211
7382
|
path,
|
|
7212
|
-
`${options.multiProject ? "**/" : ""}*.
|
|
7383
|
+
`${options.multiProject ? "**/" : ""}*.{cs,vb,fs,ts,hmi,plc}proj`,
|
|
7213
7384
|
options,
|
|
7214
7385
|
);
|
|
7215
|
-
csProjFiles = csProjFiles.concat(
|
|
7216
|
-
getAllFiles(path, `${options.multiProject ? "**/" : ""}*.vbproj`, options),
|
|
7217
|
-
);
|
|
7218
|
-
csProjFiles = csProjFiles.concat(
|
|
7219
|
-
getAllFiles(path, `${options.multiProject ? "**/" : ""}*.fsproj`, options),
|
|
7220
|
-
);
|
|
7221
7386
|
if (csProjFiles.length) {
|
|
7222
7387
|
return await createCsharpBom(path, options);
|
|
7223
7388
|
}
|
|
@@ -7397,6 +7562,16 @@ export async function createXBom(path, options) {
|
|
|
7397
7562
|
if (swiftFiles.length || pkgResolvedFiles.length) {
|
|
7398
7563
|
return await createSwiftBom(path, options);
|
|
7399
7564
|
}
|
|
7565
|
+
|
|
7566
|
+
// Cocoa
|
|
7567
|
+
const cocoaFiles = getAllFiles(
|
|
7568
|
+
path,
|
|
7569
|
+
`${options.multiProject ? "**/" : ""}Podfile`,
|
|
7570
|
+
options,
|
|
7571
|
+
);
|
|
7572
|
+
if (cocoaFiles.length) {
|
|
7573
|
+
return await createCocoaBom(path, options);
|
|
7574
|
+
}
|
|
7400
7575
|
}
|
|
7401
7576
|
|
|
7402
7577
|
/**
|
|
@@ -7550,7 +7725,7 @@ export async function createBom(path, options) {
|
|
|
7550
7725
|
);
|
|
7551
7726
|
} else {
|
|
7552
7727
|
thoughtLog(
|
|
7553
|
-
`The user wants me to focus on a single type, '${projectType}'
|
|
7728
|
+
`The user wants me to focus on a single type, '${projectType}'.`,
|
|
7554
7729
|
);
|
|
7555
7730
|
}
|
|
7556
7731
|
}
|
|
@@ -7636,6 +7811,9 @@ export async function createBom(path, options) {
|
|
|
7636
7811
|
if (PROJECT_TYPE_ALIASES["binary"].includes(projectType[0])) {
|
|
7637
7812
|
return createBinaryBom(path, options);
|
|
7638
7813
|
}
|
|
7814
|
+
if (PROJECT_TYPE_ALIASES["cocoa"].includes(projectType[0])) {
|
|
7815
|
+
return await createCocoaBom(path, options);
|
|
7816
|
+
}
|
|
7639
7817
|
switch (projectType[0]) {
|
|
7640
7818
|
case "jar":
|
|
7641
7819
|
return createJarBom(path, options);
|
package/lib/evinser/evinser.js
CHANGED
|
@@ -619,6 +619,13 @@ export async function parseSliceUsages(
|
|
|
619
619
|
const typesToLookup = new Set();
|
|
620
620
|
const lKeyOverrides = {};
|
|
621
621
|
const usages = slice.usages || [];
|
|
622
|
+
// What should be the line number to use. slice.lineNumber would be quite coarse and could lead to reports such as
|
|
623
|
+
// #1670. Line numbers under targetObj and definedBy is a safe bet for dynamic languages, but occassionally leads to
|
|
624
|
+
// confusion when inter-procedural tracking works better than expected.
|
|
625
|
+
let sliceLineNumber;
|
|
626
|
+
if (["java", "jar"].includes(language)) {
|
|
627
|
+
sliceLineNumber = slice.lineNumber;
|
|
628
|
+
}
|
|
622
629
|
// Annotations from usages
|
|
623
630
|
if (slice.signature?.startsWith("@") && !usages.length) {
|
|
624
631
|
typesToLookup.add(slice.fullName);
|
|
@@ -631,7 +638,9 @@ export async function parseSliceUsages(
|
|
|
631
638
|
}
|
|
632
639
|
for (const ausage of usages) {
|
|
633
640
|
const ausageLine =
|
|
634
|
-
|
|
641
|
+
sliceLineNumber ||
|
|
642
|
+
ausage?.targetObj?.lineNumber ||
|
|
643
|
+
ausage?.definedBy?.lineNumber;
|
|
635
644
|
// First capture the types in the targetObj and definedBy
|
|
636
645
|
for (const atype of [
|
|
637
646
|
[ausage?.targetObj?.isExternal, ausage?.targetObj?.typeFullName],
|
package/lib/evinser/swiftsem.js
CHANGED
|
@@ -585,6 +585,9 @@ export function createSemanticsSlices(basePath, options) {
|
|
|
585
585
|
console.log(
|
|
586
586
|
"TIP: Unable to detect the swift sdk needed to build this project. Try running the swift build command to check if this project builds successfully.",
|
|
587
587
|
);
|
|
588
|
+
console.log(
|
|
589
|
+
"Check whether the project requires xcodebuild to build. Such projects are currently unsupported.",
|
|
590
|
+
);
|
|
588
591
|
return;
|
|
589
592
|
}
|
|
590
593
|
}
|
package/lib/helpers/logger.js
CHANGED