@cyclonedx/cdxgen 12.2.1 → 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.
Files changed (170) hide show
  1. package/README.md +239 -90
  2. package/bin/audit.js +191 -0
  3. package/bin/cdxgen.js +513 -167
  4. package/bin/convert.js +99 -0
  5. package/bin/evinse.js +23 -0
  6. package/bin/repl.js +339 -8
  7. package/bin/sign.js +8 -0
  8. package/bin/validate.js +8 -0
  9. package/bin/verify.js +8 -0
  10. package/data/container-knowledge-index.json +125 -0
  11. package/data/gtfobins-index.json +6296 -0
  12. package/data/lolbas-index.json +150 -0
  13. package/data/queries-darwin.json +63 -3
  14. package/data/queries-win.json +45 -3
  15. package/data/queries.json +74 -2
  16. package/data/rules/chrome-extensions.yaml +240 -0
  17. package/data/rules/ci-permissions.yaml +478 -18
  18. package/data/rules/container-risk.yaml +270 -0
  19. package/data/rules/obom-runtime.yaml +891 -0
  20. package/data/rules/package-integrity.yaml +49 -0
  21. package/data/spdx-export.schema.json +6794 -0
  22. package/data/spdx-model-v3.0.1.jsonld +15999 -0
  23. package/lib/audit/index.js +1924 -0
  24. package/lib/audit/index.poku.js +1488 -0
  25. package/lib/audit/progress.js +137 -0
  26. package/lib/audit/progress.poku.js +188 -0
  27. package/lib/audit/reporters.js +618 -0
  28. package/lib/audit/scoring.js +310 -0
  29. package/lib/audit/scoring.poku.js +341 -0
  30. package/lib/audit/targets.js +260 -0
  31. package/lib/audit/targets.poku.js +331 -0
  32. package/lib/cli/index.js +154 -11
  33. package/lib/cli/index.poku.js +251 -0
  34. package/lib/helpers/analyzer.js +446 -2
  35. package/lib/helpers/analyzer.poku.js +72 -1
  36. package/lib/helpers/annotationFormatter.js +49 -0
  37. package/lib/helpers/annotationFormatter.poku.js +44 -0
  38. package/lib/helpers/bomUtils.js +36 -0
  39. package/lib/helpers/bomUtils.poku.js +51 -0
  40. package/lib/helpers/caxa.js +2 -2
  41. package/lib/helpers/chromextutils.js +1153 -0
  42. package/lib/helpers/chromextutils.poku.js +493 -0
  43. package/lib/helpers/ciParsers/githubActions.js +1632 -45
  44. package/lib/helpers/ciParsers/githubActions.poku.js +853 -1
  45. package/lib/helpers/containerRisk.js +186 -0
  46. package/lib/helpers/containerRisk.poku.js +52 -0
  47. package/lib/helpers/display.js +241 -59
  48. package/lib/helpers/display.poku.js +162 -2
  49. package/lib/helpers/exportUtils.js +123 -0
  50. package/lib/helpers/exportUtils.poku.js +60 -0
  51. package/lib/helpers/formulationParsers.js +69 -0
  52. package/lib/helpers/formulationParsers.poku.js +44 -0
  53. package/lib/helpers/gtfobins.js +189 -0
  54. package/lib/helpers/gtfobins.poku.js +49 -0
  55. package/lib/helpers/lolbas.js +267 -0
  56. package/lib/helpers/lolbas.poku.js +39 -0
  57. package/lib/helpers/osqueryTransform.js +84 -0
  58. package/lib/helpers/osqueryTransform.poku.js +49 -0
  59. package/lib/helpers/provenanceUtils.js +193 -0
  60. package/lib/helpers/provenanceUtils.poku.js +145 -0
  61. package/lib/helpers/pylockutils.js +281 -0
  62. package/lib/helpers/pylockutils.poku.js +48 -0
  63. package/lib/helpers/registryProvenance.js +793 -0
  64. package/lib/helpers/registryProvenance.poku.js +452 -0
  65. package/lib/helpers/source.js +1267 -0
  66. package/lib/helpers/source.poku.js +771 -0
  67. package/lib/helpers/spdxUtils.js +97 -0
  68. package/lib/helpers/spdxUtils.poku.js +70 -0
  69. package/lib/helpers/unicodeScan.js +147 -0
  70. package/lib/helpers/unicodeScan.poku.js +45 -0
  71. package/lib/helpers/utils.js +700 -128
  72. package/lib/helpers/utils.poku.js +877 -80
  73. package/lib/managers/binary.js +29 -5
  74. package/lib/managers/docker.js +179 -52
  75. package/lib/managers/docker.poku.js +327 -28
  76. package/lib/managers/oci.js +107 -23
  77. package/lib/managers/oci.poku.js +132 -0
  78. package/lib/server/openapi.yaml +17 -0
  79. package/lib/server/server.js +225 -336
  80. package/lib/server/server.poku.js +16 -10
  81. package/lib/stages/postgen/annotator.js +7 -0
  82. package/lib/stages/postgen/annotator.poku.js +40 -0
  83. package/lib/stages/postgen/auditBom.js +19 -3
  84. package/lib/stages/postgen/auditBom.poku.js +1729 -67
  85. package/lib/stages/postgen/postgen.js +40 -0
  86. package/lib/stages/postgen/postgen.poku.js +47 -0
  87. package/lib/stages/postgen/ruleEngine.js +80 -2
  88. package/lib/stages/postgen/spdxConverter.js +796 -0
  89. package/lib/stages/postgen/spdxConverter.poku.js +341 -0
  90. package/lib/validator/bomValidator.js +232 -0
  91. package/lib/validator/bomValidator.poku.js +70 -0
  92. package/lib/validator/complianceRules.js +70 -7
  93. package/lib/validator/complianceRules.poku.js +30 -0
  94. package/lib/validator/reporters/annotations.js +2 -2
  95. package/lib/validator/reporters/console.js +11 -0
  96. package/lib/validator/reporters.poku.js +13 -0
  97. package/package.json +10 -7
  98. package/types/bin/audit.d.ts +3 -0
  99. package/types/bin/audit.d.ts.map +1 -0
  100. package/types/bin/convert.d.ts +3 -0
  101. package/types/bin/convert.d.ts.map +1 -0
  102. package/types/bin/repl.d.ts.map +1 -1
  103. package/types/lib/audit/index.d.ts +115 -0
  104. package/types/lib/audit/index.d.ts.map +1 -0
  105. package/types/lib/audit/progress.d.ts +27 -0
  106. package/types/lib/audit/progress.d.ts.map +1 -0
  107. package/types/lib/audit/reporters.d.ts +35 -0
  108. package/types/lib/audit/reporters.d.ts.map +1 -0
  109. package/types/lib/audit/scoring.d.ts +35 -0
  110. package/types/lib/audit/scoring.d.ts.map +1 -0
  111. package/types/lib/audit/targets.d.ts +63 -0
  112. package/types/lib/audit/targets.d.ts.map +1 -0
  113. package/types/lib/cli/index.d.ts +8 -0
  114. package/types/lib/cli/index.d.ts.map +1 -1
  115. package/types/lib/helpers/analyzer.d.ts +13 -0
  116. package/types/lib/helpers/analyzer.d.ts.map +1 -1
  117. package/types/lib/helpers/annotationFormatter.d.ts +23 -0
  118. package/types/lib/helpers/annotationFormatter.d.ts.map +1 -0
  119. package/types/lib/helpers/bomUtils.d.ts +5 -0
  120. package/types/lib/helpers/bomUtils.d.ts.map +1 -0
  121. package/types/lib/helpers/chromextutils.d.ts +97 -0
  122. package/types/lib/helpers/chromextutils.d.ts.map +1 -0
  123. package/types/lib/helpers/ciParsers/githubActions.d.ts +3 -8
  124. package/types/lib/helpers/ciParsers/githubActions.d.ts.map +1 -1
  125. package/types/lib/helpers/containerRisk.d.ts +17 -0
  126. package/types/lib/helpers/containerRisk.d.ts.map +1 -0
  127. package/types/lib/helpers/display.d.ts +4 -1
  128. package/types/lib/helpers/display.d.ts.map +1 -1
  129. package/types/lib/helpers/exportUtils.d.ts +40 -0
  130. package/types/lib/helpers/exportUtils.d.ts.map +1 -0
  131. package/types/lib/helpers/formulationParsers.d.ts.map +1 -1
  132. package/types/lib/helpers/gtfobins.d.ts +17 -0
  133. package/types/lib/helpers/gtfobins.d.ts.map +1 -0
  134. package/types/lib/helpers/lolbas.d.ts +16 -0
  135. package/types/lib/helpers/lolbas.d.ts.map +1 -0
  136. package/types/lib/helpers/osqueryTransform.d.ts +7 -0
  137. package/types/lib/helpers/osqueryTransform.d.ts.map +1 -0
  138. package/types/lib/helpers/provenanceUtils.d.ts +90 -0
  139. package/types/lib/helpers/provenanceUtils.d.ts.map +1 -0
  140. package/types/lib/helpers/pylockutils.d.ts +51 -0
  141. package/types/lib/helpers/pylockutils.d.ts.map +1 -0
  142. package/types/lib/helpers/registryProvenance.d.ts +17 -0
  143. package/types/lib/helpers/registryProvenance.d.ts.map +1 -0
  144. package/types/lib/helpers/source.d.ts +141 -0
  145. package/types/lib/helpers/source.d.ts.map +1 -0
  146. package/types/lib/helpers/spdxUtils.d.ts +2 -0
  147. package/types/lib/helpers/spdxUtils.d.ts.map +1 -0
  148. package/types/lib/helpers/unicodeScan.d.ts +46 -0
  149. package/types/lib/helpers/unicodeScan.d.ts.map +1 -0
  150. package/types/lib/helpers/utils.d.ts +29 -11
  151. package/types/lib/helpers/utils.d.ts.map +1 -1
  152. package/types/lib/managers/binary.d.ts.map +1 -1
  153. package/types/lib/managers/docker.d.ts.map +1 -1
  154. package/types/lib/managers/oci.d.ts.map +1 -1
  155. package/types/lib/server/server.d.ts +0 -36
  156. package/types/lib/server/server.d.ts.map +1 -1
  157. package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
  158. package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
  159. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  160. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  161. package/types/lib/stages/postgen/spdxConverter.d.ts +11 -0
  162. package/types/lib/stages/postgen/spdxConverter.d.ts.map +1 -0
  163. package/types/lib/validator/bomValidator.d.ts +1 -0
  164. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  165. package/types/lib/validator/complianceRules.d.ts.map +1 -1
  166. package/types/lib/validator/reporters/console.d.ts.map +1 -1
  167. package/types/bin/dependencies.d.ts +0 -3
  168. package/types/bin/dependencies.d.ts.map +0 -1
  169. package/types/bin/licenses.d.ts +0 -3
  170. package/types/bin/licenses.d.ts.map +0 -1
