@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.
@@ -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: gotHttpCache,
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 (group && p.name && p.version && !shouldFetchLicense() && !force) {
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
- pkg[aprop] = value;
6973
- return;
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
- const jarMetadata = parseJarManifest(
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 && version) {
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: jarname,
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
+ }
@@ -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(`Invalid ref in dependencies.dependsOn ${don}`);
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)) {
@@ -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
- export function getOSPackages(src) {
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
- comp.purl.startsWith("pkg:npm") ||
507
- comp.purl.startsWith("pkg:maven") ||
508
- comp.purl.startsWith("pkg:pypi") ||
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
+ }
@@ -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
- pullData = await makeRequest(
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
- const removeData = await makeRequest(
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) => {