@cyclonedx/cdxgen 11.2.0 → 11.2.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/data/component-tags.json +2 -2
- package/lib/cli/index.js +490 -91
- 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 +895 -26
- package/lib/helpers/utils.test.js +77 -0
- package/lib/helpers/validator.js +10 -1
- package/lib/managers/binary.js +160 -19
- package/lib/managers/docker.js +16 -8
- package/lib/stages/postgen/annotator.js +26 -3
- package/lib/stages/postgen/postgen.js +26 -8
- package/lib/stages/pregen/pregen.js +1 -1
- package/package.json +8 -8
- 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 +70 -1
- package/types/lib/helpers/utils.d.ts.map +1 -1
- package/types/lib/helpers/validator.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts +9 -8
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +2 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
package/lib/helpers/utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Buffer } from "node:buffer";
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
|
-
import { createHash } from "node:crypto";
|
|
3
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
import {
|
|
5
5
|
constants,
|
|
6
6
|
chmodSync,
|
|
@@ -175,6 +175,16 @@ export let metadata_cache = {};
|
|
|
175
175
|
// Speed up lookup namespaces for a given jar
|
|
176
176
|
const jarNSMapping_cache = {};
|
|
177
177
|
|
|
178
|
+
// Temporary files written by cdxgen, will be removed on exit
|
|
179
|
+
const temporaryFiles = new Set();
|
|
180
|
+
process.on("exit", () =>
|
|
181
|
+
temporaryFiles.forEach((tempFile) => {
|
|
182
|
+
if (existsSync(tempFile)) {
|
|
183
|
+
unlinkSync(tempFile);
|
|
184
|
+
}
|
|
185
|
+
}),
|
|
186
|
+
);
|
|
187
|
+
|
|
178
188
|
// Whether test scope shall be included for java/maven projects; default, if unset shall be 'true'
|
|
179
189
|
export const includeMavenTestScope =
|
|
180
190
|
!process.env.CDX_MAVEN_INCLUDE_TEST_SCOPE ||
|
|
@@ -410,6 +420,14 @@ export const PROJECT_TYPE_ALIASES = {
|
|
|
410
420
|
"dotnet-framework48",
|
|
411
421
|
"vb",
|
|
412
422
|
"fsharp",
|
|
423
|
+
"twincat",
|
|
424
|
+
"csproj",
|
|
425
|
+
"tsproj",
|
|
426
|
+
"vbproj",
|
|
427
|
+
"sln",
|
|
428
|
+
"fsproj",
|
|
429
|
+
"plcproj",
|
|
430
|
+
"hmiproj",
|
|
413
431
|
],
|
|
414
432
|
dart: ["dart", "flutter", "pub"],
|
|
415
433
|
haskell: ["haskell", "hackage", "cabal"],
|
|
@@ -448,6 +466,7 @@ export const PROJECT_TYPE_ALIASES = {
|
|
|
448
466
|
],
|
|
449
467
|
binary: ["binary", "blint"],
|
|
450
468
|
oci: ["docker", "oci", "container", "podman"],
|
|
469
|
+
cocoa: ["cocoa", "cocoapods", "objective-c", "swift", "ios"],
|
|
451
470
|
};
|
|
452
471
|
|
|
453
472
|
// Package manager aliases
|
|
@@ -595,12 +614,21 @@ export function isPackageManagerAllowed(name, conflictingManagers, options) {
|
|
|
595
614
|
// HTTP cache
|
|
596
615
|
const gotHttpCache = new Map();
|
|
597
616
|
|
|
617
|
+
function isCacheDisabled() {
|
|
618
|
+
return (
|
|
619
|
+
process.env.CDXGEN_NO_CACHE &&
|
|
620
|
+
["true", "1"].includes(process.env.CDXGEN_NO_CACHE)
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const cache = isCacheDisabled() ? undefined : gotHttpCache;
|
|
625
|
+
|
|
598
626
|
// Custom user-agent for cdxgen
|
|
599
627
|
export const cdxgenAgent = got.extend({
|
|
600
628
|
headers: {
|
|
601
629
|
"user-agent": `@CycloneDX/cdxgen ${_version}`,
|
|
602
630
|
},
|
|
603
|
-
cache
|
|
631
|
+
cache,
|
|
604
632
|
retry: {
|
|
605
633
|
limit: 0,
|
|
606
634
|
},
|
|
@@ -1638,7 +1666,7 @@ export async function parsePkgLock(pkgLockFile, options = {}) {
|
|
|
1638
1666
|
}
|
|
1639
1667
|
|
|
1640
1668
|
/**
|
|
1641
|
-
* Given a lock file this method would return an Object with the
|
|
1669
|
+
* Given a lock file this method would return an Object with the identity as the key and parsed name and value
|
|
1642
1670
|
* eg: "@actions/core@^1.2.6", "@actions/core@^1.6.0":
|
|
1643
1671
|
* version "1.6.0"
|
|
1644
1672
|
* would result in two entries
|
|
@@ -1650,7 +1678,7 @@ export function yarnLockToIdentMap(lockData) {
|
|
|
1650
1678
|
let currentIdents = [];
|
|
1651
1679
|
lockData.split("\n").forEach((l) => {
|
|
1652
1680
|
l = l.replace("\r", "");
|
|
1653
|
-
if (l === "\n" || l.startsWith("#")) {
|
|
1681
|
+
if (l === "\n" || !l.length || l.startsWith("#")) {
|
|
1654
1682
|
return;
|
|
1655
1683
|
}
|
|
1656
1684
|
// "@actions/core@^1.2.6", "@actions/core@^1.6.0":
|
|
@@ -1707,14 +1735,22 @@ export function yarnLockToIdentMap(lockData) {
|
|
|
1707
1735
|
function _parseYarnLine(l) {
|
|
1708
1736
|
let name = "";
|
|
1709
1737
|
let group = "";
|
|
1710
|
-
|
|
1738
|
+
let prefixAtSymbol = l.startsWith("@");
|
|
1711
1739
|
const tmpA = l.split("@");
|
|
1712
1740
|
// ignore possible leading empty strings
|
|
1713
1741
|
if (tmpA[0] === "") {
|
|
1714
1742
|
tmpA.shift();
|
|
1715
1743
|
}
|
|
1744
|
+
let fullName;
|
|
1716
1745
|
if (tmpA.length >= 2) {
|
|
1717
|
-
|
|
1746
|
+
if (tmpA.length === 4) {
|
|
1747
|
+
if (tmpA[1] === "npm:") {
|
|
1748
|
+
prefixAtSymbol = true;
|
|
1749
|
+
}
|
|
1750
|
+
fullName = tmpA[2];
|
|
1751
|
+
} else {
|
|
1752
|
+
fullName = tmpA[0];
|
|
1753
|
+
}
|
|
1718
1754
|
if (fullName.indexOf("/") > -1) {
|
|
1719
1755
|
const parts = fullName.split("/");
|
|
1720
1756
|
group = (prefixAtSymbol ? "@" : "") + parts[0];
|
|
@@ -1891,16 +1927,26 @@ export async function parseYarnLock(yarnLockFile) {
|
|
|
1891
1927
|
if (dgroupname.endsWith(":")) {
|
|
1892
1928
|
dgroupname = dgroupname.substring(0, dgroupname.length - 1);
|
|
1893
1929
|
}
|
|
1894
|
-
let
|
|
1930
|
+
let dgroupnameToUse = dgroupname;
|
|
1931
|
+
const range = tmpA[1].replace(/["']/g, "");
|
|
1932
|
+
let versionRange = range;
|
|
1895
1933
|
// Deal with range with npm: prefix such as npm:string-width@^4.2.0, npm:@types/ioredis@^4.28.10
|
|
1896
1934
|
if (range.startsWith("npm:")) {
|
|
1897
|
-
|
|
1935
|
+
versionRange = range.split("@").splice(-1)[0];
|
|
1936
|
+
dgroupnameToUse = range
|
|
1937
|
+
.replace("npm:", "")
|
|
1938
|
+
.replace(`@${versionRange}`, "");
|
|
1898
1939
|
}
|
|
1899
|
-
const resolvedVersion =
|
|
1940
|
+
const resolvedVersion =
|
|
1941
|
+
identMap[`${dgroupname}|${versionRange}`] ||
|
|
1942
|
+
identMap[`${dgroupnameToUse}|${versionRange}`];
|
|
1943
|
+
// Handle case where the dependency name is really an alias.
|
|
1944
|
+
// Eg: legacy-swc-helpers "npm:@swc/helpers@=0.4.14". Here the dgroupname=@swc/helpers
|
|
1945
|
+
|
|
1900
1946
|
const depPurlString = new PackageURL(
|
|
1901
1947
|
"npm",
|
|
1902
1948
|
null,
|
|
1903
|
-
|
|
1949
|
+
dgroupnameToUse,
|
|
1904
1950
|
resolvedVersion,
|
|
1905
1951
|
null,
|
|
1906
1952
|
null,
|
|
@@ -4152,7 +4198,10 @@ export async function getMvnMetadata(
|
|
|
4152
4198
|
}
|
|
4153
4199
|
const group = p.group || "";
|
|
4154
4200
|
// If the package already has key metadata skip querying maven
|
|
4155
|
-
if (
|
|
4201
|
+
if (
|
|
4202
|
+
!p.version ||
|
|
4203
|
+
(group && p.name && p.version && !shouldFetchLicense() && !force)
|
|
4204
|
+
) {
|
|
4156
4205
|
cdepList.push(p);
|
|
4157
4206
|
continue;
|
|
4158
4207
|
}
|
|
@@ -6932,8 +6981,11 @@ export async function parseGemspecData(gemspecData, gemspecFile) {
|
|
|
6932
6981
|
if (["name", "version"].includes(aprop)) {
|
|
6933
6982
|
value = value.replace(/["']/g, "");
|
|
6934
6983
|
}
|
|
6935
|
-
|
|
6936
|
-
|
|
6984
|
+
// Do not set name=name or version=version
|
|
6985
|
+
if (value !== aprop) {
|
|
6986
|
+
pkg[aprop] = value;
|
|
6987
|
+
break;
|
|
6988
|
+
}
|
|
6937
6989
|
}
|
|
6938
6990
|
}
|
|
6939
6991
|
// Handle common problems
|
|
@@ -7598,11 +7650,11 @@ export async function parseCargoTomlData(
|
|
|
7598
7650
|
pkg.evidence = {
|
|
7599
7651
|
identity: {
|
|
7600
7652
|
field: "purl",
|
|
7601
|
-
confidence: 0.5,
|
|
7653
|
+
confidence: pkg.version ? 0.5 : 0,
|
|
7602
7654
|
methods: [
|
|
7603
7655
|
{
|
|
7604
7656
|
technique: "manifest-analysis",
|
|
7605
|
-
confidence: 0.5,
|
|
7657
|
+
confidence: pkg.version ? 0.5 : 0,
|
|
7606
7658
|
value: cargoTomlFile,
|
|
7607
7659
|
},
|
|
7608
7660
|
],
|
|
@@ -9228,7 +9280,10 @@ export function parseCsProjData(csProjData, projFile, pkgNameVersions = {}) {
|
|
|
9228
9280
|
let gacVersionWarningShown = false;
|
|
9229
9281
|
// First make up a parentcomponent name based on the .csproj file name
|
|
9230
9282
|
if (projFile) {
|
|
9231
|
-
parentComponent.name = basename(projFile).replaceAll(
|
|
9283
|
+
parentComponent.name = basename(projFile).replaceAll(
|
|
9284
|
+
/.(cs|fs|vb|ts|plc|hmi)proj$/g,
|
|
9285
|
+
"",
|
|
9286
|
+
);
|
|
9232
9287
|
}
|
|
9233
9288
|
// Collect details about the parent component
|
|
9234
9289
|
if (project?.PropertyGroup?.length) {
|
|
@@ -9240,6 +9295,13 @@ export function parseCsProjData(csProjData, projFile, pkgNameVersions = {}) {
|
|
|
9240
9295
|
Array.isArray(apg.AssemblyName[0]._)
|
|
9241
9296
|
) {
|
|
9242
9297
|
parentComponent.name = apg.AssemblyName[0]._[0];
|
|
9298
|
+
} else if (
|
|
9299
|
+
apg?.Name &&
|
|
9300
|
+
Array.isArray(apg.Name) &&
|
|
9301
|
+
apg.Name[0]._ &&
|
|
9302
|
+
Array.isArray(apg.Name[0]._)
|
|
9303
|
+
) {
|
|
9304
|
+
parentComponent.name = apg.Name[0]._[0];
|
|
9243
9305
|
}
|
|
9244
9306
|
if (
|
|
9245
9307
|
apg?.ProductVersion &&
|
|
@@ -9248,6 +9310,20 @@ export function parseCsProjData(csProjData, projFile, pkgNameVersions = {}) {
|
|
|
9248
9310
|
Array.isArray(apg.ProductVersion[0]._)
|
|
9249
9311
|
) {
|
|
9250
9312
|
parentComponent.version = apg.ProductVersion[0]._[0];
|
|
9313
|
+
} else if (
|
|
9314
|
+
apg?.ProgramVersion &&
|
|
9315
|
+
Array.isArray(apg.ProgramVersion) &&
|
|
9316
|
+
apg.ProgramVersion[0]._ &&
|
|
9317
|
+
Array.isArray(apg.ProgramVersion[0]._)
|
|
9318
|
+
) {
|
|
9319
|
+
parentComponent.version = apg.ProgramVersion[0]._[0];
|
|
9320
|
+
} else if (
|
|
9321
|
+
apg?.HmiVersion &&
|
|
9322
|
+
Array.isArray(apg.HmiVersion) &&
|
|
9323
|
+
apg.HmiVersion[0]._ &&
|
|
9324
|
+
Array.isArray(apg.HmiVersion[0]._)
|
|
9325
|
+
) {
|
|
9326
|
+
parentComponent.version = apg.HmiVersion[0]._[0];
|
|
9251
9327
|
}
|
|
9252
9328
|
if (
|
|
9253
9329
|
apg?.OutputType &&
|
|
@@ -9328,6 +9404,17 @@ export function parseCsProjData(csProjData, projFile, pkgNameVersions = {}) {
|
|
|
9328
9404
|
});
|
|
9329
9405
|
}
|
|
9330
9406
|
}
|
|
9407
|
+
if (
|
|
9408
|
+
apg?.AzureFunctionsVersion &&
|
|
9409
|
+
Array.isArray(apg.AzureFunctionsVersion) &&
|
|
9410
|
+
apg.AzureFunctionsVersion[0]._ &&
|
|
9411
|
+
Array.isArray(apg.AzureFunctionsVersion[0]._)
|
|
9412
|
+
) {
|
|
9413
|
+
parentComponent.properties.push({
|
|
9414
|
+
name: "cdx:dotnet:azure_functions_version",
|
|
9415
|
+
value: apg.AzureFunctionsVersion[0]._[0],
|
|
9416
|
+
});
|
|
9417
|
+
}
|
|
9331
9418
|
if (
|
|
9332
9419
|
apg?.Description &&
|
|
9333
9420
|
Array.isArray(apg.Description) &&
|
|
@@ -9476,11 +9563,13 @@ export function parseCsProjData(csProjData, projFile, pkgNameVersions = {}) {
|
|
|
9476
9563
|
}
|
|
9477
9564
|
}
|
|
9478
9565
|
}
|
|
9479
|
-
|
|
9480
|
-
|
|
9481
|
-
|
|
9482
|
-
|
|
9483
|
-
|
|
9566
|
+
// If the parent still lacks a purl, add one based on the name and version
|
|
9567
|
+
if (parentComponent && !parentComponent.purl && parentComponent.name) {
|
|
9568
|
+
parentComponent.purl = `pkg:nuget/${parentComponent.name}@${
|
|
9569
|
+
parentComponent.version || "latest"
|
|
9570
|
+
}`;
|
|
9571
|
+
}
|
|
9572
|
+
if (parentComponent?.purl) {
|
|
9484
9573
|
parentComponent["bom-ref"] = parentComponent.purl;
|
|
9485
9574
|
}
|
|
9486
9575
|
let dependencies = [];
|
|
@@ -11431,9 +11520,10 @@ export async function extractJarArchive(jarFile, tempDir, jarNSMapping = {}) {
|
|
|
11431
11520
|
}
|
|
11432
11521
|
}
|
|
11433
11522
|
}
|
|
11523
|
+
let jarMetadata;
|
|
11434
11524
|
if ((!group || !name || !version) && safeExistsSync(manifestFile)) {
|
|
11435
11525
|
confidence = 0.8;
|
|
11436
|
-
|
|
11526
|
+
jarMetadata = parseJarManifest(
|
|
11437
11527
|
readFileSync(manifestFile, {
|
|
11438
11528
|
encoding: "utf-8",
|
|
11439
11529
|
}),
|
|
@@ -11506,7 +11596,10 @@ export async function extractJarArchive(jarFile, tempDir, jarNSMapping = {}) {
|
|
|
11506
11596
|
// if group is empty use name as group
|
|
11507
11597
|
group = group === "." ? name : group || name;
|
|
11508
11598
|
}
|
|
11509
|
-
if (name
|
|
11599
|
+
if (name) {
|
|
11600
|
+
if (!version) {
|
|
11601
|
+
confidence = 0;
|
|
11602
|
+
}
|
|
11510
11603
|
const apkg = {
|
|
11511
11604
|
group: group ? encodeForPurl(group) : "",
|
|
11512
11605
|
name: name ? encodeForPurl(name) : "",
|
|
@@ -11535,7 +11628,7 @@ export async function extractJarArchive(jarFile, tempDir, jarNSMapping = {}) {
|
|
|
11535
11628
|
properties: [
|
|
11536
11629
|
{
|
|
11537
11630
|
name: "SrcFile",
|
|
11538
|
-
value:
|
|
11631
|
+
value: jf,
|
|
11539
11632
|
},
|
|
11540
11633
|
],
|
|
11541
11634
|
};
|
|
@@ -11895,6 +11988,607 @@ export function splitOutputByGradleProjects(rawOutput, relevantTasks) {
|
|
|
11895
11988
|
return outputSplitBySubprojects;
|
|
11896
11989
|
}
|
|
11897
11990
|
|
|
11991
|
+
/**
|
|
11992
|
+
* Parse the contents of a 'Podfile.lock'
|
|
11993
|
+
*
|
|
11994
|
+
* @param {Object} podfileLock The content of the podfile.lock as an Object
|
|
11995
|
+
* @param {String} projectPath The path to the project root
|
|
11996
|
+
* @returns {Map} Map of all dependencies with their direct dependencies
|
|
11997
|
+
*/
|
|
11998
|
+
export async function parsePodfileLock(podfileLock, projectPath) {
|
|
11999
|
+
const dependencies = new Map();
|
|
12000
|
+
for (const pod of podfileLock["PODS"]) {
|
|
12001
|
+
const dependency = {};
|
|
12002
|
+
if (pod.constructor === Object) {
|
|
12003
|
+
for (const key in pod) {
|
|
12004
|
+
dependency.metadata = parseCocoaDependency(key);
|
|
12005
|
+
const subDependencies = new Set();
|
|
12006
|
+
for (const subPod of pod[key]) {
|
|
12007
|
+
subDependencies.add(parseCocoaDependency(subPod, false));
|
|
12008
|
+
}
|
|
12009
|
+
dependency.dependencies = Array.from(subDependencies);
|
|
12010
|
+
}
|
|
12011
|
+
} else {
|
|
12012
|
+
dependency.metadata = parseCocoaDependency(pod);
|
|
12013
|
+
}
|
|
12014
|
+
const podName = dependency.metadata.name.includes("/")
|
|
12015
|
+
? dependency.metadata.name.substring(
|
|
12016
|
+
0,
|
|
12017
|
+
dependency.metadata.name.indexOf("/"),
|
|
12018
|
+
)
|
|
12019
|
+
: dependency.metadata.name;
|
|
12020
|
+
if (podfileLock["EXTERNAL SOURCES"]?.[podName]) {
|
|
12021
|
+
const externalPod = podfileLock["EXTERNAL SOURCES"][podName];
|
|
12022
|
+
if (externalPod[":podspec"]) {
|
|
12023
|
+
const podspecLocation = resolve(projectPath, externalPod[":podspec"]);
|
|
12024
|
+
dependency.metadata.properties = [
|
|
12025
|
+
{
|
|
12026
|
+
name: "cdx:pods:projectDir",
|
|
12027
|
+
value: dirname(podspecLocation),
|
|
12028
|
+
},
|
|
12029
|
+
{
|
|
12030
|
+
name: "cdx:pods:podspecLocation",
|
|
12031
|
+
value: podspecLocation,
|
|
12032
|
+
},
|
|
12033
|
+
];
|
|
12034
|
+
} else {
|
|
12035
|
+
const projectLocation = resolve(projectPath, externalPod[":path"]);
|
|
12036
|
+
dependency.metadata.properties = [
|
|
12037
|
+
{
|
|
12038
|
+
name: "cdx:pods:projectDir",
|
|
12039
|
+
value: projectLocation,
|
|
12040
|
+
},
|
|
12041
|
+
];
|
|
12042
|
+
let podspec = join(projectLocation, `${podName}.podspec`);
|
|
12043
|
+
if (!existsSync(podspec)) {
|
|
12044
|
+
podspec = `${podspec}.json`;
|
|
12045
|
+
}
|
|
12046
|
+
if (existsSync(podspec)) {
|
|
12047
|
+
dependency.metadata.properties.push({
|
|
12048
|
+
name: "cdx:pods:podspecLocation",
|
|
12049
|
+
value: podspec,
|
|
12050
|
+
});
|
|
12051
|
+
}
|
|
12052
|
+
}
|
|
12053
|
+
}
|
|
12054
|
+
dependencies.set(dependency.metadata.name, dependency);
|
|
12055
|
+
}
|
|
12056
|
+
if (!["false", "0"].includes(process.env.COCOA_MERGE_SUBSPECS)) {
|
|
12057
|
+
for (const subspecComponentName of [...dependencies.keys()].filter((name) =>
|
|
12058
|
+
name.includes("/"),
|
|
12059
|
+
)) {
|
|
12060
|
+
const subspecComponent = dependencies.get(subspecComponentName);
|
|
12061
|
+
const mainComponentName = subspecComponentName.split("/")[0];
|
|
12062
|
+
let mainComponent = dependencies.get(mainComponentName);
|
|
12063
|
+
if (!mainComponent) {
|
|
12064
|
+
mainComponent = {
|
|
12065
|
+
metadata: {
|
|
12066
|
+
name: mainComponentName,
|
|
12067
|
+
version: subspecComponent.metadata.version,
|
|
12068
|
+
},
|
|
12069
|
+
};
|
|
12070
|
+
dependencies.set(mainComponentName, mainComponent);
|
|
12071
|
+
}
|
|
12072
|
+
if (subspecComponent.dependencies) {
|
|
12073
|
+
if (mainComponent.dependencies) {
|
|
12074
|
+
mainComponent.dependencies = [
|
|
12075
|
+
...mainComponent.dependencies,
|
|
12076
|
+
...subspecComponent.dependencies,
|
|
12077
|
+
];
|
|
12078
|
+
} else {
|
|
12079
|
+
mainComponent.dependencies = subspecComponent.dependencies;
|
|
12080
|
+
}
|
|
12081
|
+
}
|
|
12082
|
+
mainComponent.metadata.properties = [
|
|
12083
|
+
...(mainComponent.metadata.properties
|
|
12084
|
+
? mainComponent.metadata.properties
|
|
12085
|
+
: []),
|
|
12086
|
+
{
|
|
12087
|
+
name: "cdx:pods:Subspec",
|
|
12088
|
+
value: subspecComponentName.substring(
|
|
12089
|
+
subspecComponentName.indexOf("/") + 1,
|
|
12090
|
+
),
|
|
12091
|
+
},
|
|
12092
|
+
...(subspecComponent.metadata.propertie
|
|
12093
|
+
? subspecComponent.metadata.properties
|
|
12094
|
+
: []),
|
|
12095
|
+
];
|
|
12096
|
+
dependencies.delete(subspecComponentName);
|
|
12097
|
+
}
|
|
12098
|
+
for (const [dependencyName, dependency] of dependencies) {
|
|
12099
|
+
if (dependency.dependencies) {
|
|
12100
|
+
dependency.dependencies.forEach(
|
|
12101
|
+
(dep) => (dep.name = dep.name.split("/")[0]),
|
|
12102
|
+
);
|
|
12103
|
+
dependency.dependencies = [
|
|
12104
|
+
...new Map(
|
|
12105
|
+
dependency.dependencies
|
|
12106
|
+
.filter((dep) => dep.name !== dependencyName)
|
|
12107
|
+
.map((dep) => [dep.name, dep]),
|
|
12108
|
+
).values(),
|
|
12109
|
+
];
|
|
12110
|
+
if (dependency.dependencies.length === 0) {
|
|
12111
|
+
delete dependency.dependencies;
|
|
12112
|
+
}
|
|
12113
|
+
}
|
|
12114
|
+
}
|
|
12115
|
+
}
|
|
12116
|
+
return dependencies;
|
|
12117
|
+
}
|
|
12118
|
+
|
|
12119
|
+
/**
|
|
12120
|
+
* Parse all targets and their direct dependencies from the 'Podfile'
|
|
12121
|
+
*
|
|
12122
|
+
* @param {Object} target A JSON-object representing a target
|
|
12123
|
+
* @param {Map} allDependencies The map containing all parsed direct dependencies for a target
|
|
12124
|
+
* @param {String} [prefix=undefined] Prefix to add to the targets name
|
|
12125
|
+
*/
|
|
12126
|
+
export function parsePodfileTargets(
|
|
12127
|
+
target,
|
|
12128
|
+
allDependencies,
|
|
12129
|
+
prefix = undefined,
|
|
12130
|
+
) {
|
|
12131
|
+
const targetName = (prefix ? `${prefix}/` : "") + target.name;
|
|
12132
|
+
const targetDependencies = new Set(
|
|
12133
|
+
prefix && allDependencies.has(prefix)
|
|
12134
|
+
? allDependencies.get(prefix)
|
|
12135
|
+
: targetName !== "Pods"
|
|
12136
|
+
? allDependencies.get("Pods")
|
|
12137
|
+
: [],
|
|
12138
|
+
);
|
|
12139
|
+
if (target["dependencies"]) {
|
|
12140
|
+
for (const targetDependency of target["dependencies"]) {
|
|
12141
|
+
if (targetDependency.constructor === Object) {
|
|
12142
|
+
targetDependencies.add(Object.keys(targetDependency)[0]);
|
|
12143
|
+
} else {
|
|
12144
|
+
targetDependencies.add(targetDependency);
|
|
12145
|
+
}
|
|
12146
|
+
}
|
|
12147
|
+
}
|
|
12148
|
+
allDependencies.set(targetName, Array.from(targetDependencies));
|
|
12149
|
+
if (target.children) {
|
|
12150
|
+
const childPrefix = targetName === "Pods" ? undefined : targetName;
|
|
12151
|
+
for (const childTarget of target.children) {
|
|
12152
|
+
parsePodfileTargets(childTarget, allDependencies, childPrefix);
|
|
12153
|
+
}
|
|
12154
|
+
}
|
|
12155
|
+
}
|
|
12156
|
+
|
|
12157
|
+
/**
|
|
12158
|
+
* Parse a single line representing a dependency
|
|
12159
|
+
*
|
|
12160
|
+
* @param {String} dependencyLine The line that should be parsed as a dependency
|
|
12161
|
+
* @param {boolean} [parseVersion=true] Include parsing the version of the dependency
|
|
12162
|
+
* @returns {Object} Object representing a dependency
|
|
12163
|
+
*/
|
|
12164
|
+
export function parseCocoaDependency(dependencyLine, parseVersion = true) {
|
|
12165
|
+
const dependencyData = dependencyLine.split(" (");
|
|
12166
|
+
const dependency = { name: dependencyData[0] };
|
|
12167
|
+
if (parseVersion) {
|
|
12168
|
+
dependency.version = dependencyData[1].substring(
|
|
12169
|
+
0,
|
|
12170
|
+
dependencyData[1].length - 1,
|
|
12171
|
+
);
|
|
12172
|
+
}
|
|
12173
|
+
return dependency;
|
|
12174
|
+
}
|
|
12175
|
+
|
|
12176
|
+
/**
|
|
12177
|
+
* Execute the 'pod'-command with parameters
|
|
12178
|
+
*
|
|
12179
|
+
* @param {String[]} parameters The parameters for the command
|
|
12180
|
+
* @param {String} path The path where the command should be executed
|
|
12181
|
+
* @param {Object} options CLI options
|
|
12182
|
+
* @returns {Object} The result of running the command
|
|
12183
|
+
*/
|
|
12184
|
+
export function executePodCommand(parameters, path, options) {
|
|
12185
|
+
if (DEBUG_MODE) {
|
|
12186
|
+
if (path) {
|
|
12187
|
+
console.log("Executing pod", parameters.join(" "), "in", path);
|
|
12188
|
+
} else {
|
|
12189
|
+
console.log("Executing pod", parameters.join(" "));
|
|
12190
|
+
}
|
|
12191
|
+
}
|
|
12192
|
+
const result = spawnSync(process.env.POD_CMD || "pod", parameters, {
|
|
12193
|
+
cwd: path,
|
|
12194
|
+
encoding: "utf-8",
|
|
12195
|
+
shell: isWin,
|
|
12196
|
+
maxBuffer: MAX_BUFFER,
|
|
12197
|
+
});
|
|
12198
|
+
if (result.status !== 0 || result.error) {
|
|
12199
|
+
if (result?.stderr?.includes("Unable to find a pod")) {
|
|
12200
|
+
console.log(
|
|
12201
|
+
"Try again by running 'pod install' before invoking 'cdxgen'.",
|
|
12202
|
+
);
|
|
12203
|
+
}
|
|
12204
|
+
if (process.env?.CDXGEN_IN_CONTAINER !== "true") {
|
|
12205
|
+
console.log(
|
|
12206
|
+
"Consider using the cdxgen container image (`ghcr.io/cyclonedx/cdxgen`), which includes cocoapods and additional build tools.",
|
|
12207
|
+
);
|
|
12208
|
+
} else if (!DEBUG_MODE) {
|
|
12209
|
+
console.log(
|
|
12210
|
+
"Something went wrong when trying to execute cocoapods -- Set the environment variable 'CDXGEN_DEBUG_MODE=debug' to troubleshoot cocoapods related errors",
|
|
12211
|
+
);
|
|
12212
|
+
}
|
|
12213
|
+
if (options.failOnError || DEBUG_MODE) {
|
|
12214
|
+
if (result.stdout) {
|
|
12215
|
+
console.log(result.stdout);
|
|
12216
|
+
}
|
|
12217
|
+
if (result.stderr) {
|
|
12218
|
+
console.log(result.stderr);
|
|
12219
|
+
}
|
|
12220
|
+
options.failOnError && process.exit(1);
|
|
12221
|
+
}
|
|
12222
|
+
}
|
|
12223
|
+
return result;
|
|
12224
|
+
}
|
|
12225
|
+
|
|
12226
|
+
/**
|
|
12227
|
+
* Method that handles object creation for cocoa pods.
|
|
12228
|
+
*
|
|
12229
|
+
* @param {Object} dependency The dependency that is to be transformed into an SBOM object
|
|
12230
|
+
* @param {Object} options CLI options
|
|
12231
|
+
* @param {String} [type="library"] The type of Object to create
|
|
12232
|
+
* @returns {Object} An object representing the pod in SBOM-format
|
|
12233
|
+
*/
|
|
12234
|
+
export async function buildObjectForCocoaPod(
|
|
12235
|
+
dependency,
|
|
12236
|
+
options,
|
|
12237
|
+
type = "library",
|
|
12238
|
+
) {
|
|
12239
|
+
let component;
|
|
12240
|
+
if (
|
|
12241
|
+
!["false", "0"].includes(process.env.COCOA_RESOLVE_FROM_NODE) &&
|
|
12242
|
+
dependency.properties?.find(({ name }) => name === "cdx:pods:projectDir")
|
|
12243
|
+
) {
|
|
12244
|
+
let tmpDir = dependency.properties.find(
|
|
12245
|
+
({ name }) => name === "cdx:pods:projectDir",
|
|
12246
|
+
).value;
|
|
12247
|
+
const exclusionDirs = process.env.COCOA_RESOLVE_FROM_NODE_EXCLUSION_DIRS
|
|
12248
|
+
? process.env.COCOA_RESOLVE_FROM_NODE_EXCLUSION_DIRS.split(",")
|
|
12249
|
+
: [];
|
|
12250
|
+
if (
|
|
12251
|
+
tmpDir &&
|
|
12252
|
+
!exclusionDirs.some((dir) =>
|
|
12253
|
+
`${tmpDir.replaceAll("\\", "/")}/`.includes(
|
|
12254
|
+
`/${dir.replaceAll("\\", "/")}/`.replaceAll("//", "/"),
|
|
12255
|
+
),
|
|
12256
|
+
) &&
|
|
12257
|
+
tmpDir.indexOf("node_modules") !== -1
|
|
12258
|
+
) {
|
|
12259
|
+
do {
|
|
12260
|
+
const npmPackages = await parsePkgJson(join(tmpDir, "package.json"));
|
|
12261
|
+
if (npmPackages.length === 1) {
|
|
12262
|
+
component = npmPackages[0];
|
|
12263
|
+
component.type = "library";
|
|
12264
|
+
component.properties = component.properties.concat(
|
|
12265
|
+
{
|
|
12266
|
+
name: "cdx:pods:PodName",
|
|
12267
|
+
value: dependency.name,
|
|
12268
|
+
},
|
|
12269
|
+
dependency.properties,
|
|
12270
|
+
);
|
|
12271
|
+
tmpDir = undefined;
|
|
12272
|
+
} else {
|
|
12273
|
+
tmpDir = dirname(tmpDir);
|
|
12274
|
+
}
|
|
12275
|
+
} while (tmpDir && tmpDir.indexOf("node_modules") !== -1);
|
|
12276
|
+
}
|
|
12277
|
+
}
|
|
12278
|
+
if (!component) {
|
|
12279
|
+
let name = dependency.name;
|
|
12280
|
+
let subspec = null;
|
|
12281
|
+
const locationOfSubspec = dependency.name.indexOf("/");
|
|
12282
|
+
if (locationOfSubspec !== -1) {
|
|
12283
|
+
name = dependency.name.substring(0, locationOfSubspec);
|
|
12284
|
+
subspec = dependency.name.substring(locationOfSubspec + 1);
|
|
12285
|
+
}
|
|
12286
|
+
component = {
|
|
12287
|
+
...dependency,
|
|
12288
|
+
type,
|
|
12289
|
+
};
|
|
12290
|
+
if (subspec) {
|
|
12291
|
+
if (!component.properties) {
|
|
12292
|
+
component.properties = [];
|
|
12293
|
+
}
|
|
12294
|
+
component.properties.push({
|
|
12295
|
+
name: "cdx:pods:Subspec",
|
|
12296
|
+
value: subspec,
|
|
12297
|
+
});
|
|
12298
|
+
}
|
|
12299
|
+
const purl = new PackageURL(
|
|
12300
|
+
"cocoapods",
|
|
12301
|
+
"",
|
|
12302
|
+
name,
|
|
12303
|
+
component.version,
|
|
12304
|
+
null,
|
|
12305
|
+
subspec,
|
|
12306
|
+
).toString();
|
|
12307
|
+
component["purl"] = purl;
|
|
12308
|
+
component["bom-ref"] = decodeURIComponent(purl);
|
|
12309
|
+
if (options && !["false", "0"].includes(process.env.COCOA_FULL_SCAN)) {
|
|
12310
|
+
fullScanCocoaPod(dependency, component, options);
|
|
12311
|
+
}
|
|
12312
|
+
}
|
|
12313
|
+
return component;
|
|
12314
|
+
}
|
|
12315
|
+
|
|
12316
|
+
function fullScanCocoaPod(dependency, component, options) {
|
|
12317
|
+
let result;
|
|
12318
|
+
if (
|
|
12319
|
+
component.properties?.find(({ name }) => name === "cdx:pods:projectDir")
|
|
12320
|
+
) {
|
|
12321
|
+
if (
|
|
12322
|
+
component.properties.find(
|
|
12323
|
+
({ name }) => name === "cdx:pods:podspecLocation",
|
|
12324
|
+
)
|
|
12325
|
+
) {
|
|
12326
|
+
let podspecLocation = component.properties.find(
|
|
12327
|
+
({ name }) => name === "cdx:pods:podspecLocation",
|
|
12328
|
+
).value;
|
|
12329
|
+
component.properties.push({
|
|
12330
|
+
name: "SrcFile",
|
|
12331
|
+
value: podspecLocation,
|
|
12332
|
+
});
|
|
12333
|
+
let replacements;
|
|
12334
|
+
if (
|
|
12335
|
+
podspecLocation.endsWith(".podspec") &&
|
|
12336
|
+
process.env.COCOA_PODSPEC_REPLACEMENTS
|
|
12337
|
+
) {
|
|
12338
|
+
replacements = process.env.COCOA_PODSPEC_REPLACEMENTS.split(";");
|
|
12339
|
+
} else if (
|
|
12340
|
+
podspecLocation.endsWith(".json") &&
|
|
12341
|
+
process.env.COCOA_PODSPEC_JSON_REPLACEMENTS
|
|
12342
|
+
) {
|
|
12343
|
+
replacements = process.env.COCOA_PODSPEC_JSON_REPLACEMENTS.split(";");
|
|
12344
|
+
}
|
|
12345
|
+
if (replacements) {
|
|
12346
|
+
let podspecContent = readFileSync(podspecLocation, "utf-8");
|
|
12347
|
+
for (const replacement of replacements) {
|
|
12348
|
+
const replacementPair = replacement.split("=");
|
|
12349
|
+
let match = replacementPair[0].replaceAll("<NEWLINE>", "\n");
|
|
12350
|
+
if (match.startsWith("/") && match.endsWith("/")) {
|
|
12351
|
+
match = new RegExp(match.substring(1, match.length - 1), "g");
|
|
12352
|
+
}
|
|
12353
|
+
const repl = replacementPair[1].replaceAll("<NEWLINE>", "\n");
|
|
12354
|
+
podspecContent = podspecContent.replaceAll(match, repl);
|
|
12355
|
+
}
|
|
12356
|
+
podspecLocation = join(
|
|
12357
|
+
dirname(podspecLocation),
|
|
12358
|
+
`${randomUUID()}.${podspecLocation.substring(podspecLocation.lastIndexOf(".") + 1)}`,
|
|
12359
|
+
);
|
|
12360
|
+
writeFileSync(podspecLocation, podspecContent);
|
|
12361
|
+
temporaryFiles.add(podspecLocation);
|
|
12362
|
+
}
|
|
12363
|
+
result = executePodCommand(
|
|
12364
|
+
["ipc", "spec", "--silent", podspecLocation],
|
|
12365
|
+
undefined,
|
|
12366
|
+
options,
|
|
12367
|
+
);
|
|
12368
|
+
} else {
|
|
12369
|
+
console.warn(
|
|
12370
|
+
`Unable to do a full scan of local pod '${dependency.name}', because either its podspec doesn't exist, or it wasn't found with the currently implemented algorithms.\nIf you think this is an error, please file an issue on GitHub!`,
|
|
12371
|
+
);
|
|
12372
|
+
return;
|
|
12373
|
+
}
|
|
12374
|
+
} else {
|
|
12375
|
+
let dependencyName = dependency.name;
|
|
12376
|
+
if (dependencyName.includes("/")) {
|
|
12377
|
+
dependencyName = dependencyName.substring(0, dependencyName.indexOf("/"));
|
|
12378
|
+
}
|
|
12379
|
+
const srcFileProperty = {
|
|
12380
|
+
name: "SrcFile",
|
|
12381
|
+
value: executePodCommand(
|
|
12382
|
+
["spec", "which", dependencyName, `--version=${dependency.version}`],
|
|
12383
|
+
undefined,
|
|
12384
|
+
options,
|
|
12385
|
+
).stdout.trim(),
|
|
12386
|
+
};
|
|
12387
|
+
if (component.properties) {
|
|
12388
|
+
component.properties.push(srcFileProperty);
|
|
12389
|
+
} else {
|
|
12390
|
+
component.properties = [srcFileProperty];
|
|
12391
|
+
}
|
|
12392
|
+
result = executePodCommand(
|
|
12393
|
+
["spec", "cat", dependencyName, `--version=${dependency.version}`],
|
|
12394
|
+
undefined,
|
|
12395
|
+
options,
|
|
12396
|
+
);
|
|
12397
|
+
}
|
|
12398
|
+
const podspecText = result.stdout;
|
|
12399
|
+
let podspec;
|
|
12400
|
+
try {
|
|
12401
|
+
podspec = JSON.parse(
|
|
12402
|
+
podspecText.substring(
|
|
12403
|
+
podspecText.indexOf("{"),
|
|
12404
|
+
podspecText.lastIndexOf("}") + 1,
|
|
12405
|
+
),
|
|
12406
|
+
);
|
|
12407
|
+
} catch (e) {
|
|
12408
|
+
return;
|
|
12409
|
+
}
|
|
12410
|
+
const externalRefs = [];
|
|
12411
|
+
if (podspec.authors) {
|
|
12412
|
+
component.authors = [];
|
|
12413
|
+
if (podspec.authors.constructor === Object) {
|
|
12414
|
+
Object.entries(podspec.authors).forEach(([name, email]) =>
|
|
12415
|
+
component.authors.push({ name, email }),
|
|
12416
|
+
);
|
|
12417
|
+
} else if (podspec.authors.constructor === Array) {
|
|
12418
|
+
podspec.authors.forEach((name) => component.authors.push({ name }));
|
|
12419
|
+
} else {
|
|
12420
|
+
component.authors.push({ name: podspec.authors });
|
|
12421
|
+
}
|
|
12422
|
+
}
|
|
12423
|
+
if (podspec.description) {
|
|
12424
|
+
component.description = podspec.description;
|
|
12425
|
+
} else if (podspec.summary) {
|
|
12426
|
+
component.description = podspec.summary;
|
|
12427
|
+
}
|
|
12428
|
+
if (podspec.documentation_url) {
|
|
12429
|
+
externalRefs.push({
|
|
12430
|
+
type: "documentation",
|
|
12431
|
+
url: podspec.documentation_url,
|
|
12432
|
+
});
|
|
12433
|
+
} else if (podspec.readme) {
|
|
12434
|
+
externalRefs.push({
|
|
12435
|
+
type: "documentation",
|
|
12436
|
+
url: podspec.readme,
|
|
12437
|
+
});
|
|
12438
|
+
}
|
|
12439
|
+
if (podspec.homepage) {
|
|
12440
|
+
externalRefs.push({
|
|
12441
|
+
type: "website",
|
|
12442
|
+
url: podspec.homepage,
|
|
12443
|
+
});
|
|
12444
|
+
}
|
|
12445
|
+
if (podspec.license) {
|
|
12446
|
+
if (podspec.license.constructor === Object) {
|
|
12447
|
+
if (podspec.license.type === "Copyright") {
|
|
12448
|
+
component.copyright = podspec.license.text;
|
|
12449
|
+
} else {
|
|
12450
|
+
component.licenses = [{ license: {} }];
|
|
12451
|
+
if (spdxLicenses.includes(podspec.license.type)) {
|
|
12452
|
+
component.licenses[0].license.id = podspec.license.type;
|
|
12453
|
+
} else {
|
|
12454
|
+
component.licenses[0].license.name = podspec.license.type;
|
|
12455
|
+
}
|
|
12456
|
+
const licenseText = [];
|
|
12457
|
+
if (podspec.license.text) {
|
|
12458
|
+
if (podspec.license.text.startsWith("http")) {
|
|
12459
|
+
component.licenses[0].license.url = podspec.license.text;
|
|
12460
|
+
} else {
|
|
12461
|
+
licenseText.push(podspec.license.text);
|
|
12462
|
+
}
|
|
12463
|
+
}
|
|
12464
|
+
if (podspec.license.file) {
|
|
12465
|
+
if (podspec.license.file.startsWith("http")) {
|
|
12466
|
+
if (component.licenses[0].license.url) {
|
|
12467
|
+
if (licenseText.length !== 0) {
|
|
12468
|
+
licenseText.push("");
|
|
12469
|
+
}
|
|
12470
|
+
licenseText.push(
|
|
12471
|
+
`See also: ${component.licenses[0].license.url}`,
|
|
12472
|
+
);
|
|
12473
|
+
}
|
|
12474
|
+
component.licenses[0].license.url = podspec.license.file;
|
|
12475
|
+
} else {
|
|
12476
|
+
if (licenseText.length !== 0) {
|
|
12477
|
+
licenseText.push("");
|
|
12478
|
+
}
|
|
12479
|
+
licenseText.push(`See license in file '${podspec.license.file}'`);
|
|
12480
|
+
}
|
|
12481
|
+
}
|
|
12482
|
+
if (licenseText.length !== 0) {
|
|
12483
|
+
component.licenses[0].license.text = {
|
|
12484
|
+
content: licenseText.join("\n"),
|
|
12485
|
+
};
|
|
12486
|
+
}
|
|
12487
|
+
}
|
|
12488
|
+
} else {
|
|
12489
|
+
if (spdxLicenses.includes(podspec.license)) {
|
|
12490
|
+
component.licenses = [{ license: { id: podspec.license } }];
|
|
12491
|
+
} else {
|
|
12492
|
+
component.licenses = [{ license: { name: podspec.license } }];
|
|
12493
|
+
}
|
|
12494
|
+
}
|
|
12495
|
+
}
|
|
12496
|
+
if (podspec.social_media_url) {
|
|
12497
|
+
externalRefs.push({
|
|
12498
|
+
type: "social",
|
|
12499
|
+
url: podspec.social_media_url,
|
|
12500
|
+
});
|
|
12501
|
+
}
|
|
12502
|
+
if (podspec.source) {
|
|
12503
|
+
const comment = [];
|
|
12504
|
+
if (podspec.source.http) {
|
|
12505
|
+
const sourceDistro = {
|
|
12506
|
+
type: "source-distribution",
|
|
12507
|
+
url: podspec.source.http,
|
|
12508
|
+
};
|
|
12509
|
+
const hashes = [];
|
|
12510
|
+
if (podspec.source.http.sha1) {
|
|
12511
|
+
hashes.push({
|
|
12512
|
+
alg: "SHA-1",
|
|
12513
|
+
content: podspec.source.http.sha1,
|
|
12514
|
+
});
|
|
12515
|
+
}
|
|
12516
|
+
if (podspec.source.http.sha256) {
|
|
12517
|
+
hashes.push({
|
|
12518
|
+
alg: "SHA-256",
|
|
12519
|
+
content: podspec.source.http.sha256,
|
|
12520
|
+
});
|
|
12521
|
+
}
|
|
12522
|
+
if (hashes.length !== 0) {
|
|
12523
|
+
sourceDistro.hashes = hashes;
|
|
12524
|
+
}
|
|
12525
|
+
if (podspec.source.flatten) {
|
|
12526
|
+
comment.push(`Flatten: ${podspec.source.flatten}`);
|
|
12527
|
+
}
|
|
12528
|
+
if (podspec.source.type) {
|
|
12529
|
+
comment.push(`Type: ${podspec.source.type}`);
|
|
12530
|
+
}
|
|
12531
|
+
if (podspec.source.headers) {
|
|
12532
|
+
comment.push(`Headers: ${podspec.source.headers}`);
|
|
12533
|
+
}
|
|
12534
|
+
if (comment.length !== 0) {
|
|
12535
|
+
sourceDistro.comment = comment.join("\n");
|
|
12536
|
+
}
|
|
12537
|
+
externalRefs.push(sourceDistro);
|
|
12538
|
+
} else {
|
|
12539
|
+
let url;
|
|
12540
|
+
if (podspec.source.git) {
|
|
12541
|
+
url = podspec.source.git;
|
|
12542
|
+
comment.push("Type: git");
|
|
12543
|
+
if (podspec.source.branch) {
|
|
12544
|
+
comment.push(`Branch: ${podspec.source.branch}`);
|
|
12545
|
+
}
|
|
12546
|
+
if (podspec.source.commit) {
|
|
12547
|
+
comment.push(`Commit: ${podspec.source.commit}`);
|
|
12548
|
+
}
|
|
12549
|
+
if (podspec.source.tag) {
|
|
12550
|
+
comment.push(`Tag: ${podspec.source.tag}`);
|
|
12551
|
+
}
|
|
12552
|
+
if (podspec.source.submodules) {
|
|
12553
|
+
comment.push(`Submodules: ${podspec.source.submodules}`);
|
|
12554
|
+
}
|
|
12555
|
+
} else if (podspec.source.hg) {
|
|
12556
|
+
url = podspec.source.hg;
|
|
12557
|
+
comment.push("Type: hg");
|
|
12558
|
+
if (podspec.source.revision) {
|
|
12559
|
+
comment.push(`Revision: ${podspec.source.revision}`);
|
|
12560
|
+
}
|
|
12561
|
+
} else if (podspec.source.svn) {
|
|
12562
|
+
url = podspec.source.svn;
|
|
12563
|
+
comment.push("Type: svn");
|
|
12564
|
+
if (podspec.source.folder) {
|
|
12565
|
+
comment.push(`Folder: ${podspec.source.folder}`);
|
|
12566
|
+
}
|
|
12567
|
+
if (podspec.source.revision) {
|
|
12568
|
+
comment.push(`Revision: ${podspec.source.revision}`);
|
|
12569
|
+
}
|
|
12570
|
+
if (podspec.source.tag) {
|
|
12571
|
+
comment.push(`Tag: ${podspec.source.tag}`);
|
|
12572
|
+
}
|
|
12573
|
+
}
|
|
12574
|
+
if (url) {
|
|
12575
|
+
externalRefs.push({
|
|
12576
|
+
type: "vcs",
|
|
12577
|
+
url: url,
|
|
12578
|
+
comment: comment.join("\n"),
|
|
12579
|
+
});
|
|
12580
|
+
} else {
|
|
12581
|
+
console.warn(
|
|
12582
|
+
`${dependency.name} has property 'source' defined, but it does not contain a URL -- ignoring...`,
|
|
12583
|
+
);
|
|
12584
|
+
}
|
|
12585
|
+
}
|
|
12586
|
+
}
|
|
12587
|
+
if (externalRefs.length !== 0) {
|
|
12588
|
+
component.externalReferences = externalRefs;
|
|
12589
|
+
}
|
|
12590
|
+
}
|
|
12591
|
+
|
|
11898
12592
|
/**
|
|
11899
12593
|
* Method that handles object creation for gradle modules.
|
|
11900
12594
|
*
|
|
@@ -11904,8 +12598,11 @@ export function splitOutputByGradleProjects(rawOutput, relevantTasks) {
|
|
|
11904
12598
|
*/
|
|
11905
12599
|
export async function buildObjectForGradleModule(name, metadata) {
|
|
11906
12600
|
let component;
|
|
11907
|
-
if (
|
|
11908
|
-
|
|
12601
|
+
if (
|
|
12602
|
+
!["false", "0"].includes(process.env.GRADLE_RESOLVE_FROM_NODE) &&
|
|
12603
|
+
metadata.properties?.find(({ name }) => name === "projectDir")
|
|
12604
|
+
) {
|
|
12605
|
+
let tmpDir = metadata.properties?.find(
|
|
11909
12606
|
({ name }) => name === "projectDir",
|
|
11910
12607
|
).value;
|
|
11911
12608
|
if (tmpDir.indexOf("node_modules") !== -1) {
|
|
@@ -14183,6 +14880,9 @@ export function addEvidenceForDotnet(pkgList, slicesFile) {
|
|
|
14183
14880
|
}
|
|
14184
14881
|
const slicesData = JSON.parse(readFileSync(slicesFile, "utf-8"));
|
|
14185
14882
|
if (slicesData && Object.keys(slicesData)) {
|
|
14883
|
+
thoughtLog(
|
|
14884
|
+
"Let's thoroughly inspect the dependency slice to identify where and how the components are used.",
|
|
14885
|
+
);
|
|
14186
14886
|
if (slicesData.Dependencies) {
|
|
14187
14887
|
for (const adep of slicesData.Dependencies) {
|
|
14188
14888
|
// Case 1: Dependencies slice has the .dll file
|
|
@@ -14274,6 +14974,10 @@ export function addEvidenceForDotnet(pkgList, slicesFile) {
|
|
|
14274
14974
|
});
|
|
14275
14975
|
}
|
|
14276
14976
|
}
|
|
14977
|
+
} else if (slicesData?.Dependencies || slicesData?.MethodCalls) {
|
|
14978
|
+
thoughtLog(
|
|
14979
|
+
"I didn't find any occurrence evidence or detailed imported modules, even though there is good dependency slice data from dosai. This is surprising.",
|
|
14980
|
+
);
|
|
14277
14981
|
}
|
|
14278
14982
|
return pkgList;
|
|
14279
14983
|
}
|
|
@@ -14408,3 +15112,168 @@ export function recomputeScope(pkgList, dependencies) {
|
|
|
14408
15112
|
}
|
|
14409
15113
|
return pkgList;
|
|
14410
15114
|
}
|
|
15115
|
+
|
|
15116
|
+
/**
|
|
15117
|
+
* Function to parse a list of environment variables to identify the paths containing executable binaries
|
|
15118
|
+
*
|
|
15119
|
+
* @param envValues {Array[String]} Environment variables list
|
|
15120
|
+
* @returns {Array[String]} Binary Paths identified from the environment variables
|
|
15121
|
+
*/
|
|
15122
|
+
export function extractPathEnv(envValues) {
|
|
15123
|
+
if (!envValues) {
|
|
15124
|
+
return [];
|
|
15125
|
+
}
|
|
15126
|
+
let binPaths = new Set();
|
|
15127
|
+
const shellVariables = {};
|
|
15128
|
+
// Let's focus only on linux container images for now
|
|
15129
|
+
for (const env of envValues) {
|
|
15130
|
+
if (env.startsWith("PATH=")) {
|
|
15131
|
+
binPaths = new Set(env.replace("PATH=", "").split(":"));
|
|
15132
|
+
} else {
|
|
15133
|
+
const tmpA = env.split("=");
|
|
15134
|
+
if (tmpA.length === 2) {
|
|
15135
|
+
shellVariables[`$${tmpA[0]}`] = tmpA[1];
|
|
15136
|
+
shellVariables[`\${${tmpA[0]}}`] = tmpA[1];
|
|
15137
|
+
}
|
|
15138
|
+
}
|
|
15139
|
+
}
|
|
15140
|
+
binPaths = Array.from(binPaths);
|
|
15141
|
+
const expandedBinPaths = [];
|
|
15142
|
+
for (let apath of binPaths) {
|
|
15143
|
+
// Filter empty paths
|
|
15144
|
+
if (!apath.length) {
|
|
15145
|
+
continue;
|
|
15146
|
+
}
|
|
15147
|
+
if (apath.includes("$")) {
|
|
15148
|
+
for (const k of Object.keys(shellVariables)) {
|
|
15149
|
+
apath = apath.replace(k, shellVariables[k]);
|
|
15150
|
+
}
|
|
15151
|
+
}
|
|
15152
|
+
// We're here, but not all paths got substituted
|
|
15153
|
+
// Let's ignore them for now instead of risking substitution based on host values.
|
|
15154
|
+
// Eg: ${GITHUB_TOKEN} could get expanded with the values from the host
|
|
15155
|
+
if (apath.length && !apath.includes("$")) {
|
|
15156
|
+
expandedBinPaths.push(apath);
|
|
15157
|
+
}
|
|
15158
|
+
}
|
|
15159
|
+
return expandedBinPaths;
|
|
15160
|
+
}
|
|
15161
|
+
|
|
15162
|
+
/**
|
|
15163
|
+
* Collect all executable files from the given list of binary paths
|
|
15164
|
+
*
|
|
15165
|
+
* @param basePath Base directory
|
|
15166
|
+
* @param binPaths {Array[String]} Paths containing potential binaries
|
|
15167
|
+
* @return {Array[String]} List of executables
|
|
15168
|
+
*/
|
|
15169
|
+
export function collectExecutables(basePath, binPaths) {
|
|
15170
|
+
if (!binPaths) {
|
|
15171
|
+
return [];
|
|
15172
|
+
}
|
|
15173
|
+
let executables = [];
|
|
15174
|
+
const ignoreList = [
|
|
15175
|
+
"**/*.{h,c,cpp,hpp,man,txt,md,htm,html,jar,ear,war,zip,tar,egg,keepme,gitignore,json,js,py,pyc}",
|
|
15176
|
+
"[",
|
|
15177
|
+
];
|
|
15178
|
+
for (const apath of binPaths) {
|
|
15179
|
+
try {
|
|
15180
|
+
const files = globSync(`**${apath}/*`, {
|
|
15181
|
+
cwd: basePath,
|
|
15182
|
+
absolute: false,
|
|
15183
|
+
nocase: true,
|
|
15184
|
+
nodir: true,
|
|
15185
|
+
dot: true,
|
|
15186
|
+
follow: true,
|
|
15187
|
+
ignore: ignoreList,
|
|
15188
|
+
});
|
|
15189
|
+
executables = executables.concat(files);
|
|
15190
|
+
} catch (err) {
|
|
15191
|
+
// ignore
|
|
15192
|
+
}
|
|
15193
|
+
}
|
|
15194
|
+
return Array.from(new Set(executables)).sort();
|
|
15195
|
+
}
|
|
15196
|
+
|
|
15197
|
+
/**
|
|
15198
|
+
* Collect all shared library files from the given list of paths
|
|
15199
|
+
*
|
|
15200
|
+
* @param basePath Base directory
|
|
15201
|
+
* @param libPaths {Array[String]} Paths containing potential libraries
|
|
15202
|
+
* @param ldConf {String} Config file used by ldconfig to locate additional paths
|
|
15203
|
+
* @param ldConfDirPattern {String} Config directory that can contain more .conf files for ldconfig
|
|
15204
|
+
*
|
|
15205
|
+
* @return {Array[String]} List of executables
|
|
15206
|
+
*/
|
|
15207
|
+
export function collectSharedLibs(
|
|
15208
|
+
basePath,
|
|
15209
|
+
libPaths,
|
|
15210
|
+
ldConf,
|
|
15211
|
+
ldConfDirPattern,
|
|
15212
|
+
) {
|
|
15213
|
+
if (!libPaths) {
|
|
15214
|
+
return [];
|
|
15215
|
+
}
|
|
15216
|
+
let sharedLibs = [];
|
|
15217
|
+
const ignoreList = [
|
|
15218
|
+
"**/*.{h,c,cpp,hpp,man,txt,md,htm,html,jar,ear,war,zip,tar,egg,keepme,gitignore,json,js,py,pyc}",
|
|
15219
|
+
];
|
|
15220
|
+
const allLdConfDirs = ldConfDirPattern ? [ldConfDirPattern] : [];
|
|
15221
|
+
collectAllLdConfs(basePath, ldConf, allLdConfDirs, libPaths);
|
|
15222
|
+
if (allLdConfDirs.length) {
|
|
15223
|
+
for (const aldconfPattern of allLdConfDirs) {
|
|
15224
|
+
const confFiles = globSync(aldconfPattern, {
|
|
15225
|
+
cwd: basePath,
|
|
15226
|
+
absolute: false,
|
|
15227
|
+
nocase: true,
|
|
15228
|
+
nodir: true,
|
|
15229
|
+
dot: true,
|
|
15230
|
+
follow: false,
|
|
15231
|
+
});
|
|
15232
|
+
for (const moreConf of confFiles) {
|
|
15233
|
+
collectAllLdConfs(basePath, moreConf, allLdConfDirs, libPaths);
|
|
15234
|
+
}
|
|
15235
|
+
}
|
|
15236
|
+
}
|
|
15237
|
+
for (const apath of Array.from(new Set(libPaths))) {
|
|
15238
|
+
try {
|
|
15239
|
+
const files = globSync(`**${apath}/*.{so,so.*,a,lib,dll}`, {
|
|
15240
|
+
cwd: basePath,
|
|
15241
|
+
absolute: false,
|
|
15242
|
+
nocase: true,
|
|
15243
|
+
nodir: true,
|
|
15244
|
+
dot: true,
|
|
15245
|
+
follow: true,
|
|
15246
|
+
ignore: ignoreList,
|
|
15247
|
+
});
|
|
15248
|
+
sharedLibs = sharedLibs.concat(files);
|
|
15249
|
+
} catch (err) {
|
|
15250
|
+
// ignore
|
|
15251
|
+
}
|
|
15252
|
+
}
|
|
15253
|
+
return Array.from(new Set(sharedLibs)).sort();
|
|
15254
|
+
}
|
|
15255
|
+
|
|
15256
|
+
function collectAllLdConfs(basePath, ldConf, allLdConfDirs, libPaths) {
|
|
15257
|
+
if (ldConf && existsSync(join(basePath, ldConf))) {
|
|
15258
|
+
const ldConfData = readFileSync(join(basePath, ldConf), "utf-8");
|
|
15259
|
+
for (let line of ldConfData.split("\n")) {
|
|
15260
|
+
line = line.replace("\r", "").trim();
|
|
15261
|
+
if (!line.length || line.startsWith("#")) {
|
|
15262
|
+
continue;
|
|
15263
|
+
}
|
|
15264
|
+
if (line.startsWith("include ")) {
|
|
15265
|
+
let apattern = line.replace("include ", "");
|
|
15266
|
+
if (!apattern.includes("*")) {
|
|
15267
|
+
apattern = `${apattern}/*.conf`;
|
|
15268
|
+
}
|
|
15269
|
+
if (!allLdConfDirs.includes(apattern)) {
|
|
15270
|
+
allLdConfDirs.push(apattern);
|
|
15271
|
+
}
|
|
15272
|
+
} else if (line.startsWith("/")) {
|
|
15273
|
+
if (!libPaths.includes(line)) {
|
|
15274
|
+
libPaths.push(line);
|
|
15275
|
+
}
|
|
15276
|
+
}
|
|
15277
|
+
}
|
|
15278
|
+
}
|
|
15279
|
+
}
|