@cyclonedx/cdxgen 11.2.1 → 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 +291 -70
- package/lib/helpers/utils.js +193 -9
- 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 +21 -7
- package/lib/stages/pregen/pregen.js +1 -1
- package/package.json +6 -6
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/helpers/utils.d.ts +26 -0
- 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
|
@@ -614,12 +614,21 @@ export function isPackageManagerAllowed(name, conflictingManagers, options) {
|
|
|
614
614
|
// HTTP cache
|
|
615
615
|
const gotHttpCache = new Map();
|
|
616
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
|
+
|
|
617
626
|
// Custom user-agent for cdxgen
|
|
618
627
|
export const cdxgenAgent = got.extend({
|
|
619
628
|
headers: {
|
|
620
629
|
"user-agent": `@CycloneDX/cdxgen ${_version}`,
|
|
621
630
|
},
|
|
622
|
-
cache
|
|
631
|
+
cache,
|
|
623
632
|
retry: {
|
|
624
633
|
limit: 0,
|
|
625
634
|
},
|
|
@@ -4189,7 +4198,10 @@ export async function getMvnMetadata(
|
|
|
4189
4198
|
}
|
|
4190
4199
|
const group = p.group || "";
|
|
4191
4200
|
// If the package already has key metadata skip querying maven
|
|
4192
|
-
if (
|
|
4201
|
+
if (
|
|
4202
|
+
!p.version ||
|
|
4203
|
+
(group && p.name && p.version && !shouldFetchLicense() && !force)
|
|
4204
|
+
) {
|
|
4193
4205
|
cdepList.push(p);
|
|
4194
4206
|
continue;
|
|
4195
4207
|
}
|
|
@@ -6969,8 +6981,11 @@ export async function parseGemspecData(gemspecData, gemspecFile) {
|
|
|
6969
6981
|
if (["name", "version"].includes(aprop)) {
|
|
6970
6982
|
value = value.replace(/["']/g, "");
|
|
6971
6983
|
}
|
|
6972
|
-
|
|
6973
|
-
|
|
6984
|
+
// Do not set name=name or version=version
|
|
6985
|
+
if (value !== aprop) {
|
|
6986
|
+
pkg[aprop] = value;
|
|
6987
|
+
break;
|
|
6988
|
+
}
|
|
6974
6989
|
}
|
|
6975
6990
|
}
|
|
6976
6991
|
// Handle common problems
|
|
@@ -7635,11 +7650,11 @@ export async function parseCargoTomlData(
|
|
|
7635
7650
|
pkg.evidence = {
|
|
7636
7651
|
identity: {
|
|
7637
7652
|
field: "purl",
|
|
7638
|
-
confidence: 0.5,
|
|
7653
|
+
confidence: pkg.version ? 0.5 : 0,
|
|
7639
7654
|
methods: [
|
|
7640
7655
|
{
|
|
7641
7656
|
technique: "manifest-analysis",
|
|
7642
|
-
confidence: 0.5,
|
|
7657
|
+
confidence: pkg.version ? 0.5 : 0,
|
|
7643
7658
|
value: cargoTomlFile,
|
|
7644
7659
|
},
|
|
7645
7660
|
],
|
|
@@ -11505,9 +11520,10 @@ export async function extractJarArchive(jarFile, tempDir, jarNSMapping = {}) {
|
|
|
11505
11520
|
}
|
|
11506
11521
|
}
|
|
11507
11522
|
}
|
|
11523
|
+
let jarMetadata;
|
|
11508
11524
|
if ((!group || !name || !version) && safeExistsSync(manifestFile)) {
|
|
11509
11525
|
confidence = 0.8;
|
|
11510
|
-
|
|
11526
|
+
jarMetadata = parseJarManifest(
|
|
11511
11527
|
readFileSync(manifestFile, {
|
|
11512
11528
|
encoding: "utf-8",
|
|
11513
11529
|
}),
|
|
@@ -11580,7 +11596,10 @@ export async function extractJarArchive(jarFile, tempDir, jarNSMapping = {}) {
|
|
|
11580
11596
|
// if group is empty use name as group
|
|
11581
11597
|
group = group === "." ? name : group || name;
|
|
11582
11598
|
}
|
|
11583
|
-
if (name
|
|
11599
|
+
if (name) {
|
|
11600
|
+
if (!version) {
|
|
11601
|
+
confidence = 0;
|
|
11602
|
+
}
|
|
11584
11603
|
const apkg = {
|
|
11585
11604
|
group: group ? encodeForPurl(group) : "",
|
|
11586
11605
|
name: name ? encodeForPurl(name) : "",
|
|
@@ -11609,7 +11628,7 @@ export async function extractJarArchive(jarFile, tempDir, jarNSMapping = {}) {
|
|
|
11609
11628
|
properties: [
|
|
11610
11629
|
{
|
|
11611
11630
|
name: "SrcFile",
|
|
11612
|
-
value:
|
|
11631
|
+
value: jf,
|
|
11613
11632
|
},
|
|
11614
11633
|
],
|
|
11615
11634
|
};
|
|
@@ -15093,3 +15112,168 @@ export function recomputeScope(pkgList, dependencies) {
|
|
|
15093
15112
|
}
|
|
15094
15113
|
return pkgList;
|
|
15095
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
|
+
}
|
package/lib/helpers/validator.js
CHANGED
|
@@ -273,7 +273,9 @@ export const validateRefs = (bomJson) => {
|
|
|
273
273
|
if (dep.dependsOn) {
|
|
274
274
|
for (const don of dep.dependsOn) {
|
|
275
275
|
if (!refMap[don]) {
|
|
276
|
-
warningsList.push(
|
|
276
|
+
warningsList.push(
|
|
277
|
+
`Invalid ref in dependencies.dependsOn ${don}. Parent: ${dep.ref}`,
|
|
278
|
+
);
|
|
277
279
|
}
|
|
278
280
|
let childPurlType;
|
|
279
281
|
try {
|
|
@@ -329,6 +331,13 @@ export function validateProps(bomJson) {
|
|
|
329
331
|
let lacksProperties = false;
|
|
330
332
|
let lacksEvidence = false;
|
|
331
333
|
let lacksRelativePath = false;
|
|
334
|
+
if (
|
|
335
|
+
!["application", "framework", "library"].includes(
|
|
336
|
+
bomJson?.metadata?.component?.type,
|
|
337
|
+
)
|
|
338
|
+
) {
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
332
341
|
if (bomJson?.components) {
|
|
333
342
|
for (const comp of bomJson.components) {
|
|
334
343
|
if (!["library", "framework"].includes(comp.type)) {
|
package/lib/managers/binary.js
CHANGED
|
@@ -16,7 +16,10 @@ import {
|
|
|
16
16
|
MAX_BUFFER,
|
|
17
17
|
TIMEOUT_MS,
|
|
18
18
|
adjustLicenseInformation,
|
|
19
|
+
collectExecutables,
|
|
20
|
+
collectSharedLibs,
|
|
19
21
|
dirNameStr,
|
|
22
|
+
extractPathEnv,
|
|
20
23
|
findLicenseId,
|
|
21
24
|
getTmpDir,
|
|
22
25
|
isSpdxLicenseExpression,
|
|
@@ -278,6 +281,36 @@ const OS_DISTRO_ALIAS = {
|
|
|
278
281
|
"red hat enterprise linux 9": "rhel-9",
|
|
279
282
|
};
|
|
280
283
|
|
|
284
|
+
// TODO: Move the lists to a config file
|
|
285
|
+
const COMMON_RUNTIMES = [
|
|
286
|
+
"java",
|
|
287
|
+
"node",
|
|
288
|
+
"nodejs",
|
|
289
|
+
"nodejs-current",
|
|
290
|
+
"deno",
|
|
291
|
+
"bun",
|
|
292
|
+
"python",
|
|
293
|
+
"python3",
|
|
294
|
+
"ruby",
|
|
295
|
+
"php",
|
|
296
|
+
"php7",
|
|
297
|
+
"php8",
|
|
298
|
+
"perl",
|
|
299
|
+
"openjdk",
|
|
300
|
+
"openjdk8",
|
|
301
|
+
"openjdk11",
|
|
302
|
+
"openjdk17",
|
|
303
|
+
"openjdk21",
|
|
304
|
+
"openjdk8-jdk",
|
|
305
|
+
"openjdk11-jdk",
|
|
306
|
+
"openjdk17-jdk",
|
|
307
|
+
"openjdk21-jdk",
|
|
308
|
+
"openjdk8-jre",
|
|
309
|
+
"openjdk11-jre",
|
|
310
|
+
"openjdk17-jre",
|
|
311
|
+
"openjdk21-jre",
|
|
312
|
+
];
|
|
313
|
+
|
|
281
314
|
export function getGoBuildInfo(src) {
|
|
282
315
|
if (GOVERSION_BIN) {
|
|
283
316
|
let result = spawnSync(GOVERSION_BIN, [src], {
|
|
@@ -356,10 +389,21 @@ export function executeSourcekitten(args) {
|
|
|
356
389
|
return undefined;
|
|
357
390
|
}
|
|
358
391
|
|
|
359
|
-
|
|
392
|
+
/**
|
|
393
|
+
* Get the packages installed in the container image filesystem.
|
|
394
|
+
*
|
|
395
|
+
* @param src {String} Source directory containing the extracted filesystem.
|
|
396
|
+
* @param imageConfig {Object} Image configuration containing environment variables, command, entrypoints etc
|
|
397
|
+
*
|
|
398
|
+
* @returns {Object} Metadata containing packages, dependencies, etc
|
|
399
|
+
*/
|
|
400
|
+
export function getOSPackages(src, imageConfig) {
|
|
360
401
|
const pkgList = [];
|
|
361
402
|
const dependenciesList = [];
|
|
362
403
|
const allTypes = new Set();
|
|
404
|
+
const bundledSdks = new Set();
|
|
405
|
+
const bundledRuntimes = new Set();
|
|
406
|
+
const binPaths = extractPathEnv(imageConfig?.Env);
|
|
363
407
|
if (TRIVY_BIN) {
|
|
364
408
|
let imageType = "image";
|
|
365
409
|
const trivyCacheDir = join(homedir(), ".cache", "trivy");
|
|
@@ -467,6 +511,11 @@ export function getOSPackages(src) {
|
|
|
467
511
|
case "pop":
|
|
468
512
|
purl_type = "deb";
|
|
469
513
|
break;
|
|
514
|
+
case "sles":
|
|
515
|
+
case "suse":
|
|
516
|
+
case "opensuse":
|
|
517
|
+
purl_type = "rpm";
|
|
518
|
+
break;
|
|
470
519
|
case "alpine":
|
|
471
520
|
purl_type = "apk";
|
|
472
521
|
if (osReleaseData.VERSION_ID) {
|
|
@@ -503,19 +552,9 @@ export function getOSPackages(src) {
|
|
|
503
552
|
if (comp.purl) {
|
|
504
553
|
// Retain go components alone from trivy
|
|
505
554
|
if (
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
comp.purl.startsWith("pkg:cargo") ||
|
|
510
|
-
comp.purl.startsWith("pkg:composer") ||
|
|
511
|
-
comp.purl.startsWith("pkg:gem") ||
|
|
512
|
-
comp.purl.startsWith("pkg:nuget") ||
|
|
513
|
-
comp.purl.startsWith("pkg:pub") ||
|
|
514
|
-
comp.purl.startsWith("pkg:hackage") ||
|
|
515
|
-
comp.purl.startsWith("pkg:hex") ||
|
|
516
|
-
comp.purl.startsWith("pkg:conan") ||
|
|
517
|
-
comp.purl.startsWith("pkg:clojars") ||
|
|
518
|
-
comp.purl.startsWith("pkg:github")
|
|
555
|
+
/^pkg:(npm|maven|pypi|cargo|composer|gem|nuget|pub|hackage|hex|conan|clojars|github)/.test(
|
|
556
|
+
comp.purl,
|
|
557
|
+
)
|
|
519
558
|
) {
|
|
520
559
|
continue;
|
|
521
560
|
}
|
|
@@ -544,8 +583,6 @@ export function getOSPackages(src) {
|
|
|
544
583
|
if (distro_codename?.length) {
|
|
545
584
|
purlObj.qualifiers["distro_name"] = distro_codename;
|
|
546
585
|
}
|
|
547
|
-
// Remove any epoch values
|
|
548
|
-
delete purlObj.qualifiers.epoch;
|
|
549
586
|
// Bug fix for mageia and oracle linux
|
|
550
587
|
// Type is being returned as none for ubuntu as well!
|
|
551
588
|
if (purlObj.type === "none") {
|
|
@@ -624,7 +661,6 @@ export function getOSPackages(src) {
|
|
|
624
661
|
if (distro_codename?.length) {
|
|
625
662
|
purlObj.qualifiers["distro_name"] = distro_codename;
|
|
626
663
|
}
|
|
627
|
-
delete purlObj.qualifiers.epoch;
|
|
628
664
|
allTypes.add(purlObj.namespace);
|
|
629
665
|
comp.purl = new PackageURL(
|
|
630
666
|
purlObj.type,
|
|
@@ -695,7 +731,16 @@ export function getOSPackages(src) {
|
|
|
695
731
|
}
|
|
696
732
|
}
|
|
697
733
|
delete comp.properties;
|
|
734
|
+
// Bug fix: We can get bom-ref like this: pkg:rpm/sles/libstdc%2B%2B6@14.2.0+git10526-150000.1.6.1?arch=x86_64&distro=sles-15.5
|
|
735
|
+
if (
|
|
736
|
+
comp["bom-ref"] &&
|
|
737
|
+
comp.purl &&
|
|
738
|
+
comp["bom-ref"] !== decodeURIComponent(comp.purl)
|
|
739
|
+
) {
|
|
740
|
+
comp["bom-ref"] = decodeURIComponent(comp.purl);
|
|
741
|
+
}
|
|
698
742
|
pkgList.push(comp);
|
|
743
|
+
detectSdksRuntimes(comp, bundledSdks, bundledRuntimes);
|
|
699
744
|
const compDeps = retrieveDependencies(
|
|
700
745
|
tmpDependencies,
|
|
701
746
|
origBomRef,
|
|
@@ -721,19 +766,79 @@ export function getOSPackages(src) {
|
|
|
721
766
|
}
|
|
722
767
|
newComp["bom-ref"] = decodeURIComponent(newComp.purl);
|
|
723
768
|
pkgList.push(newComp);
|
|
769
|
+
detectSdksRuntimes(newComp, bundledSdks, bundledRuntimes);
|
|
724
770
|
}
|
|
725
771
|
}
|
|
726
772
|
}
|
|
727
773
|
}
|
|
728
774
|
}
|
|
729
775
|
}
|
|
776
|
+
let executables = [];
|
|
777
|
+
if (binPaths?.length) {
|
|
778
|
+
executables = fileComponents(
|
|
779
|
+
collectExecutables(src, binPaths),
|
|
780
|
+
"executable",
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
// Directories containing shared libraries
|
|
784
|
+
const defaultLibPaths = [
|
|
785
|
+
"/lib",
|
|
786
|
+
"/lib64",
|
|
787
|
+
"/usr/lib",
|
|
788
|
+
"/usr/lib64",
|
|
789
|
+
"/usr/local/lib64",
|
|
790
|
+
"/usr/local/lib",
|
|
791
|
+
"/lib/x86_64-linux-gnu",
|
|
792
|
+
"/usr/lib/x86_64-linux-gnu",
|
|
793
|
+
"/lib/i386-linux-gnu",
|
|
794
|
+
"/usr/lib/i386-linux-gnu",
|
|
795
|
+
"/lib/arm-linux-gnueabihf",
|
|
796
|
+
"/usr/lib/arm-linux-gnueabihf",
|
|
797
|
+
"/opt/**/lib",
|
|
798
|
+
"/root/**/lib",
|
|
799
|
+
];
|
|
800
|
+
const sharedLibs = fileComponents(
|
|
801
|
+
collectSharedLibs(
|
|
802
|
+
src,
|
|
803
|
+
defaultLibPaths,
|
|
804
|
+
"/etc/ld.so.conf",
|
|
805
|
+
"/etc/ld.so.conf.d/*.conf",
|
|
806
|
+
),
|
|
807
|
+
"shared_library",
|
|
808
|
+
);
|
|
730
809
|
return {
|
|
731
810
|
osPackages: pkgList,
|
|
732
811
|
dependenciesList,
|
|
733
|
-
allTypes: Array.from(allTypes),
|
|
812
|
+
allTypes: Array.from(allTypes).sort(),
|
|
813
|
+
bundledSdks: Array.from(bundledSdks).sort(),
|
|
814
|
+
bundledRuntimes: Array.from(bundledRuntimes).sort(),
|
|
815
|
+
binPaths,
|
|
816
|
+
executables,
|
|
817
|
+
sharedLibs,
|
|
734
818
|
};
|
|
735
819
|
}
|
|
736
820
|
|
|
821
|
+
// Detect common sdks and runtimes from the name
|
|
822
|
+
function detectSdksRuntimes(comp, bundledSdks, bundledRuntimes) {
|
|
823
|
+
if (!comp?.name) {
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
if (/dotnet[6-9]?-sdk/.test(comp.name)) {
|
|
827
|
+
bundledSdks.add(comp.purl);
|
|
828
|
+
}
|
|
829
|
+
if (
|
|
830
|
+
/dotnet[6-9]?-runtime/.test(comp.name) ||
|
|
831
|
+
comp.name.includes("aspnet-runtime") ||
|
|
832
|
+
/aspnetcore[6-9]?-runtime/.test(comp.name)
|
|
833
|
+
) {
|
|
834
|
+
bundledRuntimes.add(comp.purl);
|
|
835
|
+
}
|
|
836
|
+
// TODO: Need to test this for a range of base images
|
|
837
|
+
if (COMMON_RUNTIMES.includes(comp.name)) {
|
|
838
|
+
bundledRuntimes.add(comp.name);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
737
842
|
const retrieveDependencies = (tmpDependencies, origBomRef, comp) => {
|
|
738
843
|
try {
|
|
739
844
|
const tmpDependsOn = tmpDependencies[origBomRef] || [];
|
|
@@ -752,7 +857,6 @@ const retrieveDependencies = (tmpDependencies, origBomRef, comp) => {
|
|
|
752
857
|
if (compPurl.qualifiers.distro) {
|
|
753
858
|
tmpPurl.qualifiers.distro = compPurl.qualifiers.distro;
|
|
754
859
|
}
|
|
755
|
-
delete tmpPurl.qualifiers.epoch;
|
|
756
860
|
}
|
|
757
861
|
dependsOn.add(decodeURIComponent(tmpPurl.toString()));
|
|
758
862
|
} catch (e) {
|
|
@@ -906,3 +1010,40 @@ export function getBinaryBom(src, binaryBomFile, deepMode) {
|
|
|
906
1010
|
}
|
|
907
1011
|
return true;
|
|
908
1012
|
}
|
|
1013
|
+
|
|
1014
|
+
function fileComponents(fileList, fileType) {
|
|
1015
|
+
const components = [];
|
|
1016
|
+
for (let f of fileList) {
|
|
1017
|
+
// Collect methods returns relative paths from the extracted directory.
|
|
1018
|
+
// We make them absolute by prefixing / here
|
|
1019
|
+
if (!f.startsWith("/")) {
|
|
1020
|
+
f = `/${f}`;
|
|
1021
|
+
}
|
|
1022
|
+
const name = basename(f);
|
|
1023
|
+
const purl = `pkg:generic/${name}`;
|
|
1024
|
+
components.push({
|
|
1025
|
+
name,
|
|
1026
|
+
type: "file",
|
|
1027
|
+
purl,
|
|
1028
|
+
"bom-ref": purl,
|
|
1029
|
+
properties: [
|
|
1030
|
+
{ name: "SrcFile", value: f },
|
|
1031
|
+
{ name: `internal:is_${fileType}`, value: "true" },
|
|
1032
|
+
],
|
|
1033
|
+
evidence: {
|
|
1034
|
+
identity: {
|
|
1035
|
+
field: "purl",
|
|
1036
|
+
confidence: 0,
|
|
1037
|
+
methods: [
|
|
1038
|
+
{
|
|
1039
|
+
technique: "filename",
|
|
1040
|
+
confidence: 0,
|
|
1041
|
+
value: f,
|
|
1042
|
+
},
|
|
1043
|
+
],
|
|
1044
|
+
},
|
|
1045
|
+
},
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
return components;
|
|
1049
|
+
}
|
package/lib/managers/docker.js
CHANGED
|
@@ -19,6 +19,7 @@ import got from "got";
|
|
|
19
19
|
import { x } from "tar";
|
|
20
20
|
import {
|
|
21
21
|
DEBUG_MODE,
|
|
22
|
+
extractPathEnv,
|
|
22
23
|
getAllFiles,
|
|
23
24
|
getTmpDir,
|
|
24
25
|
safeExistsSync,
|
|
@@ -556,6 +557,7 @@ export const parseImageName = (fullImageName) => {
|
|
|
556
557
|
fullImageName.includes("/") &&
|
|
557
558
|
(fullImageName.includes(".") || fullImageName.includes(":"))
|
|
558
559
|
) {
|
|
560
|
+
// TODO: Change to URL
|
|
559
561
|
const urlObj = parse(fullImageName);
|
|
560
562
|
const tmpA = fullImageName.split("/");
|
|
561
563
|
if (
|
|
@@ -742,7 +744,7 @@ export const getImage = async (fullImageName) => {
|
|
|
742
744
|
if (DEBUG_MODE) {
|
|
743
745
|
console.log(`Re-trying the pull with the name ${repoWithTag}.`);
|
|
744
746
|
}
|
|
745
|
-
|
|
747
|
+
await makeRequest(
|
|
746
748
|
`images/create?fromImage=${repoWithTag}`,
|
|
747
749
|
"POST",
|
|
748
750
|
registry,
|
|
@@ -1078,6 +1080,7 @@ export const extractFromManifest = async (
|
|
|
1078
1080
|
}
|
|
1079
1081
|
}
|
|
1080
1082
|
}
|
|
1083
|
+
const binPaths = extractPathEnv(localData?.Config?.Env);
|
|
1081
1084
|
const exportData = {
|
|
1082
1085
|
inspectData: localData,
|
|
1083
1086
|
manifest,
|
|
@@ -1085,6 +1088,7 @@ export const extractFromManifest = async (
|
|
|
1085
1088
|
allLayersExplodedDir,
|
|
1086
1089
|
lastLayerConfig,
|
|
1087
1090
|
lastWorkingDir,
|
|
1091
|
+
binPaths,
|
|
1088
1092
|
};
|
|
1089
1093
|
exportData.pkgPathList = getPkgPathList(exportData, lastWorkingDir);
|
|
1090
1094
|
return exportData;
|
|
@@ -1253,6 +1257,7 @@ export const getPkgPathList = (exportData, lastWorkingDir) => {
|
|
|
1253
1257
|
join(allLayersExplodedDir, "/usr/local/lib"),
|
|
1254
1258
|
join(allLayersExplodedDir, "/usr/local/lib64"),
|
|
1255
1259
|
join(allLayersExplodedDir, "/opt"),
|
|
1260
|
+
join(allLayersExplodedDir, "/root"),
|
|
1256
1261
|
join(allLayersExplodedDir, "/home"),
|
|
1257
1262
|
join(allLayersExplodedDir, "/usr/share"),
|
|
1258
1263
|
join(allLayersExplodedDir, "/usr/src"),
|
|
@@ -1266,6 +1271,7 @@ export const getPkgPathList = (exportData, lastWorkingDir) => {
|
|
|
1266
1271
|
join(allLayersExplodedDir, "/usr/local/lib"),
|
|
1267
1272
|
join(allLayersExplodedDir, "/usr/local/lib64"),
|
|
1268
1273
|
join(allLayersExplodedDir, "/opt"),
|
|
1274
|
+
join(allLayersExplodedDir, "/root"),
|
|
1269
1275
|
join(allLayersExplodedDir, "/usr/share"),
|
|
1270
1276
|
join(allLayersExplodedDir, "/usr/src"),
|
|
1271
1277
|
join(allLayersExplodedDir, "/var/www/html"),
|
|
@@ -1296,7 +1302,8 @@ export const getPkgPathList = (exportData, lastWorkingDir) => {
|
|
|
1296
1302
|
if (lastWorkingDir && lastWorkingDir !== "") {
|
|
1297
1303
|
if (
|
|
1298
1304
|
!lastWorkingDir.includes("/opt/") &&
|
|
1299
|
-
!lastWorkingDir.includes("/home/")
|
|
1305
|
+
!lastWorkingDir.includes("/home/") &&
|
|
1306
|
+
!lastWorkingDir.includes("/root/")
|
|
1300
1307
|
) {
|
|
1301
1308
|
knownSysPaths.push(lastWorkingDir);
|
|
1302
1309
|
}
|
|
@@ -1320,13 +1327,17 @@ export const getPkgPathList = (exportData, lastWorkingDir) => {
|
|
|
1320
1327
|
// Build path list
|
|
1321
1328
|
for (const wpath of knownSysPaths) {
|
|
1322
1329
|
pathList = pathList.concat(wpath);
|
|
1330
|
+
const nodeModuleDirs = getOnlyDirs(wpath, "node_modules");
|
|
1331
|
+
if (nodeModuleDirs?.length) {
|
|
1332
|
+
pathList.push(nodeModuleDirs[0]);
|
|
1333
|
+
}
|
|
1323
1334
|
const pyDirs = getOnlyDirs(wpath, "site-packages");
|
|
1324
1335
|
if (pyDirs?.length) {
|
|
1325
1336
|
pathList = pathList.concat(pyDirs);
|
|
1326
1337
|
}
|
|
1327
1338
|
const gemsDirs = getOnlyDirs(wpath, "gems");
|
|
1328
1339
|
if (gemsDirs?.length) {
|
|
1329
|
-
pathList = pathList.concat(gemsDirs);
|
|
1340
|
+
pathList = pathList.concat(gemsDirs[0]);
|
|
1330
1341
|
}
|
|
1331
1342
|
const cargoDirs = getOnlyDirs(wpath, ".cargo");
|
|
1332
1343
|
if (cargoDirs?.length) {
|
|
@@ -1337,6 +1348,7 @@ export const getPkgPathList = (exportData, lastWorkingDir) => {
|
|
|
1337
1348
|
pathList = pathList.concat(composerDirs);
|
|
1338
1349
|
}
|
|
1339
1350
|
}
|
|
1351
|
+
pathList = Array.from(new Set(pathList)).sort();
|
|
1340
1352
|
if (DEBUG_MODE) {
|
|
1341
1353
|
console.log("pathList", pathList);
|
|
1342
1354
|
}
|
|
@@ -1344,11 +1356,7 @@ export const getPkgPathList = (exportData, lastWorkingDir) => {
|
|
|
1344
1356
|
};
|
|
1345
1357
|
|
|
1346
1358
|
export const removeImage = async (fullImageName, force = false) => {
|
|
1347
|
-
|
|
1348
|
-
`images/${fullImageName}?force=${force}`,
|
|
1349
|
-
"DELETE",
|
|
1350
|
-
);
|
|
1351
|
-
return removeData;
|
|
1359
|
+
return await makeRequest(`images/${fullImageName}?force=${force}`, "DELETE");
|
|
1352
1360
|
};
|
|
1353
1361
|
|
|
1354
1362
|
export const getCredsFromHelper = (exeSuffix, serverAddress) => {
|