@@ -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 && DEBUG_MODE) {
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 = binPaths
466
- .concat(rootBinPaths)
467
- .concat(usrBinPaths)
468
- .map((f) => relative(src, f));
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",
@@ -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
- let dockerCmd = process.env.DOCKER_CMD || "docker";
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
- return localData;
701
- }
702
- try {
703
- const stdout = result.stdout;
704
- if (stdout) {
705
- const inspectData = JSON.parse(Buffer.from(stdout).toString());
706
- if (inspectData && Array.isArray(inspectData)) {
707
- return inspectData[0];
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
- return inspectData;
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 = JSON.parse(
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
- // Extract the manifest for the new containerd syntax
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(manifest[manifest.length - 1]);
1251
+ console.log(selectedManifest);
1127
1252
  }
1128
1253
  }
1129
- const layers = manifest[manifest.length - 1]["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 (manifest.Config) {
1168
- lastLayerConfigFile = join(tempDir, manifest.Config);
1292
+ if (selectedManifest?.Config) {
1293
+ lastLayerConfigFile = join(tempDir, selectedManifest.Config);
1169
1294
  }
1170
- if (lastLayer.includes("layer.tar")) {
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 binPaths = extractPathEnv(localData?.Config?.Env);
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: localData,
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
- let dockerCmd = process.env.DOCKER_CMD || "docker";
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
  );