@cyclonedx/cdxgen 10.0.0 → 10.0.1

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 (4) hide show
  1. package/docker.js +40 -6
  2. package/index.js +33 -25
  3. package/package.json +1 -1
  4. package/utils.js +66 -34
package/docker.js CHANGED
@@ -684,7 +684,7 @@ export const extractTar = async (fullImageName, dir) => {
684
684
  preserveOwner: false,
685
685
  noMtime: true,
686
686
  noChmod: true,
687
- strict: true,
687
+ strict: false,
688
688
  C: dir,
689
689
  portable: true,
690
690
  onwarn: () => {},
@@ -696,6 +696,9 @@ export const extractTar = async (fullImageName, dir) => {
696
696
  path.includes("etc/") ||
697
697
  path.includes("logs/") ||
698
698
  path.includes("dev/") ||
699
+ path.includes("usr/share/zoneinfo/") ||
700
+ path.includes("usr/share/doc/") ||
701
+ path.includes("usr/share/i18n/") ||
699
702
  [
700
703
  "BlockDevice",
701
704
  "CharacterDevice",
@@ -727,6 +730,12 @@ export const extractTar = async (fullImageName, dir) => {
727
730
  console.log("------------");
728
731
  console.log(err);
729
732
  console.log("------------");
733
+ } else if (err.code === "TAR_BAD_ARCHIVE") {
734
+ if (DEBUG_MODE) {
735
+ console.log(`Archive ${fullImageName} is empty. Skipping.`);
736
+ }
737
+ } else {
738
+ console.log(err);
730
739
  }
731
740
  return false;
732
741
  }
@@ -832,13 +841,30 @@ export const extractFromManifest = async (
832
841
  }
833
842
  const lastLayer = layers[layers.length - 1];
834
843
  for (const layer of layers) {
844
+ try {
845
+ if (!lstatSync(join(tempDir, layer)).isFile()) {
846
+ console.log(
847
+ `Skipping layer ${layer} since it is not a readable file.`
848
+ );
849
+ continue;
850
+ }
851
+ } catch (e) {
852
+ console.log(`Skipping layer ${layer} since it is not a readable file.`);
853
+ continue;
854
+ }
835
855
  if (DEBUG_MODE) {
836
856
  console.log(`Extracting layer ${layer} to ${allLayersExplodedDir}`);
837
857
  }
838
858
  try {
839
859
  await extractTar(join(tempDir, layer), allLayersExplodedDir);
840
860
  } catch (err) {
841
- console.log(err);
861
+ if (err.code === "TAR_BAD_ARCHIVE") {
862
+ if (DEBUG_MODE) {
863
+ console.log(`Layer ${layer} is empty.`);
864
+ }
865
+ } else {
866
+ console.log(err);
867
+ }
842
868
  }
843
869
  }
844
870
  if (manifest.Config) {
@@ -1058,15 +1084,23 @@ export const getPkgPathList = (exportData, lastWorkingDir) => {
1058
1084
  }
1059
1085
  }
1060
1086
  if (lastWorkingDir && lastWorkingDir !== "") {
1061
- knownSysPaths.push(lastWorkingDir);
1087
+ if (
1088
+ !lastWorkingDir.includes("/opt/") &&
1089
+ !lastWorkingDir.includes("/home/")
1090
+ ) {
1091
+ knownSysPaths.push(lastWorkingDir);
1092
+ }
1062
1093
  // Some more common app dirs
1063
- if (!lastWorkingDir.startsWith("/app")) {
1094
+ if (!lastWorkingDir.includes("/app/")) {
1064
1095
  knownSysPaths.push(join(allLayersExplodedDir, "/app"));
1065
1096
  }
1066
- if (!lastWorkingDir.startsWith("/data")) {
1097
+ if (!lastWorkingDir.includes("/layers/")) {
1098
+ knownSysPaths.push(join(allLayersExplodedDir, "/layers"));
1099
+ }
1100
+ if (!lastWorkingDir.includes("/data/")) {
1067
1101
  knownSysPaths.push(join(allLayersExplodedDir, "/data"));
1068
1102
  }
1069
- if (!lastWorkingDir.startsWith("/srv")) {
1103
+ if (!lastWorkingDir.includes("/srv/")) {
1070
1104
  knownSysPaths.push(join(allLayersExplodedDir, "/srv"));
1071
1105
  }
1072
1106
  }
package/index.js CHANGED
@@ -948,39 +948,42 @@ export const createJarBom = async (path, options) => {
948
948
  false,
949
949
  true
950
950
  );
951
+ }
952
+ if (path.endsWith(".jar")) {
953
+ jarFiles = [resolve(path)];
951
954
  } else {
952
955
  jarFiles = getAllFiles(
953
956
  path,
954
957
  (options.multiProject ? "**/" : "") + "*.[jw]ar",
955
958
  options
956
959
  );
957
- // Jenkins plugins
958
- const hpiFiles = getAllFiles(
959
- path,
960
- (options.multiProject ? "**/" : "") + "*.hpi",
961
- options
962
- );
963
- if (hpiFiles.length) {
964
- jarFiles = jarFiles.concat(hpiFiles);
960
+ }
961
+ // Jenkins plugins
962
+ const hpiFiles = getAllFiles(
963
+ path,
964
+ (options.multiProject ? "**/" : "") + "*.hpi",
965
+ options
966
+ );
967
+ if (hpiFiles.length) {
968
+ jarFiles = jarFiles.concat(hpiFiles);
969
+ }
970
+ const tempDir = mkdtempSync(join(tmpdir(), "jar-deps-"));
971
+ for (const jar of jarFiles) {
972
+ if (DEBUG_MODE) {
973
+ console.log(`Parsing ${jar}`);
965
974
  }
966
- const tempDir = mkdtempSync(join(tmpdir(), "jar-deps-"));
967
- for (const jar of jarFiles) {
968
- if (DEBUG_MODE) {
969
- console.log(`Parsing ${jar}`);
970
- }
971
- const dlist = await extractJarArchive(jar, tempDir);
972
- if (dlist && dlist.length) {
973
- pkgList = pkgList.concat(dlist);
974
- }
975
- if (pkgList.length) {
976
- pkgList = await getMvnMetadata(pkgList);
977
- }
975
+ const dlist = await extractJarArchive(jar, tempDir);
976
+ if (dlist && dlist.length) {
977
+ pkgList = pkgList.concat(dlist);
978
978
  }
979
- // Clean up
980
- if (tempDir && tempDir.startsWith(tmpdir()) && rmSync) {
981
- rmSync(tempDir, { recursive: true, force: true });
979
+ if (pkgList.length) {
980
+ pkgList = await getMvnMetadata(pkgList);
982
981
  }
983
982
  }
983
+ // Clean up
984
+ if (tempDir && tempDir.startsWith(tmpdir()) && rmSync) {
985
+ rmSync(tempDir, { recursive: true, force: true });
986
+ }
984
987
  pkgList = pkgList.concat(convertJarNSToPackages(nsMapping));
985
988
  return buildBomNSData(options, pkgList, "maven", {
986
989
  src: path,
@@ -1002,7 +1005,7 @@ export const createJavaBom = async (path, options) => {
1002
1005
  // This is subsequently referred to in the dependencies list
1003
1006
  let parentComponent = {};
1004
1007
  // war/ear mode
1005
- if (path.endsWith(".war")) {
1008
+ if (path.endsWith(".war") || path.endsWith(".jar")) {
1006
1009
  // Check if the file exists
1007
1010
  if (existsSync(path)) {
1008
1011
  if (DEBUG_MODE) {
@@ -4800,7 +4803,12 @@ export const createMultiXBom = async (pathList, options) => {
4800
4803
  }
4801
4804
  }
4802
4805
  } // for
4803
- if (options.lastWorkingDir && options.lastWorkingDir !== "") {
4806
+ if (
4807
+ options.lastWorkingDir &&
4808
+ options.lastWorkingDir !== "" &&
4809
+ !options.lastWorkingDir.includes("/opt/") &&
4810
+ !options.lastWorkingDir.includes("/home/")
4811
+ ) {
4804
4812
  bomData = await createJarBom(options.lastWorkingDir, options);
4805
4813
  if (
4806
4814
  bomData &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyclonedx/cdxgen",
3
- "version": "10.0.0",
3
+ "version": "10.0.1",
4
4
  "description": "Creates CycloneDX Software Bill of Materials (SBOM) from source or container image",
5
5
  "homepage": "http://github.com/cyclonedx/cdxgen",
6
6
  "author": "Prabhu Subramanian <prabhu@appthreat.com>",
package/utils.js CHANGED
@@ -7026,7 +7026,7 @@ export const extractJarArchive = async function (
7026
7026
  existsSync(manifestname)
7027
7027
  ) {
7028
7028
  tempDir = dirname(jarFile);
7029
- } else if (!existsSync(join(tempDir, fname))) {
7029
+ } else if (!existsSync(join(tempDir, fname)) && lstatSync(jarFile).isFile()) {
7030
7030
  // Only copy if the file doesn't exist
7031
7031
  copyFileSync(jarFile, join(tempDir, fname), constants.COPYFILE_FICLONE);
7032
7032
  }
@@ -7040,29 +7040,47 @@ export const extractJarArchive = async function (
7040
7040
  "bin"
7041
7041
  )}`;
7042
7042
  }
7043
- if (jarFile.endsWith(".war") || jarFile.endsWith(".hpi")) {
7044
- const jarResult = spawnSync("jar", ["-xf", join(tempDir, fname)], {
7045
- encoding: "utf-8",
7046
- cwd: tempDir,
7047
- shell: isWin,
7048
- env
7049
- });
7050
- if (jarResult.status !== 0) {
7051
- console.error(jarResult.stdout, jarResult.stderr);
7052
- console.log(
7053
- "Check if JRE is installed and the jar command is available in the PATH."
7054
- );
7043
+ if (
7044
+ jarFile.endsWith(".war") ||
7045
+ jarFile.endsWith(".hpi") ||
7046
+ jarFile.endsWith(".jar")
7047
+ ) {
7048
+ try {
7049
+ const zip = new StreamZip.async({ file: join(tempDir, fname) });
7050
+ await zip.extract(null, tempDir);
7051
+ await zip.close();
7052
+ } catch (e) {
7053
+ console.log(`Unable to extract ${join(tempDir, fname)}. Skipping.`);
7055
7054
  return pkgList;
7056
7055
  }
7057
7056
  jarFiles = getAllFiles(join(tempDir, "WEB-INF", "lib"), "**/*.jar");
7058
7057
  if (jarFile.endsWith(".hpi")) {
7059
7058
  jarFiles.push(jarFile);
7060
7059
  }
7060
+ // Some jar files could also have more jar files inside BOOT-INF directory
7061
+ const jarFiles2 = getAllFiles(join(tempDir, "BOOT-INF", "lib"), "**/*.jar");
7062
+ if (jarFiles && jarFiles2.length) {
7063
+ jarFiles = jarFiles.concat(jarFiles2);
7064
+ }
7065
+ // Fallback. If our jar file didn't include any jar
7066
+ if (jarFile.endsWith(".jar") && !jarFiles.length) {
7067
+ jarFiles = [join(tempDir, fname)];
7068
+ }
7061
7069
  } else {
7062
7070
  jarFiles = [join(tempDir, fname)];
7063
7071
  }
7072
+ if (DEBUG_MODE) {
7073
+ console.log(`List of jars: ${jarFiles}`);
7074
+ }
7064
7075
  if (jarFiles && jarFiles.length) {
7065
7076
  for (const jf of jarFiles) {
7077
+ // If the jar file doesn't exist at the point of use, skip it
7078
+ if (!existsSync(jf)) {
7079
+ if (DEBUG_MODE) {
7080
+ console.log(jf, "is not a readable file");
7081
+ }
7082
+ continue;
7083
+ }
7066
7084
  pomname = jf.replace(".jar", ".pom");
7067
7085
  const jarname = basename(jf);
7068
7086
  // Ignore test jars
@@ -7070,6 +7088,9 @@ export const extractJarArchive = async function (
7070
7088
  jarname.endsWith("-tests.jar") ||
7071
7089
  jarname.endsWith("-test-sources.jar")
7072
7090
  ) {
7091
+ if (DEBUG_MODE) {
7092
+ console.log(`Skipping tests jar ${jarname}`);
7093
+ }
7073
7094
  continue;
7074
7095
  }
7075
7096
  const manifestDir = join(tempDir, "META-INF");
@@ -7081,16 +7102,20 @@ export const extractJarArchive = async function (
7081
7102
  if (existsSync(pomname)) {
7082
7103
  jarResult = { status: 0 };
7083
7104
  } else {
7084
- jarResult = spawnSync("jar", ["-xf", jf, "META-INF"], {
7085
- encoding: "utf-8",
7086
- cwd: tempDir,
7087
- shell: isWin,
7088
- env
7089
- });
7105
+ // Unzip natively
7106
+ try {
7107
+ const zip = new StreamZip.async({ file: jf });
7108
+ await zip.extract(null, tempDir);
7109
+ await zip.close();
7110
+ jarResult = { status: 0 };
7111
+ } catch (e) {
7112
+ if (DEBUG_MODE) {
7113
+ console.log(`Unable to extract ${jf}. Skipping.`);
7114
+ }
7115
+ jarResult = { status: 1 };
7116
+ }
7090
7117
  }
7091
- if (jarResult.status !== 0) {
7092
- console.error(jarResult.stdout, jarResult.stderr);
7093
- } else {
7118
+ if (jarResult.status === 0) {
7094
7119
  // When maven descriptor is available take group, name and version from pom.properties
7095
7120
  // META-INF/maven/${groupId}/${artifactId}/pom.properties
7096
7121
  // see https://maven.apache.org/shared/maven-archiver/index.html
@@ -7114,7 +7139,7 @@ export const extractJarArchive = async function (
7114
7139
  const res = await cdxgenAgent.get(searchurl, {
7115
7140
  responseType: "json",
7116
7141
  timeout: {
7117
- lookup: 200,
7142
+ lookup: 1000,
7118
7143
  connect: 5000,
7119
7144
  secureConnect: 5000,
7120
7145
  socket: 1000,
@@ -7132,7 +7157,11 @@ export const extractJarArchive = async function (
7132
7157
  }
7133
7158
  } catch (err) {
7134
7159
  if (err && err.message && !err.message.includes("404")) {
7135
- if (DEBUG_MODE) {
7160
+ if (err.message.includes("Timeout")) {
7161
+ console.log(
7162
+ "Maven search appears to be unavailable. Search will be skipped for all remaining packages."
7163
+ );
7164
+ } else if (DEBUG_MODE) {
7136
7165
  console.log(err);
7137
7166
  }
7138
7167
  search_maven_org_errors++;
@@ -7266,20 +7295,23 @@ export const extractJarArchive = async function (
7266
7295
  console.log(`Ignored jar ${jarname}`, name, version);
7267
7296
  }
7268
7297
  }
7269
- try {
7270
- if (rmSync && existsSync(join(tempDir, "META-INF"))) {
7271
- // Clean up META-INF
7272
- rmSync(join(tempDir, "META-INF"), {
7273
- recursive: true,
7274
- force: true
7275
- });
7276
- }
7277
- } catch (err) {
7278
- // ignore cleanup errors
7298
+ }
7299
+ try {
7300
+ if (rmSync && existsSync(join(tempDir, "META-INF"))) {
7301
+ // Clean up META-INF
7302
+ rmSync(join(tempDir, "META-INF"), {
7303
+ recursive: true,
7304
+ force: true
7305
+ });
7279
7306
  }
7307
+ } catch (err) {
7308
+ // ignore cleanup errors
7280
7309
  }
7281
7310
  } // for
7282
7311
  } // if
7312
+ if (jarFiles.length !== pkgList.length) {
7313
+ console.log(`Obtained only ${pkgList.length} from ${jarFiles.length} jars`);
7314
+ }
7283
7315
  return pkgList;
7284
7316
  };
7285
7317