@cyclonedx/cdxgen 12.2.0 → 12.3.0
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 +242 -90
- package/bin/audit.js +191 -0
- package/bin/cdxgen.js +532 -168
- package/bin/convert.js +99 -0
- package/bin/evinse.js +23 -0
- package/bin/repl.js +339 -8
- package/bin/sign.js +8 -0
- package/bin/validate.js +8 -0
- package/bin/verify.js +8 -0
- package/data/container-knowledge-index.json +125 -0
- package/data/gtfobins-index.json +6296 -0
- package/data/lolbas-index.json +150 -0
- package/data/queries-darwin.json +63 -3
- package/data/queries-win.json +45 -3
- package/data/queries.json +74 -2
- package/data/rules/chrome-extensions.yaml +240 -0
- package/data/rules/ci-permissions.yaml +478 -18
- package/data/rules/container-risk.yaml +270 -0
- package/data/rules/obom-runtime.yaml +891 -0
- package/data/rules/package-integrity.yaml +49 -0
- package/data/spdx-export.schema.json +6794 -0
- package/data/spdx-model-v3.0.1.jsonld +15999 -0
- package/lib/audit/index.js +1924 -0
- package/lib/audit/index.poku.js +1488 -0
- package/lib/audit/progress.js +137 -0
- package/lib/audit/progress.poku.js +188 -0
- package/lib/audit/reporters.js +618 -0
- package/lib/audit/scoring.js +310 -0
- package/lib/audit/scoring.poku.js +341 -0
- package/lib/audit/targets.js +260 -0
- package/lib/audit/targets.poku.js +331 -0
- package/lib/cli/index.js +276 -68
- package/lib/cli/index.poku.js +368 -0
- package/lib/helpers/analyzer.js +1052 -5
- package/lib/helpers/analyzer.poku.js +301 -0
- package/lib/helpers/annotationFormatter.js +49 -0
- package/lib/helpers/annotationFormatter.poku.js +44 -0
- package/lib/helpers/bomUtils.js +36 -0
- package/lib/helpers/bomUtils.poku.js +51 -0
- package/lib/helpers/caxa.js +2 -2
- package/lib/helpers/chromextutils.js +1153 -0
- package/lib/helpers/chromextutils.poku.js +493 -0
- package/lib/helpers/ciParsers/githubActions.js +1632 -45
- package/lib/helpers/ciParsers/githubActions.poku.js +853 -1
- package/lib/helpers/containerRisk.js +186 -0
- package/lib/helpers/containerRisk.poku.js +52 -0
- package/lib/helpers/depsUtils.js +16 -0
- package/lib/helpers/depsUtils.poku.js +58 -1
- package/lib/helpers/display.js +245 -61
- package/lib/helpers/display.poku.js +162 -2
- package/lib/helpers/exportUtils.js +123 -0
- package/lib/helpers/exportUtils.poku.js +60 -0
- package/lib/helpers/formulationParsers.js +69 -0
- package/lib/helpers/formulationParsers.poku.js +44 -0
- package/lib/helpers/gtfobins.js +189 -0
- package/lib/helpers/gtfobins.poku.js +49 -0
- package/lib/helpers/lolbas.js +267 -0
- package/lib/helpers/lolbas.poku.js +39 -0
- package/lib/helpers/osqueryTransform.js +84 -0
- package/lib/helpers/osqueryTransform.poku.js +49 -0
- package/lib/helpers/provenanceUtils.js +193 -0
- package/lib/helpers/provenanceUtils.poku.js +145 -0
- package/lib/helpers/pylockutils.js +281 -0
- package/lib/helpers/pylockutils.poku.js +48 -0
- package/lib/helpers/registryProvenance.js +793 -0
- package/lib/helpers/registryProvenance.poku.js +452 -0
- package/lib/helpers/remote/dependency-track.js +84 -0
- package/lib/helpers/remote/dependency-track.poku.js +119 -0
- package/lib/helpers/source.js +1267 -0
- package/lib/helpers/source.poku.js +771 -0
- package/lib/helpers/spdxUtils.js +97 -0
- package/lib/helpers/spdxUtils.poku.js +70 -0
- package/lib/helpers/table.js +384 -0
- package/lib/helpers/table.poku.js +186 -0
- package/lib/helpers/unicodeScan.js +147 -0
- package/lib/helpers/unicodeScan.poku.js +45 -0
- package/lib/helpers/utils.js +882 -136
- package/lib/helpers/utils.poku.js +995 -91
- package/lib/managers/binary.js +29 -5
- package/lib/managers/docker.js +179 -52
- package/lib/managers/docker.poku.js +327 -28
- package/lib/managers/oci.js +107 -23
- package/lib/managers/oci.poku.js +132 -0
- package/lib/server/openapi.yaml +50 -0
- package/lib/server/server.js +228 -331
- package/lib/server/server.poku.js +220 -5
- package/lib/stages/postgen/annotator.js +7 -0
- package/lib/stages/postgen/annotator.poku.js +40 -0
- package/lib/stages/postgen/auditBom.js +20 -5
- package/lib/stages/postgen/auditBom.poku.js +1729 -67
- package/lib/stages/postgen/postgen.js +40 -0
- package/lib/stages/postgen/postgen.poku.js +47 -0
- package/lib/stages/postgen/ruleEngine.js +80 -2
- package/lib/stages/postgen/spdxConverter.js +796 -0
- package/lib/stages/postgen/spdxConverter.poku.js +341 -0
- package/lib/validator/bomValidator.js +232 -0
- package/lib/validator/bomValidator.poku.js +70 -0
- package/lib/validator/complianceRules.js +70 -7
- package/lib/validator/complianceRules.poku.js +30 -0
- package/lib/validator/reporters/annotations.js +2 -2
- package/lib/validator/reporters/console.js +13 -2
- package/lib/validator/reporters.poku.js +13 -0
- package/package.json +10 -8
- package/types/bin/audit.d.ts +3 -0
- package/types/bin/audit.d.ts.map +1 -0
- package/types/bin/convert.d.ts +3 -0
- package/types/bin/convert.d.ts.map +1 -0
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts +115 -0
- package/types/lib/audit/index.d.ts.map +1 -0
- package/types/lib/audit/progress.d.ts +27 -0
- package/types/lib/audit/progress.d.ts.map +1 -0
- package/types/lib/audit/reporters.d.ts +35 -0
- package/types/lib/audit/reporters.d.ts.map +1 -0
- package/types/lib/audit/scoring.d.ts +35 -0
- package/types/lib/audit/scoring.d.ts.map +1 -0
- package/types/lib/audit/targets.d.ts +63 -0
- package/types/lib/audit/targets.d.ts.map +1 -0
- package/types/lib/cli/index.d.ts +8 -0
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/helpers/analyzer.d.ts +13 -0
- package/types/lib/helpers/analyzer.d.ts.map +1 -1
- package/types/lib/helpers/annotationFormatter.d.ts +23 -0
- package/types/lib/helpers/annotationFormatter.d.ts.map +1 -0
- package/types/lib/helpers/bomUtils.d.ts +5 -0
- package/types/lib/helpers/bomUtils.d.ts.map +1 -0
- package/types/lib/helpers/chromextutils.d.ts +97 -0
- package/types/lib/helpers/chromextutils.d.ts.map +1 -0
- package/types/lib/helpers/ciParsers/githubActions.d.ts +3 -8
- package/types/lib/helpers/ciParsers/githubActions.d.ts.map +1 -1
- package/types/lib/helpers/containerRisk.d.ts +17 -0
- package/types/lib/helpers/containerRisk.d.ts.map +1 -0
- package/types/lib/helpers/depsUtils.d.ts.map +1 -1
- package/types/lib/helpers/display.d.ts +4 -1
- package/types/lib/helpers/display.d.ts.map +1 -1
- package/types/lib/helpers/exportUtils.d.ts +40 -0
- package/types/lib/helpers/exportUtils.d.ts.map +1 -0
- package/types/lib/helpers/formulationParsers.d.ts.map +1 -1
- package/types/lib/helpers/gtfobins.d.ts +17 -0
- package/types/lib/helpers/gtfobins.d.ts.map +1 -0
- package/types/lib/helpers/lolbas.d.ts +16 -0
- package/types/lib/helpers/lolbas.d.ts.map +1 -0
- package/types/lib/helpers/osqueryTransform.d.ts +7 -0
- package/types/lib/helpers/osqueryTransform.d.ts.map +1 -0
- package/types/lib/helpers/provenanceUtils.d.ts +90 -0
- package/types/lib/helpers/provenanceUtils.d.ts.map +1 -0
- package/types/lib/helpers/pylockutils.d.ts +51 -0
- package/types/lib/helpers/pylockutils.d.ts.map +1 -0
- package/types/lib/helpers/registryProvenance.d.ts +17 -0
- package/types/lib/helpers/registryProvenance.d.ts.map +1 -0
- package/types/lib/helpers/remote/dependency-track.d.ts +16 -0
- package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -0
- package/types/lib/helpers/source.d.ts +141 -0
- package/types/lib/helpers/source.d.ts.map +1 -0
- package/types/lib/helpers/spdxUtils.d.ts +2 -0
- package/types/lib/helpers/spdxUtils.d.ts.map +1 -0
- package/types/lib/helpers/table.d.ts +6 -0
- package/types/lib/helpers/table.d.ts.map +1 -0
- package/types/lib/helpers/unicodeScan.d.ts +46 -0
- package/types/lib/helpers/unicodeScan.d.ts.map +1 -0
- package/types/lib/helpers/utils.d.ts +30 -11
- package/types/lib/helpers/utils.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/server/server.d.ts +0 -35
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
- package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/postgen/spdxConverter.d.ts +11 -0
- package/types/lib/stages/postgen/spdxConverter.d.ts.map +1 -0
- package/types/lib/validator/bomValidator.d.ts +1 -0
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/complianceRules.d.ts.map +1 -1
- package/types/lib/validator/reporters/console.d.ts.map +1 -1
- package/types/bin/dependencies.d.ts +0 -3
- package/types/bin/dependencies.d.ts.map +0 -1
- package/types/bin/licenses.d.ts +0 -3
- package/types/bin/licenses.d.ts.map +0 -1
package/lib/managers/binary.js
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
lstatSync,
|
|
3
3
|
mkdtempSync,
|
|
4
4
|
readFileSync,
|
|
5
|
+
realpathSync,
|
|
5
6
|
rmSync,
|
|
6
7
|
statSync,
|
|
7
8
|
} from "node:fs";
|
|
@@ -18,6 +19,8 @@ import process from "node:process";
|
|
|
18
19
|
|
|
19
20
|
import { PackageURL } from "packageurl-js";
|
|
20
21
|
|
|
22
|
+
import { createContainerRiskProperties } from "../helpers/containerRisk.js";
|
|
23
|
+
import { createGtfoBinsProperties } from "../helpers/gtfobins.js";
|
|
21
24
|
import {
|
|
22
25
|
adjustLicenseInformation,
|
|
23
26
|
collectExecutables,
|
|
@@ -454,7 +457,7 @@ export async function getOSPackages(src, imageConfig) {
|
|
|
454
457
|
const bundledSdks = new Set();
|
|
455
458
|
const bundledRuntimes = new Set();
|
|
456
459
|
let binPaths = extractPathEnv(imageConfig?.Env);
|
|
457
|
-
if (!binPaths?.length
|
|
460
|
+
if (!binPaths?.length) {
|
|
458
461
|
const rootBinPaths = getDirs(src, "{sbin,bin}", true, false);
|
|
459
462
|
const usrBinPaths = getDirs(
|
|
460
463
|
src,
|
|
@@ -462,10 +465,19 @@ export async function getOSPackages(src, imageConfig) {
|
|
|
462
465
|
true,
|
|
463
466
|
true,
|
|
464
467
|
);
|
|
465
|
-
binPaths =
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
468
|
+
binPaths = Array.from(
|
|
469
|
+
new Set(
|
|
470
|
+
rootBinPaths
|
|
471
|
+
.concat(usrBinPaths)
|
|
472
|
+
.map((f) => relative(src, f))
|
|
473
|
+
.filter(Boolean),
|
|
474
|
+
),
|
|
475
|
+
).sort();
|
|
476
|
+
if (DEBUG_MODE && binPaths.length) {
|
|
477
|
+
console.log(
|
|
478
|
+
`Falling back to inferred binary paths for ${src}: ${binPaths.join(", ")}`,
|
|
479
|
+
);
|
|
480
|
+
}
|
|
469
481
|
}
|
|
470
482
|
if (TRIVY_BIN) {
|
|
471
483
|
let imageType = "image";
|
|
@@ -1173,6 +1185,16 @@ async function fileComponents(basePath, fileList, fileType) {
|
|
|
1173
1185
|
f = `/${f}`;
|
|
1174
1186
|
}
|
|
1175
1187
|
const name = basename(f);
|
|
1188
|
+
let linkedName;
|
|
1189
|
+
try {
|
|
1190
|
+
const resolvedPath = realpathSync(join(basePath, f.replace(/^\/+/, "")));
|
|
1191
|
+
const linkStats = lstatSync(join(basePath, f.replace(/^\/+/, "")));
|
|
1192
|
+
if (linkStats?.isSymbolicLink()) {
|
|
1193
|
+
linkedName = basename(resolvedPath);
|
|
1194
|
+
}
|
|
1195
|
+
} catch (_e) {
|
|
1196
|
+
// ignore
|
|
1197
|
+
}
|
|
1176
1198
|
const purl = `pkg:generic/${name}`;
|
|
1177
1199
|
let isExecutable;
|
|
1178
1200
|
let isSetuid;
|
|
@@ -1206,6 +1228,8 @@ async function fileComponents(basePath, fileList, fileType) {
|
|
|
1206
1228
|
if (isSticky) {
|
|
1207
1229
|
properties.push({ name: "internal:has_sticky", value: "true" });
|
|
1208
1230
|
}
|
|
1231
|
+
properties.push(...createContainerRiskProperties(name, linkedName));
|
|
1232
|
+
properties.push(...createGtfoBinsProperties(name, linkedName));
|
|
1209
1233
|
components.push({
|
|
1210
1234
|
name,
|
|
1211
1235
|
type: "file",
|
package/lib/managers/docker.js
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
readdirSync,
|
|
7
7
|
readFileSync,
|
|
8
8
|
rmSync,
|
|
9
|
+
writeFileSync,
|
|
9
10
|
} from "node:fs";
|
|
10
11
|
import { platform as _platform, userInfo as _userInfo, homedir } from "node:os";
|
|
11
12
|
import { basename, join, resolve, win32 } from "node:path";
|
|
@@ -616,9 +617,21 @@ export const parseImageName = (fullImageName) => {
|
|
|
616
617
|
*
|
|
617
618
|
* @returns boolean true if we should use the cli. false otherwise
|
|
618
619
|
*/
|
|
620
|
+
const getContainerCliCmd = () => {
|
|
621
|
+
if (process.env.DOCKER_CMD?.trim()) {
|
|
622
|
+
return process.env.DOCKER_CMD.trim();
|
|
623
|
+
}
|
|
624
|
+
detectRancherDesktop() || detectColima();
|
|
625
|
+
if (isNerdctl) {
|
|
626
|
+
return "nerdctl";
|
|
627
|
+
}
|
|
628
|
+
return "docker";
|
|
629
|
+
};
|
|
630
|
+
|
|
619
631
|
const needsCliFallback = () => {
|
|
620
632
|
if (
|
|
621
633
|
["true", "1"].includes(process.env.DOCKER_USE_CLI) ||
|
|
634
|
+
process.env.DOCKER_CMD?.trim() ||
|
|
622
635
|
(_platform() === "darwin" && (detectRancherDesktop() || detectColima()))
|
|
623
636
|
) {
|
|
624
637
|
return true;
|
|
@@ -646,20 +659,14 @@ export const getImage = async (fullImageName) => {
|
|
|
646
659
|
if (tag === "" && digest === "") {
|
|
647
660
|
fullImageName = `${fullImageName}:latest`;
|
|
648
661
|
}
|
|
649
|
-
if (isContainerd) {
|
|
662
|
+
if (isContainerd && !needsCliFallback()) {
|
|
650
663
|
console.log(
|
|
651
664
|
"containerd/nerdctl is currently unsupported. Export the image manually and run cdxgen against the tar image.",
|
|
652
665
|
);
|
|
653
666
|
return undefined;
|
|
654
667
|
}
|
|
655
668
|
if (needsCliFallback()) {
|
|
656
|
-
|
|
657
|
-
if (!process.env.DOCKER_CMD) {
|
|
658
|
-
detectRancherDesktop() || detectColima();
|
|
659
|
-
if (isNerdctl) {
|
|
660
|
-
dockerCmd = "nerdctl";
|
|
661
|
-
}
|
|
662
|
-
}
|
|
669
|
+
const dockerCmd = getContainerCliCmd();
|
|
663
670
|
let needsPull = true;
|
|
664
671
|
// Let's check the local cache first
|
|
665
672
|
let result = safeSpawnSync(dockerCmd, ["images", "--format=json"]);
|
|
@@ -697,19 +704,21 @@ export const getImage = async (fullImageName) => {
|
|
|
697
704
|
if (result.stderr) {
|
|
698
705
|
console.log(result.stderr);
|
|
699
706
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
707
|
+
// Continue with the daemon client when the CLI fallback is unavailable
|
|
708
|
+
// or unable to inspect the image.
|
|
709
|
+
} else {
|
|
710
|
+
try {
|
|
711
|
+
const stdout = result.stdout;
|
|
712
|
+
if (stdout) {
|
|
713
|
+
const inspectData = JSON.parse(Buffer.from(stdout).toString());
|
|
714
|
+
if (inspectData && Array.isArray(inspectData)) {
|
|
715
|
+
return inspectData[0];
|
|
716
|
+
}
|
|
717
|
+
return inspectData;
|
|
708
718
|
}
|
|
709
|
-
|
|
719
|
+
} catch (_err) {
|
|
720
|
+
// continue regardless of error
|
|
710
721
|
}
|
|
711
|
-
} catch (_err) {
|
|
712
|
-
// continue regardless of error
|
|
713
722
|
}
|
|
714
723
|
}
|
|
715
724
|
try {
|
|
@@ -1017,6 +1026,101 @@ export const extractTar = async (fullImageName, dir, options) => {
|
|
|
1017
1026
|
}
|
|
1018
1027
|
};
|
|
1019
1028
|
|
|
1029
|
+
const readArchiveJson = (jsonFile) => {
|
|
1030
|
+
if (!jsonFile || !safeExistsSync(jsonFile)) {
|
|
1031
|
+
return undefined;
|
|
1032
|
+
}
|
|
1033
|
+
return JSON.parse(
|
|
1034
|
+
readFileSync(jsonFile, {
|
|
1035
|
+
encoding: "utf-8",
|
|
1036
|
+
}),
|
|
1037
|
+
);
|
|
1038
|
+
};
|
|
1039
|
+
|
|
1040
|
+
const tryReadArchiveJson = (jsonFile) => {
|
|
1041
|
+
try {
|
|
1042
|
+
return readArchiveJson(jsonFile);
|
|
1043
|
+
} catch (_err) {
|
|
1044
|
+
return undefined;
|
|
1045
|
+
}
|
|
1046
|
+
};
|
|
1047
|
+
|
|
1048
|
+
const digestToBlobPath = (digest) => {
|
|
1049
|
+
if (!digest?.startsWith("sha256:")) {
|
|
1050
|
+
return undefined;
|
|
1051
|
+
}
|
|
1052
|
+
return join("blobs", "sha256", digest.replace("sha256:", ""));
|
|
1053
|
+
};
|
|
1054
|
+
|
|
1055
|
+
const archiveBlobPath = (tempDir, digest) => {
|
|
1056
|
+
const blobPath = digestToBlobPath(digest);
|
|
1057
|
+
return blobPath ? join(tempDir, blobPath) : undefined;
|
|
1058
|
+
};
|
|
1059
|
+
|
|
1060
|
+
const toManifestEntry = (manifestBlob) => {
|
|
1061
|
+
const configBlob = digestToBlobPath(manifestBlob?.config?.digest);
|
|
1062
|
+
const layers =
|
|
1063
|
+
manifestBlob?.layers
|
|
1064
|
+
?.map((layer) => digestToBlobPath(layer?.digest))
|
|
1065
|
+
.filter(Boolean) || [];
|
|
1066
|
+
if (!configBlob && !layers.length) {
|
|
1067
|
+
return undefined;
|
|
1068
|
+
}
|
|
1069
|
+
return {
|
|
1070
|
+
Config: configBlob,
|
|
1071
|
+
Layers: layers,
|
|
1072
|
+
};
|
|
1073
|
+
};
|
|
1074
|
+
|
|
1075
|
+
const resolveArchiveManifest = (manifestData, tempDir) => {
|
|
1076
|
+
if (Array.isArray(manifestData)) {
|
|
1077
|
+
return manifestData;
|
|
1078
|
+
}
|
|
1079
|
+
if (!manifestData || typeof manifestData !== "object") {
|
|
1080
|
+
return [];
|
|
1081
|
+
}
|
|
1082
|
+
if (Array.isArray(manifestData.manifests)) {
|
|
1083
|
+
const resolvedManifests = manifestData.manifests
|
|
1084
|
+
.map((manifestEntry) => {
|
|
1085
|
+
if (manifestEntry?.Layers?.length || manifestEntry?.Config) {
|
|
1086
|
+
return manifestEntry;
|
|
1087
|
+
}
|
|
1088
|
+
const manifestBlob = tryReadArchiveJson(
|
|
1089
|
+
archiveBlobPath(tempDir, manifestEntry?.digest),
|
|
1090
|
+
);
|
|
1091
|
+
const resolvedEntry = toManifestEntry(manifestBlob);
|
|
1092
|
+
return resolvedEntry
|
|
1093
|
+
? {
|
|
1094
|
+
...manifestEntry,
|
|
1095
|
+
...resolvedEntry,
|
|
1096
|
+
}
|
|
1097
|
+
: manifestEntry;
|
|
1098
|
+
})
|
|
1099
|
+
.filter(Boolean);
|
|
1100
|
+
return resolvedManifests.length
|
|
1101
|
+
? resolvedManifests
|
|
1102
|
+
: manifestData.manifests;
|
|
1103
|
+
}
|
|
1104
|
+
const manifestEntry = toManifestEntry(manifestData);
|
|
1105
|
+
return manifestEntry ? [manifestEntry] : [];
|
|
1106
|
+
};
|
|
1107
|
+
|
|
1108
|
+
const discoverManifestFromBlobs = (tempDir) => {
|
|
1109
|
+
const blobsDir = join(tempDir, "blobs", "sha256");
|
|
1110
|
+
if (!safeExistsSync(blobsDir)) {
|
|
1111
|
+
return undefined;
|
|
1112
|
+
}
|
|
1113
|
+
const blobFiles = readdirSync(blobsDir);
|
|
1114
|
+
for (const blobFile of blobFiles) {
|
|
1115
|
+
const manifestBlob = tryReadArchiveJson(join(blobsDir, blobFile));
|
|
1116
|
+
const manifestEntry = toManifestEntry(manifestBlob);
|
|
1117
|
+
if (manifestEntry?.Layers?.length || manifestEntry?.Config) {
|
|
1118
|
+
return [manifestEntry];
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
return undefined;
|
|
1122
|
+
};
|
|
1123
|
+
|
|
1020
1124
|
/**
|
|
1021
1125
|
* Method to export a container image archive.
|
|
1022
1126
|
* Returns the location of the layers with additional packages related metadata
|
|
@@ -1032,10 +1136,46 @@ export const exportArchive = async (fullImageName, options = {}) => {
|
|
|
1032
1136
|
const blobsDir = join(tempDir, "blobs", "sha256");
|
|
1033
1137
|
safeMkdirSync(allLayersExplodedDir);
|
|
1034
1138
|
const manifestFile = join(tempDir, "manifest.json");
|
|
1139
|
+
const manifestIndexFile = join(tempDir, "index.json");
|
|
1140
|
+
const synthesizedManifestFile = join(tempDir, "synthetic-manifest.json");
|
|
1035
1141
|
try {
|
|
1036
1142
|
await extractTar(fullImageName, tempDir, options);
|
|
1143
|
+
if (safeExistsSync(manifestFile)) {
|
|
1144
|
+
// docker archive manifest file
|
|
1145
|
+
return await extractFromManifest(
|
|
1146
|
+
manifestFile,
|
|
1147
|
+
{},
|
|
1148
|
+
tempDir,
|
|
1149
|
+
allLayersExplodedDir,
|
|
1150
|
+
options,
|
|
1151
|
+
);
|
|
1152
|
+
}
|
|
1153
|
+
if (safeExistsSync(manifestIndexFile)) {
|
|
1154
|
+
return await extractFromManifest(
|
|
1155
|
+
manifestIndexFile,
|
|
1156
|
+
{},
|
|
1157
|
+
tempDir,
|
|
1158
|
+
allLayersExplodedDir,
|
|
1159
|
+
options,
|
|
1160
|
+
);
|
|
1161
|
+
}
|
|
1037
1162
|
// podman use blobs dir
|
|
1038
1163
|
if (safeExistsSync(blobsDir)) {
|
|
1164
|
+
const discoveredManifest = discoverManifestFromBlobs(tempDir);
|
|
1165
|
+
if (discoveredManifest?.length) {
|
|
1166
|
+
writeFileSync(
|
|
1167
|
+
synthesizedManifestFile,
|
|
1168
|
+
JSON.stringify(discoveredManifest),
|
|
1169
|
+
"utf-8",
|
|
1170
|
+
);
|
|
1171
|
+
return await extractFromManifest(
|
|
1172
|
+
synthesizedManifestFile,
|
|
1173
|
+
{},
|
|
1174
|
+
tempDir,
|
|
1175
|
+
allLayersExplodedDir,
|
|
1176
|
+
options,
|
|
1177
|
+
);
|
|
1178
|
+
}
|
|
1039
1179
|
if (DEBUG_MODE) {
|
|
1040
1180
|
console.log(
|
|
1041
1181
|
`Image archive ${fullImageName} successfully exported to directory ${tempDir}`,
|
|
@@ -1061,16 +1201,6 @@ export const exportArchive = async (fullImageName, options = {}) => {
|
|
|
1061
1201
|
exportData.pkgPathList = getPkgPathList(exportData, lastWorkingDir);
|
|
1062
1202
|
return exportData;
|
|
1063
1203
|
}
|
|
1064
|
-
if (safeExistsSync(manifestFile)) {
|
|
1065
|
-
// docker manifest file
|
|
1066
|
-
return await extractFromManifest(
|
|
1067
|
-
manifestFile,
|
|
1068
|
-
{},
|
|
1069
|
-
tempDir,
|
|
1070
|
-
allLayersExplodedDir,
|
|
1071
|
-
options,
|
|
1072
|
-
);
|
|
1073
|
-
}
|
|
1074
1204
|
console.log(`Unable to extract image archive to ${tempDir}`);
|
|
1075
1205
|
options.failOnError && process.exit(1);
|
|
1076
1206
|
} catch (_err) {
|
|
@@ -1105,28 +1235,23 @@ export const extractFromManifest = async (
|
|
|
1105
1235
|
// Example of manifests
|
|
1106
1236
|
// [{"Config":"blobs/sha256/dedc100afa8d6718f5ac537730dd4a5ceea3563e695c90f1a8ac6df32c4cb291","RepoTags":["shiftleft/core:latest"],"Layers":["blobs/sha256/eaead16dc43bb8811d4ff450935d607f9ba4baffda4fc110cc402fa43f601d83","blobs/sha256/2039af03c0e17a3025b989335e9414149577fa09e7d0dcbee80155333639d11f"]}]
|
|
1107
1237
|
// {"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.docker.distribution.manifest.list.v2+json","digest":"sha256:7706ac20c7587081dc7a00e0ec65a6633b0bb3788e0048a3e971d3eae492db63","size":318,"annotations":{"io.containerd.image.name":"docker.io/shiftleft/scan-slim:latest","org.opencontainers.image.ref.name":"latest"}}]}
|
|
1108
|
-
let manifest =
|
|
1109
|
-
readFileSync(manifestFile, {
|
|
1110
|
-
encoding: "utf-8",
|
|
1111
|
-
}),
|
|
1112
|
-
);
|
|
1238
|
+
let manifest = readArchiveJson(manifestFile);
|
|
1113
1239
|
let lastLayerConfig = {};
|
|
1114
1240
|
let lastLayerConfigFile = "";
|
|
1241
|
+
let selectedManifest;
|
|
1115
1242
|
let lastWorkingDir = "";
|
|
1116
|
-
|
|
1117
|
-
if (Object.keys(manifest).length !== 0 && manifest.manifests) {
|
|
1118
|
-
manifest = manifest.manifests;
|
|
1119
|
-
}
|
|
1243
|
+
manifest = resolveArchiveManifest(manifest, tempDir);
|
|
1120
1244
|
if (Array.isArray(manifest)) {
|
|
1245
|
+
selectedManifest = manifest[manifest.length - 1];
|
|
1121
1246
|
if (manifest.length !== 1) {
|
|
1122
1247
|
if (DEBUG_MODE) {
|
|
1123
1248
|
console.log(
|
|
1124
1249
|
"Multiple image tags was downloaded. Only the last one would be used",
|
|
1125
1250
|
);
|
|
1126
|
-
console.log(
|
|
1251
|
+
console.log(selectedManifest);
|
|
1127
1252
|
}
|
|
1128
1253
|
}
|
|
1129
|
-
const layers =
|
|
1254
|
+
const layers = selectedManifest?.Layers || [];
|
|
1130
1255
|
if (!layers.length && safeExistsSync(tempDir)) {
|
|
1131
1256
|
const blobFiles = readdirSync(join(tempDir, "blobs", "sha256"));
|
|
1132
1257
|
if (blobFiles?.length) {
|
|
@@ -1164,10 +1289,10 @@ export const extractFromManifest = async (
|
|
|
1164
1289
|
}
|
|
1165
1290
|
}
|
|
1166
1291
|
}
|
|
1167
|
-
if (
|
|
1168
|
-
lastLayerConfigFile = join(tempDir,
|
|
1292
|
+
if (selectedManifest?.Config) {
|
|
1293
|
+
lastLayerConfigFile = join(tempDir, selectedManifest.Config);
|
|
1169
1294
|
}
|
|
1170
|
-
if (lastLayer
|
|
1295
|
+
if (!lastLayerConfigFile && lastLayer?.includes("layer.tar")) {
|
|
1171
1296
|
lastLayerConfigFile = join(
|
|
1172
1297
|
tempDir,
|
|
1173
1298
|
lastLayer.replace("layer.tar", "json"),
|
|
@@ -1188,9 +1313,17 @@ export const extractFromManifest = async (
|
|
|
1188
1313
|
}
|
|
1189
1314
|
}
|
|
1190
1315
|
}
|
|
1191
|
-
const
|
|
1316
|
+
const inspectData = localData?.Config
|
|
1317
|
+
? localData
|
|
1318
|
+
: lastLayerConfig?.config
|
|
1319
|
+
? {
|
|
1320
|
+
...localData,
|
|
1321
|
+
Config: lastLayerConfig.config,
|
|
1322
|
+
}
|
|
1323
|
+
: localData;
|
|
1324
|
+
const binPaths = extractPathEnv(inspectData?.Config?.Env);
|
|
1192
1325
|
const exportData = {
|
|
1193
|
-
inspectData
|
|
1326
|
+
inspectData,
|
|
1194
1327
|
manifest,
|
|
1195
1328
|
allLayersDir: tempDir,
|
|
1196
1329
|
allLayersExplodedDir,
|
|
@@ -1233,13 +1366,7 @@ export const exportImage = async (fullImageName, options) => {
|
|
|
1233
1366
|
// On Windows or on mac with Rancher Desktop, fallback to invoking cli
|
|
1234
1367
|
if (needsCliFallback()) {
|
|
1235
1368
|
const imageTarFile = join(tempDir, "image.tar");
|
|
1236
|
-
|
|
1237
|
-
if (!process.env.DOCKER_CMD) {
|
|
1238
|
-
detectRancherDesktop() || detectColima();
|
|
1239
|
-
if (isNerdctl) {
|
|
1240
|
-
dockerCmd = "nerdctl";
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1369
|
+
const dockerCmd = getContainerCliCmd();
|
|
1243
1370
|
console.log(
|
|
1244
1371
|
`About to export image ${fullImageName} to ${imageTarFile} using ${dockerCmd} cli`,
|
|
1245
1372
|
);
|