@cyclonedx/cdxgen 12.1.1 → 12.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -1
- package/bin/dependencies.js +36 -36
- package/bin/licenses.js +78 -0
- package/data/spdx.schema.json +35 -2
- package/data/templates/asvs-5.0.cdx.json +1727 -3471
- package/lib/cli/index.js +30 -2
- package/lib/helpers/licenses.poku.js +11 -0
- package/lib/helpers/utils.js +66 -29
- package/lib/helpers/utils.poku.js +21 -1
- package/lib/helpers/validator.js +31 -0
- package/lib/managers/binary.js +34 -12
- package/lib/managers/containerutils.js +68 -0
- package/lib/managers/docker.js +36 -96
- package/package.json +37 -35
- package/types/bin/dependencies.d.ts.map +1 -1
- package/types/bin/licenses.d.ts +3 -0
- package/types/bin/licenses.d.ts.map +1 -0
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/helpers/utils.d.ts +1 -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.map +1 -1
- package/types/lib/managers/containerutils.d.ts +3 -0
- package/types/lib/managers/containerutils.d.ts.map +1 -0
- package/types/lib/managers/docker.d.ts +0 -2
- package/types/lib/managers/docker.d.ts.map +1 -1
package/lib/cli/index.js
CHANGED
|
@@ -4379,7 +4379,7 @@ export async function createGoBom(path, options) {
|
|
|
4379
4379
|
) {
|
|
4380
4380
|
for (const f of sortedGomodFiles) {
|
|
4381
4381
|
const basePath = dirname(f);
|
|
4382
|
-
// Ignore vendor packages
|
|
4382
|
+
// Ignore vendor packages and test fixtures
|
|
4383
4383
|
if (
|
|
4384
4384
|
basePath.includes("/vendor/") ||
|
|
4385
4385
|
basePath.includes("/build/") ||
|
|
@@ -4391,13 +4391,14 @@ export async function createGoBom(path, options) {
|
|
|
4391
4391
|
if (DEBUG_MODE) {
|
|
4392
4392
|
console.log("Executing go list -deps in", basePath);
|
|
4393
4393
|
}
|
|
4394
|
+
// TODO: Replacing this with -json gives us more interesting data points such as GoFiles, Imports, and Deps
|
|
4394
4395
|
let result = safeSpawnSync(
|
|
4395
4396
|
"go",
|
|
4396
4397
|
[
|
|
4397
4398
|
"list",
|
|
4398
4399
|
"-deps",
|
|
4399
4400
|
"-f",
|
|
4400
|
-
"'{{with .Module}}{{.Path}}
|
|
4401
|
+
"'{{with .Module}}{{.Path}}|{{.Version}}|{{.Indirect}}|{{.GoMod}}|{{.GoVersion}}|{{.Main}}|{{.Time}}|{{.Deprecated}}|{{.GoModSum}}|{{.Dir}}{{end}}'",
|
|
4401
4402
|
"./...",
|
|
4402
4403
|
],
|
|
4403
4404
|
{
|
|
@@ -6407,6 +6408,7 @@ export async function createRubyBom(path, options) {
|
|
|
6407
6408
|
const gemLockExcludeList = (options.exclude || []).concat([
|
|
6408
6409
|
"**/vendor/bundle/ruby/**/Gemfile.lock",
|
|
6409
6410
|
"**/test/data/**/Gemfile*.lock",
|
|
6411
|
+
"**/.rbenv/versions/**/Gemfile.lock",
|
|
6410
6412
|
]);
|
|
6411
6413
|
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
6412
6414
|
excludeList.push("**/vendor/bundle/**");
|
|
@@ -6509,6 +6511,7 @@ export async function createRubyBom(path, options) {
|
|
|
6509
6511
|
}
|
|
6510
6512
|
}
|
|
6511
6513
|
// Parsing .gemspec files would help us get more metadata such as description, authors, licenses etc
|
|
6514
|
+
let rootGemspecComponent;
|
|
6512
6515
|
if (gemspecFiles.length) {
|
|
6513
6516
|
if (!gemLockFiles.length && !hasAnyProjectType(["oci"], options, false)) {
|
|
6514
6517
|
console.log(
|
|
@@ -6522,8 +6525,33 @@ export async function createRubyBom(path, options) {
|
|
|
6522
6525
|
if (gpkgList.length) {
|
|
6523
6526
|
pkgList = pkgList.concat(gpkgList);
|
|
6524
6527
|
pkgList = trimComponents(pkgList);
|
|
6528
|
+
if (
|
|
6529
|
+
!rootGemspecComponent &&
|
|
6530
|
+
dirname(resolve(f)) === resolve(path) &&
|
|
6531
|
+
gpkgList[0]?.name
|
|
6532
|
+
) {
|
|
6533
|
+
rootGemspecComponent = gpkgList[0];
|
|
6534
|
+
}
|
|
6525
6535
|
}
|
|
6526
6536
|
}
|
|
6537
|
+
if (
|
|
6538
|
+
rootGemspecComponent &&
|
|
6539
|
+
!("project-name" in options) &&
|
|
6540
|
+
options.projectName === undefined
|
|
6541
|
+
) {
|
|
6542
|
+
parentComponent.name = rootGemspecComponent.name;
|
|
6543
|
+
parentComponent.version = rootGemspecComponent.version || "latest";
|
|
6544
|
+
const parentPurl = new PackageURL(
|
|
6545
|
+
"gem",
|
|
6546
|
+
parentComponent.group,
|
|
6547
|
+
parentComponent.name,
|
|
6548
|
+
parentComponent.version,
|
|
6549
|
+
null,
|
|
6550
|
+
null,
|
|
6551
|
+
).toString();
|
|
6552
|
+
parentComponent["bom-ref"] = decodeURIComponent(parentPurl);
|
|
6553
|
+
parentComponent["purl"] = parentPurl;
|
|
6554
|
+
}
|
|
6527
6555
|
}
|
|
6528
6556
|
if (rootList.length) {
|
|
6529
6557
|
dependencies = mergeDependencies(
|
package/lib/helpers/utils.js
CHANGED
|
@@ -7257,18 +7257,20 @@ export async function parseGoModData(goModData, gosumMap) {
|
|
|
7257
7257
|
isTool = false;
|
|
7258
7258
|
continue;
|
|
7259
7259
|
}
|
|
7260
|
-
if (l.includes("tool ")) {
|
|
7260
|
+
if (l.includes("tool ") || isTool) {
|
|
7261
7261
|
continue;
|
|
7262
7262
|
}
|
|
7263
|
-
if (
|
|
7263
|
+
if (l.startsWith("toolchain ")) {
|
|
7264
|
+
const toolchainVer = l.split(" ").pop().trim();
|
|
7265
|
+
parentComponent.properties = [
|
|
7266
|
+
{ name: "cdx:go:toolchain", value: toolchainVer },
|
|
7267
|
+
];
|
|
7264
7268
|
continue;
|
|
7265
7269
|
}
|
|
7266
|
-
|
|
7267
7270
|
// Skip go.mod file headers, whitespace, and/or comments
|
|
7268
7271
|
if (
|
|
7269
7272
|
l.startsWith("go ") ||
|
|
7270
7273
|
//TODO: should toolchain be considered as a dependency
|
|
7271
|
-
l.startsWith("toolchain ") ||
|
|
7272
7274
|
l.includes(")") ||
|
|
7273
7275
|
l.trim() === "" ||
|
|
7274
7276
|
l.trim().startsWith("//")
|
|
@@ -7378,14 +7380,17 @@ export async function parseGoListDep(rawOutput, gosumMap) {
|
|
|
7378
7380
|
.split("\n")
|
|
7379
7381
|
.filter((p) => p.trim().replace(/["']/g, "").length);
|
|
7380
7382
|
for (const l of pkgs) {
|
|
7381
|
-
const verArr = l.trim().replace(/["']/g, "").split("
|
|
7383
|
+
const verArr = l.trim().replace(/["']/g, "").split("|");
|
|
7382
7384
|
if (verArr && verArr.length >= 5) {
|
|
7383
7385
|
const key = `${verArr[0]}-${verArr[1]}`;
|
|
7384
7386
|
// Filter duplicates
|
|
7385
7387
|
if (!keys_cache[key]) {
|
|
7386
7388
|
keys_cache[key] = key;
|
|
7387
7389
|
const version = verArr[1];
|
|
7388
|
-
|
|
7390
|
+
let gosumHash = gosumMap[`${verArr[0]}@${version}`];
|
|
7391
|
+
if (!gosumHash && verArr.length >= 8 && verArr[8]?.length) {
|
|
7392
|
+
gosumHash = `sha256-${verArr[8].replace("h1:", "")}`;
|
|
7393
|
+
}
|
|
7389
7394
|
const component = await getGoPkgComponent(
|
|
7390
7395
|
"",
|
|
7391
7396
|
verArr[0],
|
|
@@ -7412,6 +7417,43 @@ export async function parseGoListDep(rawOutput, gosumMap) {
|
|
|
7412
7417
|
value: verArr[2],
|
|
7413
7418
|
},
|
|
7414
7419
|
];
|
|
7420
|
+
if (
|
|
7421
|
+
verArr.length >= 6 &&
|
|
7422
|
+
verArr[6]?.length &&
|
|
7423
|
+
verArr[6] !== "<nil>"
|
|
7424
|
+
) {
|
|
7425
|
+
component.properties.push({
|
|
7426
|
+
name: "cdx:go:creation_time",
|
|
7427
|
+
value: verArr[6],
|
|
7428
|
+
});
|
|
7429
|
+
}
|
|
7430
|
+
if (verArr.length >= 7 && verArr[7]?.length) {
|
|
7431
|
+
component.properties.push({
|
|
7432
|
+
name: "cdx:go:deprecated",
|
|
7433
|
+
value: verArr[7],
|
|
7434
|
+
});
|
|
7435
|
+
}
|
|
7436
|
+
if (verArr.length >= 9 && verArr[9]?.length) {
|
|
7437
|
+
component.properties.push({
|
|
7438
|
+
name: "cdx:go:local_dir",
|
|
7439
|
+
value: verArr[9],
|
|
7440
|
+
});
|
|
7441
|
+
if (safeExistsSync(join(verArr[9], "LICENSE"))) {
|
|
7442
|
+
const licenseText = readFileSync(join(verArr[9], "LICENSE"), {
|
|
7443
|
+
encoding: "utf-8",
|
|
7444
|
+
});
|
|
7445
|
+
if (licenseText?.length) {
|
|
7446
|
+
component.licenses = [
|
|
7447
|
+
{
|
|
7448
|
+
license: {
|
|
7449
|
+
name: "CUSTOM",
|
|
7450
|
+
text: { contentType: "text/plain", content: licenseText },
|
|
7451
|
+
},
|
|
7452
|
+
},
|
|
7453
|
+
];
|
|
7454
|
+
}
|
|
7455
|
+
}
|
|
7456
|
+
}
|
|
7415
7457
|
if (verArr.length > 5 && verArr[5] === "true") {
|
|
7416
7458
|
parentComponent = component;
|
|
7417
7459
|
} else {
|
|
@@ -16059,29 +16101,24 @@ export function parseCmakeLikeFile(cmakeListFile, pkgType, options = {}) {
|
|
|
16059
16101
|
}
|
|
16060
16102
|
}
|
|
16061
16103
|
} else if (l.includes("dependency(")) {
|
|
16062
|
-
|
|
16063
|
-
|
|
16064
|
-
|
|
16065
|
-
|
|
16066
|
-
|
|
16067
|
-
|
|
16068
|
-
|
|
16069
|
-
|
|
16070
|
-
if (
|
|
16071
|
-
|
|
16072
|
-
|
|
16073
|
-
|
|
16074
|
-
|
|
16075
|
-
|
|
16076
|
-
|
|
16077
|
-
|
|
16078
|
-
|
|
16079
|
-
|
|
16080
|
-
) {
|
|
16081
|
-
// We have a valid version
|
|
16082
|
-
versionsMap[tmpA[0]] = tmpB[1];
|
|
16083
|
-
}
|
|
16084
|
-
}
|
|
16104
|
+
if (!l.includes("_dependency") && !l.includes(".dependency")) {
|
|
16105
|
+
const depMatch = l.match(/dependency\(\s*['"]?([^'",)\s]+)['"]?/);
|
|
16106
|
+
const depName = depMatch?.[1]?.trim();
|
|
16107
|
+
if (depName) {
|
|
16108
|
+
name_list.push(depName);
|
|
16109
|
+
const versionMatch = l.match(/version\s*:\s*['"]?([^'",)\s]+)['"]?/);
|
|
16110
|
+
const depVersion = versionMatch?.[1]?.trim();
|
|
16111
|
+
if (depVersion) {
|
|
16112
|
+
if (depVersion.includes(">") || depVersion.includes("<")) {
|
|
16113
|
+
// We have a version specifier
|
|
16114
|
+
versionSpecifiersMap[depName] = depVersion;
|
|
16115
|
+
} else if (
|
|
16116
|
+
/^\d+/.test(depVersion) &&
|
|
16117
|
+
!depVersion.includes("${") &&
|
|
16118
|
+
!depVersion.startsWith("@")
|
|
16119
|
+
) {
|
|
16120
|
+
// We have a valid version
|
|
16121
|
+
versionsMap[depName] = depVersion;
|
|
16085
16122
|
}
|
|
16086
16123
|
}
|
|
16087
16124
|
}
|
|
@@ -1475,7 +1475,7 @@ describe("go data with licenses", () => {
|
|
|
1475
1475
|
});
|
|
1476
1476
|
|
|
1477
1477
|
it("parse go list dependencies", async () => {
|
|
1478
|
-
|
|
1478
|
+
let retMap = await parseGoListDep(
|
|
1479
1479
|
readFileSync("./test/data/golist-dep.txt", { encoding: "utf-8" }),
|
|
1480
1480
|
{},
|
|
1481
1481
|
);
|
|
@@ -1502,6 +1502,11 @@ it("parse go list dependencies", async () => {
|
|
|
1502
1502
|
},
|
|
1503
1503
|
],
|
|
1504
1504
|
});
|
|
1505
|
+
retMap = await parseGoListDep(
|
|
1506
|
+
readFileSync("./test/data/golist-dep3.txt", { encoding: "utf-8" }),
|
|
1507
|
+
{},
|
|
1508
|
+
);
|
|
1509
|
+
assert.deepStrictEqual(retMap.pkgList.length, 291);
|
|
1505
1510
|
});
|
|
1506
1511
|
|
|
1507
1512
|
it("parse go mod graph", async () => {
|
|
@@ -7645,6 +7650,21 @@ it("parseCmakeLikeFile tests", () => {
|
|
|
7645
7650
|
version: "20230125.1",
|
|
7646
7651
|
});
|
|
7647
7652
|
assert.deepStrictEqual(retMap.pkgList.length, 2);
|
|
7653
|
+
|
|
7654
|
+
retMap = parseCmakeLikeFile(
|
|
7655
|
+
"./test/data/meson-empty-dependency.build",
|
|
7656
|
+
"conan",
|
|
7657
|
+
);
|
|
7658
|
+
assert.deepStrictEqual(retMap.parentComponent, {
|
|
7659
|
+
"bom-ref": "pkg:conan/empty-dep-test",
|
|
7660
|
+
group: "",
|
|
7661
|
+
name: "empty-dep-test",
|
|
7662
|
+
purl: "pkg:conan/empty-dep-test",
|
|
7663
|
+
type: "application",
|
|
7664
|
+
version: "",
|
|
7665
|
+
});
|
|
7666
|
+
assert.deepStrictEqual(retMap.pkgList.length, 1);
|
|
7667
|
+
assert.deepStrictEqual(retMap.pkgList[0].name, "threads");
|
|
7648
7668
|
});
|
|
7649
7669
|
|
|
7650
7670
|
it("parseMakeDFile tests", () => {
|
package/lib/helpers/validator.js
CHANGED
|
@@ -81,6 +81,8 @@ export const validateBom = (bomJson) => {
|
|
|
81
81
|
*
|
|
82
82
|
* @param {object} bomJson Bom json object
|
|
83
83
|
*/
|
|
84
|
+
const PLACEHOLDER_COMPONENT_NAMES = new Set(["app", "application", "project"]);
|
|
85
|
+
|
|
84
86
|
export const validateMetadata = (bomJson) => {
|
|
85
87
|
const errorList = [];
|
|
86
88
|
const warningsList = [];
|
|
@@ -107,6 +109,14 @@ export const validateMetadata = (bomJson) => {
|
|
|
107
109
|
"Version is missing for metadata.component. Pass the version using --project-version argument.",
|
|
108
110
|
);
|
|
109
111
|
}
|
|
112
|
+
const metadataName = bomJson.metadata.component.name
|
|
113
|
+
?.trim()
|
|
114
|
+
.toLowerCase();
|
|
115
|
+
if (metadataName && PLACEHOLDER_COMPONENT_NAMES.has(metadataName)) {
|
|
116
|
+
warningsList.push(
|
|
117
|
+
`metadata.component.name appears to be a placeholder ('${bomJson.metadata.component.name}'). Pass --project-name to set the correct parent component name.`,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
110
120
|
// Is the same component getting repeated inside the components block
|
|
111
121
|
if (bomJson.metadata.component.components?.length) {
|
|
112
122
|
for (const comp of bomJson.metadata.component.components) {
|
|
@@ -198,6 +208,27 @@ export const validatePurls = (bomJson) => {
|
|
|
198
208
|
}
|
|
199
209
|
// Catch the trivy version hack that removes the epoch from version
|
|
200
210
|
const qualifiers = purlObj.qualifiers || {};
|
|
211
|
+
if (
|
|
212
|
+
Object.keys(qualifiers).length &&
|
|
213
|
+
[
|
|
214
|
+
"cargo",
|
|
215
|
+
"cocoapods",
|
|
216
|
+
"composer",
|
|
217
|
+
"cran",
|
|
218
|
+
"github",
|
|
219
|
+
"golang",
|
|
220
|
+
"hackage",
|
|
221
|
+
"nuget",
|
|
222
|
+
"opam",
|
|
223
|
+
"pub",
|
|
224
|
+
"qpkg",
|
|
225
|
+
"swift",
|
|
226
|
+
].includes(purlObj.type)
|
|
227
|
+
) {
|
|
228
|
+
warningsList.push(
|
|
229
|
+
`Qualifiers are usually not expected for the PURL type: ${purlObj.type}. Purl: ${comp.purl}, Qualifiers: ${Object.keys(qualifiers).join(", ")}.`,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
201
232
|
if (
|
|
202
233
|
qualifiers.epoch &&
|
|
203
234
|
!comp.version.startsWith(`${qualifiers.epoch}:`)
|
package/lib/managers/binary.js
CHANGED
|
@@ -7,7 +7,14 @@ import {
|
|
|
7
7
|
statSync,
|
|
8
8
|
} from "node:fs";
|
|
9
9
|
import { arch as _arch, platform as _platform, homedir } from "node:os";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
basename,
|
|
12
|
+
delimiter,
|
|
13
|
+
dirname,
|
|
14
|
+
join,
|
|
15
|
+
relative,
|
|
16
|
+
resolve,
|
|
17
|
+
} from "node:path";
|
|
11
18
|
import process from "node:process";
|
|
12
19
|
|
|
13
20
|
import { PackageURL } from "packageurl-js";
|
|
@@ -27,9 +34,11 @@ import {
|
|
|
27
34
|
safeMkdirSync,
|
|
28
35
|
safeSpawnSync,
|
|
29
36
|
} from "../helpers/utils.js";
|
|
37
|
+
import { getDirs } from "./containerutils.js";
|
|
30
38
|
|
|
31
39
|
const dirName = dirNameStr;
|
|
32
40
|
const isWin = _platform() === "win32";
|
|
41
|
+
const OS_PURL_TYPES = ["deb", "apk", "rpm", "alpm", "qpkg"];
|
|
33
42
|
|
|
34
43
|
function isMusl() {
|
|
35
44
|
const result = safeSpawnSync("ldd", ["--version"]);
|
|
@@ -444,7 +453,20 @@ export async function getOSPackages(src, imageConfig) {
|
|
|
444
453
|
const allTypes = new Set();
|
|
445
454
|
const bundledSdks = new Set();
|
|
446
455
|
const bundledRuntimes = new Set();
|
|
447
|
-
|
|
456
|
+
let binPaths = extractPathEnv(imageConfig?.Env);
|
|
457
|
+
if (!binPaths?.length && DEBUG_MODE) {
|
|
458
|
+
const rootBinPaths = getDirs(src, "{sbin,bin}", true, false);
|
|
459
|
+
const usrBinPaths = getDirs(
|
|
460
|
+
src,
|
|
461
|
+
"/{app,opt,usr,home}/**/{sbin,bin}",
|
|
462
|
+
true,
|
|
463
|
+
true,
|
|
464
|
+
);
|
|
465
|
+
binPaths = binPaths
|
|
466
|
+
.concat(rootBinPaths)
|
|
467
|
+
.concat(usrBinPaths)
|
|
468
|
+
.map((f) => relative(src, f));
|
|
469
|
+
}
|
|
448
470
|
if (TRIVY_BIN) {
|
|
449
471
|
let imageType = "image";
|
|
450
472
|
const trivyCacheDir = join(homedir(), ".cache", "trivy");
|
|
@@ -607,7 +629,7 @@ export async function getOSPackages(src, imageConfig) {
|
|
|
607
629
|
} catch (_err) {
|
|
608
630
|
// continue regardless of error
|
|
609
631
|
}
|
|
610
|
-
if (group === "") {
|
|
632
|
+
if (group === "" && OS_PURL_TYPES.includes(purlObj["type"])) {
|
|
611
633
|
try {
|
|
612
634
|
if (purlObj?.namespace && purlObj.namespace !== "") {
|
|
613
635
|
group = purlObj.namespace;
|
|
@@ -955,16 +977,16 @@ const retrieveDependencies = (tmpDependencies, origBomRef, comp) => {
|
|
|
955
977
|
const tmpPurl = PackageURL.fromString(d.replace("none", compPurl.type));
|
|
956
978
|
tmpPurl.type = compPurl.type;
|
|
957
979
|
// FIXME: Check if this hack is still needed with the latest trivy
|
|
958
|
-
if (
|
|
980
|
+
if (OS_PURL_TYPES.includes(compPurl.type)) {
|
|
959
981
|
tmpPurl.namespace = compPurl.namespace;
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
982
|
+
tmpPurl.qualifiers = tmpPurl.qualifiers || {};
|
|
983
|
+
if (compPurl.qualifiers) {
|
|
984
|
+
if (compPurl.qualifiers.distro_name) {
|
|
985
|
+
tmpPurl.qualifiers.distro_name = compPurl.qualifiers.distro_name;
|
|
986
|
+
}
|
|
987
|
+
if (compPurl.qualifiers.distro) {
|
|
988
|
+
tmpPurl.qualifiers.distro = compPurl.qualifiers.distro;
|
|
989
|
+
}
|
|
968
990
|
}
|
|
969
991
|
}
|
|
970
992
|
if (tmpPurl.qualifiers) {
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { lstatSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { globSync } from "glob";
|
|
5
|
+
|
|
6
|
+
import { safeExistsSync } from "../helpers/utils.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Method to get all dirs matching a name
|
|
10
|
+
*
|
|
11
|
+
* @param {string} dirPath Root directory for search
|
|
12
|
+
* @param {string} dirName Directory name
|
|
13
|
+
* @param {boolean} hidden Include hidden directories and files. Default: false
|
|
14
|
+
* @param {boolean} recurse Recurse. Default: false
|
|
15
|
+
*/
|
|
16
|
+
export const getDirs = (dirPath, dirName, hidden = false, recurse = true) => {
|
|
17
|
+
try {
|
|
18
|
+
return globSync(`${recurse ? "**" : ""}${dirName}`, {
|
|
19
|
+
cwd: dirPath,
|
|
20
|
+
absolute: true,
|
|
21
|
+
nocase: true,
|
|
22
|
+
nodir: false,
|
|
23
|
+
follow: false,
|
|
24
|
+
dot: hidden,
|
|
25
|
+
});
|
|
26
|
+
} catch (_err) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function flatten(lists) {
|
|
32
|
+
return lists.reduce((a, b) => a.concat(b), []);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getDirectories(srcpath) {
|
|
36
|
+
if (safeExistsSync(srcpath)) {
|
|
37
|
+
return readdirSync(srcpath)
|
|
38
|
+
.map((file) => join(srcpath, file))
|
|
39
|
+
.filter((path) => {
|
|
40
|
+
try {
|
|
41
|
+
return statSync(path).isDirectory();
|
|
42
|
+
} catch (_e) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const getOnlyDirs = (srcpath, dirName) => {
|
|
51
|
+
return [
|
|
52
|
+
srcpath,
|
|
53
|
+
...flatten(
|
|
54
|
+
getDirectories(srcpath)
|
|
55
|
+
.map((p) => {
|
|
56
|
+
try {
|
|
57
|
+
if (safeExistsSync(p) && lstatSync(p).isDirectory()) {
|
|
58
|
+
return getOnlyDirs(p, dirName);
|
|
59
|
+
}
|
|
60
|
+
return [];
|
|
61
|
+
} catch (_err) {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
.filter((p) => p !== undefined),
|
|
66
|
+
),
|
|
67
|
+
].filter((d) => d.endsWith(dirName));
|
|
68
|
+
};
|
package/lib/managers/docker.js
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
readdirSync,
|
|
7
7
|
readFileSync,
|
|
8
8
|
rmSync,
|
|
9
|
-
statSync,
|
|
10
9
|
} from "node:fs";
|
|
11
10
|
import { platform as _platform, userInfo as _userInfo, homedir } from "node:os";
|
|
12
11
|
import { basename, join, resolve, win32 } from "node:path";
|
|
@@ -14,7 +13,6 @@ import process from "node:process";
|
|
|
14
13
|
import stream from "node:stream/promises";
|
|
15
14
|
import { URL } from "node:url";
|
|
16
15
|
|
|
17
|
-
import { globSync } from "glob";
|
|
18
16
|
import got from "got";
|
|
19
17
|
import { x } from "tar";
|
|
20
18
|
|
|
@@ -27,6 +25,7 @@ import {
|
|
|
27
25
|
safeMkdirSync,
|
|
28
26
|
safeSpawnSync,
|
|
29
27
|
} from "../helpers/utils.js";
|
|
28
|
+
import { getDirs, getOnlyDirs } from "./containerutils.js";
|
|
30
29
|
|
|
31
30
|
export const isWin = _platform() === "win32";
|
|
32
31
|
export const DOCKER_HUB_REGISTRY = "docker.io";
|
|
@@ -156,67 +155,6 @@ export function detectRancherDesktop() {
|
|
|
156
155
|
|
|
157
156
|
// Cache the registry auth keys
|
|
158
157
|
const registry_auth_keys = {};
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Method to get all dirs matching a name
|
|
162
|
-
*
|
|
163
|
-
* @param {string} dirPath Root directory for search
|
|
164
|
-
* @param {string} dirName Directory name
|
|
165
|
-
*/
|
|
166
|
-
export const getDirs = (dirPath, dirName, hidden = false, recurse = true) => {
|
|
167
|
-
try {
|
|
168
|
-
return globSync(recurse ? "**/" : `${dirName}`, {
|
|
169
|
-
cwd: dirPath,
|
|
170
|
-
absolute: true,
|
|
171
|
-
nocase: true,
|
|
172
|
-
nodir: false,
|
|
173
|
-
follow: false,
|
|
174
|
-
dot: hidden,
|
|
175
|
-
});
|
|
176
|
-
} catch (_err) {
|
|
177
|
-
return [];
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
function flatten(lists) {
|
|
182
|
-
return lists.reduce((a, b) => a.concat(b), []);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function getDirectories(srcpath) {
|
|
186
|
-
if (safeExistsSync(srcpath)) {
|
|
187
|
-
return readdirSync(srcpath)
|
|
188
|
-
.map((file) => join(srcpath, file))
|
|
189
|
-
.filter((path) => {
|
|
190
|
-
try {
|
|
191
|
-
return statSync(path).isDirectory();
|
|
192
|
-
} catch (_e) {
|
|
193
|
-
return false;
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
return [];
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
export const getOnlyDirs = (srcpath, dirName) => {
|
|
201
|
-
return [
|
|
202
|
-
srcpath,
|
|
203
|
-
...flatten(
|
|
204
|
-
getDirectories(srcpath)
|
|
205
|
-
.map((p) => {
|
|
206
|
-
try {
|
|
207
|
-
if (safeExistsSync(p) && lstatSync(p).isDirectory()) {
|
|
208
|
-
return getOnlyDirs(p, dirName);
|
|
209
|
-
}
|
|
210
|
-
return [];
|
|
211
|
-
} catch (_err) {
|
|
212
|
-
return [];
|
|
213
|
-
}
|
|
214
|
-
})
|
|
215
|
-
.filter((p) => p !== undefined),
|
|
216
|
-
),
|
|
217
|
-
].filter((d) => d.endsWith(dirName));
|
|
218
|
-
};
|
|
219
|
-
|
|
220
158
|
const REQUEST_TIMEOUT_SECS = 60000;
|
|
221
159
|
const getDefaultOptions = (forRegistry) => {
|
|
222
160
|
let authTokenSet = false;
|
|
@@ -844,6 +782,33 @@ function handleAbsolutePath(entry) {
|
|
|
844
782
|
}
|
|
845
783
|
}
|
|
846
784
|
|
|
785
|
+
/**
|
|
786
|
+
* Filter out problematic files, paths, and devices during tar extraction.
|
|
787
|
+
*/
|
|
788
|
+
function tarFilter(path, entry) {
|
|
789
|
+
const name = basename(path);
|
|
790
|
+
if (name.startsWith(".wh.")) {
|
|
791
|
+
return false;
|
|
792
|
+
}
|
|
793
|
+
const ext = win32.extname(name).toLowerCase();
|
|
794
|
+
if (MEDIA_EXTENSIONS.has(ext)) {
|
|
795
|
+
return false;
|
|
796
|
+
}
|
|
797
|
+
return !(
|
|
798
|
+
EXTRACT_EXCLUDE_PATHS.some((p) => path.includes(p)) ||
|
|
799
|
+
EXTRACT_EXCLUDE_TYPES.has(entry.type)
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
function handleTarWarning(code, message) {
|
|
804
|
+
if (code === "TAR_ENTRY_INFO" || code === "TAR_LONGLINK") {
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
if (DEBUG_MODE) {
|
|
808
|
+
console.log(code, message);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
847
812
|
// These paths are known to cause extract errors
|
|
848
813
|
const EXTRACT_EXCLUDE_PATHS = [
|
|
849
814
|
"etc/machine-id",
|
|
@@ -918,29 +883,9 @@ export const extractTar = async (fullImageName, dir, options) => {
|
|
|
918
883
|
C: dir,
|
|
919
884
|
portable: true,
|
|
920
885
|
unlink: true,
|
|
921
|
-
onwarn:
|
|
922
|
-
if (code === "TAR_ENTRY_INFO" || code === "TAR_LONGLINK") {
|
|
923
|
-
return;
|
|
924
|
-
}
|
|
925
|
-
if (DEBUG_MODE) {
|
|
926
|
-
console.log(code, message);
|
|
927
|
-
}
|
|
928
|
-
},
|
|
886
|
+
onwarn: handleTarWarning,
|
|
929
887
|
onReadEntry: handleAbsolutePath,
|
|
930
|
-
filter:
|
|
931
|
-
const name = basename(path);
|
|
932
|
-
if (name.startsWith(".wh.")) {
|
|
933
|
-
return false;
|
|
934
|
-
}
|
|
935
|
-
const ext = win32.extname(name).toLowerCase();
|
|
936
|
-
if (MEDIA_EXTENSIONS.has(ext)) {
|
|
937
|
-
return false;
|
|
938
|
-
}
|
|
939
|
-
return !(
|
|
940
|
-
EXTRACT_EXCLUDE_PATHS.some((p) => path.includes(p)) ||
|
|
941
|
-
EXTRACT_EXCLUDE_TYPES.has(entry.type)
|
|
942
|
-
);
|
|
943
|
-
},
|
|
888
|
+
filter: tarFilter,
|
|
944
889
|
}),
|
|
945
890
|
);
|
|
946
891
|
return true;
|
|
@@ -1238,19 +1183,17 @@ export const exportImage = async (fullImageName, options) => {
|
|
|
1238
1183
|
await stream.pipeline(
|
|
1239
1184
|
client.stream(`images/${fullImageName}/get`),
|
|
1240
1185
|
x({
|
|
1241
|
-
sync:
|
|
1186
|
+
sync: false,
|
|
1242
1187
|
preserveOwner: false,
|
|
1243
1188
|
noMtime: true,
|
|
1244
1189
|
noChmod: true,
|
|
1245
1190
|
strict: !NON_STRICT_TAR_EXTRACT,
|
|
1246
1191
|
C: tempDir,
|
|
1247
1192
|
portable: true,
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
console.log(code, message);
|
|
1251
|
-
}
|
|
1252
|
-
},
|
|
1193
|
+
unlink: true,
|
|
1194
|
+
onwarn: handleTarWarning,
|
|
1253
1195
|
onReadEntry: handleAbsolutePath,
|
|
1196
|
+
filter: tarFilter,
|
|
1254
1197
|
}),
|
|
1255
1198
|
);
|
|
1256
1199
|
} catch (_err) {
|
|
@@ -1267,12 +1210,9 @@ export const exportImage = async (fullImageName, options) => {
|
|
|
1267
1210
|
strict: !NON_STRICT_TAR_EXTRACT,
|
|
1268
1211
|
C: tempDir,
|
|
1269
1212
|
portable: true,
|
|
1270
|
-
onwarn:
|
|
1271
|
-
if (DEBUG_MODE) {
|
|
1272
|
-
console.log(code, message);
|
|
1273
|
-
}
|
|
1274
|
-
},
|
|
1213
|
+
onwarn: handleTarWarning,
|
|
1275
1214
|
onReadEntry: handleAbsolutePath,
|
|
1215
|
+
filter: tarFilter,
|
|
1276
1216
|
}),
|
|
1277
1217
|
);
|
|
1278
1218
|
} catch (_err) {
|