@cyclonedx/cdxgen 9.9.7 → 9.9.9

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 CHANGED
@@ -27,7 +27,7 @@ Most SBOM tools are like barcode scanners. They can scan a few package manifest
27
27
  | Go | binary, go.mod, go.sum, Gopkg.lock | Yes except binary | Yes |
28
28
  | Ruby | Gemfile.lock, gemspec | Only for Gemfile.lock | |
29
29
  | Rust | binary, Cargo.toml, Cargo.lock | Only for Cargo.lock | |
30
- | .Net | .csproj, packages.config, project.assets.json [3], packages.lock.json, .nupkg, paket.lock | Only for project.assets.json, packages.lock.json, paket.lock | |
30
+ | .Net | .csproj, .vbproj, .fsproj, packages.config, project.assets.json [3], packages.lock.json, .nupkg, paket.lock | Only for project.assets.json, packages.lock.json, paket.lock | |
31
31
  | Dart | pubspec.lock, pubspec.yaml | Only for pubspec.lock | |
32
32
  | Haskell | cabal.project.freeze | Yes | |
33
33
  | Elixir | mix.lock | Yes | |
@@ -371,6 +371,7 @@ cdxgen can retain the dependency tree under the `dependencies` attribute for a s
371
371
  | GRADLE_DEPENDENCY_TASK | By default cdxgen use the task "dependencies" to collect packages. Set to override the task name. |
372
372
  | SBT_CACHE_DIR | Specify sbt cache directory. Useful for class name resolving |
373
373
  | FETCH_LICENSE | Set this variable to `true` or `1` to fetch license information from the registry. npm and golang |
374
+ | SEARCH_MAVEN_ORG | If maven metadata is missing in jar file, a search is performed on search.maven.org. Set to `false` or `0` to disable search. |
374
375
  | USE_GOSUM | Set to `true` or `1` to generate BOMs for golang projects using go.sum as the dependency source of truth, instead of go.mod |
375
376
  | CDXGEN_TIMEOUT_MS | Default timeout for known execution involving maven, gradle or sbt |
376
377
  | CDXGEN_SERVER_TIMEOUT_MS | Default timeout in server mode |
package/binary.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  import { join, dirname, basename } from "node:path";
10
10
  import { spawnSync } from "node:child_process";
11
11
  import { PackageURL } from "packageurl-js";
12
- import { DEBUG_MODE, findLicenseId } from "./utils.js";
12
+ import { DEBUG_MODE, TIMEOUT_MS, findLicenseId } from "./utils.js";
13
13
 
14
14
  import { fileURLToPath } from "node:url";
15
15
 
@@ -24,7 +24,7 @@ const isWin = _platform() === "win32";
24
24
  let platform = _platform();
25
25
  let extn = "";
26
26
  let pluginsBinSuffix = "";
27
- if (platform == "win32") {
27
+ if (platform === "win32") {
28
28
  platform = "windows";
29
29
  extn = ".exe";
30
30
  }
@@ -36,6 +36,9 @@ switch (arch) {
36
36
  break;
37
37
  case "x64":
38
38
  arch = "amd64";
39
+ if (platform === "windows") {
40
+ pluginsBinSuffix = "-windows-amd64";
41
+ }
39
42
  break;
40
43
  case "arm64":
41
44
  pluginsBinSuffix = "-arm64";
@@ -165,7 +168,21 @@ if (existsSync(join(CDXGEN_PLUGINS_DIR, "osquery"))) {
165
168
  } else if (process.env.OSQUERY_CMD) {
166
169
  OSQUERY_BIN = process.env.OSQUERY_CMD;
167
170
  }
171
+ let DOSAI_BIN = null;
172
+ if (existsSync(join(CDXGEN_PLUGINS_DIR, "dosai"))) {
173
+ if (platform === "darwin") {
174
+ platform = "osx";
175
+ }
176
+ DOSAI_BIN = join(
177
+ CDXGEN_PLUGINS_DIR,
178
+ "dosai",
179
+ "dosai-" + platform + "-" + arch + extn
180
+ );
181
+ } else if (process.env.DOSAI_CMD) {
182
+ DOSAI_BIN = process.env.DOSAI_CMD;
183
+ }
168
184
 
185
+ // Keep this list updated every year
169
186
  const OS_DISTRO_ALIAS = {
170
187
  "ubuntu-4.10": "warty",
171
188
  "ubuntu-5.04": "hoary",
@@ -692,3 +709,36 @@ export const executeOsQuery = (query) => {
692
709
  }
693
710
  return undefined;
694
711
  };
712
+
713
+ /**
714
+ * Method to execute dosai to create slices for dotnet
715
+ *
716
+ * @param {string} src
717
+ * @param {string} slicesFile
718
+ * @returns boolean
719
+ */
720
+ export const getDotnetSlices = (src, slicesFile) => {
721
+ if (!DOSAI_BIN) {
722
+ return false;
723
+ }
724
+ const args = ["methods", "--path", src, "--o", slicesFile];
725
+ if (DEBUG_MODE) {
726
+ console.log("Executing", DOSAI_BIN, args.join(" "));
727
+ }
728
+ const result = spawnSync(DOSAI_BIN, args, {
729
+ encoding: "utf-8",
730
+ timeout: TIMEOUT_MS,
731
+ cwd: src
732
+ });
733
+ if (result.status !== 0 || result.error) {
734
+ if (DEBUG_MODE && result.error) {
735
+ if (result.stderr) {
736
+ console.error(result.stdout, result.stderr);
737
+ } else {
738
+ console.log("Check if dosai plugin was installed successfully.");
739
+ }
740
+ }
741
+ return false;
742
+ }
743
+ return true;
744
+ };
package/evinser.js CHANGED
@@ -244,7 +244,9 @@ export const createSlice = (
244
244
  args.push(path.resolve(filePath));
245
245
  const result = executeAtom(filePath, args);
246
246
  if (!result || !fs.existsSync(slicesFile)) {
247
- console.warn(`Unable to generate ${sliceType} slice using atom.`);
247
+ console.warn(
248
+ `Unable to generate ${sliceType} slice using atom. Check if this is a supported language.`
249
+ );
248
250
  console.log(
249
251
  "Set the environment variable CDXGEN_DEBUG_MODE=debug to troubleshoot."
250
252
  );
package/index.js CHANGED
@@ -107,7 +107,8 @@ import {
107
107
  frameworksList,
108
108
  parseContainerFile,
109
109
  parseBitbucketPipelinesFile,
110
- getPyMetadata
110
+ getPyMetadata,
111
+ addEvidenceForDotnet
111
112
  } from "./utils.js";
112
113
  import { spawnSync } from "node:child_process";
113
114
  import { fileURLToPath } from "node:url";
@@ -131,7 +132,8 @@ import {
131
132
  getGoBuildInfo,
132
133
  getCargoAuditableInfo,
133
134
  executeOsQuery,
134
- getOSPackages
135
+ getOSPackages,
136
+ getDotnetSlices
135
137
  } from "./binary.js";
136
138
 
137
139
  const isWin = _platform() === "win32";
@@ -1083,7 +1085,7 @@ export const createJarBom = async (path, options) => {
1083
1085
  if (DEBUG_MODE) {
1084
1086
  console.log(`Parsing ${jar}`);
1085
1087
  }
1086
- const dlist = extractJarArchive(jar, tempDir);
1088
+ const dlist = await extractJarArchive(jar, tempDir);
1087
1089
  if (dlist && dlist.length) {
1088
1090
  pkgList = pkgList.concat(dlist);
1089
1091
  }
@@ -1125,7 +1127,7 @@ export const createJavaBom = async (path, options) => {
1125
1127
  }
1126
1128
  const tempDir = mkdtempSync(join(tmpdir(), "war-deps-"));
1127
1129
  jarNSMapping = collectJarNS(tempDir);
1128
- pkgList = extractJarArchive(path, tempDir, jarNSMapping);
1130
+ pkgList = await extractJarArchive(path, tempDir, jarNSMapping);
1129
1131
  if (pkgList.length) {
1130
1132
  pkgList = await getMvnMetadata(pkgList);
1131
1133
  }
@@ -3558,7 +3560,7 @@ export const createJenkinsBom = async (path, options) => {
3558
3560
  if (DEBUG_MODE) {
3559
3561
  console.log(`Parsing ${f}`);
3560
3562
  }
3561
- const dlist = extractJarArchive(f, tempDir);
3563
+ const dlist = await extractJarArchive(f, tempDir);
3562
3564
  if (dlist && dlist.length) {
3563
3565
  pkgList = pkgList.concat(dlist);
3564
3566
  }
@@ -4202,11 +4204,17 @@ export const createCsharpBom = async (
4202
4204
  let manifestFiles = [];
4203
4205
  let pkgData = undefined;
4204
4206
  let dependencies = [];
4205
- const csProjFiles = getAllFiles(
4207
+ let csProjFiles = getAllFiles(
4206
4208
  path,
4207
4209
  (options.multiProject ? "**/" : "") + "*.csproj",
4208
4210
  options
4209
4211
  );
4212
+ csProjFiles = csProjFiles.concat(
4213
+ getAllFiles(path, (options.multiProject ? "**/" : "") + "*.vbproj", options)
4214
+ );
4215
+ csProjFiles = csProjFiles.concat(
4216
+ getAllFiles(path, (options.multiProject ? "**/" : "") + "*.fsproj", options)
4217
+ );
4210
4218
  const pkgConfigFiles = getAllFiles(
4211
4219
  path,
4212
4220
  (options.multiProject ? "**/" : "") + "packages.config",
@@ -4253,7 +4261,7 @@ export const createCsharpBom = async (
4253
4261
  console.log(`Parsing ${af}`);
4254
4262
  }
4255
4263
  pkgData = readFileSync(af, { encoding: "utf-8" });
4256
- let results = await parseCsProjAssetsData(pkgData);
4264
+ let results = await parseCsProjAssetsData(pkgData, af);
4257
4265
  let deps = results["dependenciesList"];
4258
4266
  let dlist = results["pkgList"];
4259
4267
  if (dlist && dlist.length) {
@@ -4305,7 +4313,7 @@ export const createCsharpBom = async (
4305
4313
  if (csProjData.charCodeAt(0) === 0xfeff) {
4306
4314
  csProjData = csProjData.slice(1);
4307
4315
  }
4308
- const dlist = await parseCsProjData(csProjData);
4316
+ const dlist = await parseCsProjData(csProjData, f);
4309
4317
  if (dlist && dlist.length) {
4310
4318
  pkgList = pkgList.concat(dlist);
4311
4319
  }
@@ -4336,6 +4344,22 @@ export const createCsharpBom = async (
4336
4344
  if (pkgList.length) {
4337
4345
  dependencies = mergeDependencies(dependencies, [], parentComponent);
4338
4346
  pkgList = trimComponents(pkgList, "json");
4347
+ // Perform deep analysis using dosai
4348
+ if (options.deep) {
4349
+ const slicesFile = resolve(
4350
+ options.depsSlicesFile || join(tmpdir(), "dosai.json")
4351
+ );
4352
+ // Create the slices file if it doesn't exist
4353
+ if (!existsSync(slicesFile)) {
4354
+ const sliceResult = getDotnetSlices(resolve(path), resolve(slicesFile));
4355
+ if (!sliceResult && DEBUG_MODE) {
4356
+ console.log(
4357
+ "Slicing with dosai was unsuccessful. Check the errors reported in the logs above."
4358
+ );
4359
+ }
4360
+ }
4361
+ pkgList = addEvidenceForDotnet(pkgList, slicesFile, options);
4362
+ }
4339
4363
  }
4340
4364
  if (FETCH_LICENSE) {
4341
4365
  const retMap = await getNugetMetadata(pkgList, dependencies);
@@ -5117,11 +5141,17 @@ export const createXBom = async (path, options) => {
5117
5141
  }
5118
5142
 
5119
5143
  // .Net
5120
- const csProjFiles = getAllFiles(
5144
+ let csProjFiles = getAllFiles(
5121
5145
  path,
5122
5146
  (options.multiProject ? "**/" : "") + "*.csproj",
5123
5147
  options
5124
5148
  );
5149
+ csProjFiles = csProjFiles.concat(
5150
+ getAllFiles(path, (options.multiProject ? "**/" : "") + "*.vbproj", options)
5151
+ );
5152
+ csProjFiles = csProjFiles.concat(
5153
+ getAllFiles(path, (options.multiProject ? "**/" : "") + "*.fsproj", options)
5154
+ );
5125
5155
  if (csProjFiles.length) {
5126
5156
  return await createCsharpBom(path, options);
5127
5157
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyclonedx/cdxgen",
3
- "version": "9.9.7",
3
+ "version": "9.9.9",
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>",
@@ -55,17 +55,17 @@
55
55
  "url": "https://github.com/cyclonedx/cdxgen/issues"
56
56
  },
57
57
  "dependencies": {
58
- "@babel/parser": "^7.23.5",
59
- "@babel/traverse": "^7.23.5",
58
+ "@babel/parser": "^7.23.6",
59
+ "@babel/traverse": "^7.23.6",
60
60
  "@npmcli/arborist": "7.2.0",
61
61
  "ajv": "^8.12.0",
62
62
  "ajv-formats": "^2.1.1",
63
63
  "cheerio": "^1.0.0-rc.12",
64
64
  "edn-data": "1.1.1",
65
- "find-up": "^6.3.0",
65
+ "find-up": "6.3.0",
66
66
  "glob": "^10.3.10",
67
67
  "global-agent": "^3.0.0",
68
- "got": "^13.0.0",
68
+ "got": "13.0.0",
69
69
  "iconv-lite": "^0.6.3",
70
70
  "js-yaml": "^4.1.0",
71
71
  "jws": "^4.0.0",
@@ -84,14 +84,15 @@
84
84
  },
85
85
  "optionalDependencies": {
86
86
  "@appthreat/atom": "1.7.2",
87
- "@cyclonedx/cdxgen-plugins-bin": "^1.4.0",
88
- "@cyclonedx/cdxgen-plugins-bin-arm64": "^1.4.0",
89
- "@cyclonedx/cdxgen-plugins-bin-ppc64": "^1.4.0",
87
+ "@cyclonedx/cdxgen-plugins-bin": "^1.5.4",
88
+ "@cyclonedx/cdxgen-plugins-bin-windows-amd64": "^1.5.4",
89
+ "@cyclonedx/cdxgen-plugins-bin-arm64": "^1.5.4",
90
+ "@cyclonedx/cdxgen-plugins-bin-ppc64": "^1.5.4",
90
91
  "body-parser": "^1.20.2",
91
92
  "compression": "^1.7.4",
92
93
  "connect": "^3.7.0",
93
94
  "jsonata": "^2.0.3",
94
- "sequelize": "^6.35.1",
95
+ "sequelize": "^6.35.2",
95
96
  "sqlite3": "^5.1.6"
96
97
  },
97
98
  "files": [
@@ -102,10 +103,10 @@
102
103
  "devDependencies": {
103
104
  "caxa": "^3.0.1",
104
105
  "docsify-cli": "^4.4.4",
105
- "eslint": "^8.54.0",
106
- "eslint-config-prettier": "^9.0.0",
106
+ "eslint": "^8.55.0",
107
+ "eslint-config-prettier": "^9.1.0",
107
108
  "eslint-plugin-prettier": "^5.0.1",
108
109
  "jest": "^29.7.0",
109
- "prettier": "3.1.0"
110
+ "prettier": "3.1.1"
110
111
  }
111
112
  }
package/utils.js CHANGED
@@ -19,8 +19,10 @@ import {
19
19
  readFileSync,
20
20
  rmSync,
21
21
  unlinkSync,
22
- writeFileSync
22
+ writeFileSync,
23
+ createReadStream
23
24
  } from "node:fs";
25
+ import { createHash } from "node:crypto";
24
26
  import got from "got";
25
27
  import Arborist from "@npmcli/arborist";
26
28
  import path from "node:path";
@@ -120,6 +122,19 @@ export const FETCH_LICENSE =
120
122
  process.env.FETCH_LICENSE &&
121
123
  ["true", "1"].includes(process.env.FETCH_LICENSE);
122
124
 
125
+ // Wether search.maven.org will be used to identify jars without maven metadata; default, if unset shall be 'true'
126
+ export const SEARCH_MAVEN_ORG =
127
+ !process.env.SEARCH_MAVEN_ORG ||
128
+ ["true", "1"].includes(process.env.SEARCH_MAVEN_ORG);
129
+
130
+ // circuit breaker for search maven.org
131
+ let search_maven_org_errors = 0;
132
+ const MAX_SEARCH_MAVEN_ORG_ERRORS = 5;
133
+
134
+ // circuit breaker for get repo license
135
+ let get_repo_license_errors = 0;
136
+ const MAX_GET_REPO_LICENSE_ERRORS = 5;
137
+
123
138
  const MAX_LICENSE_ID_LENGTH = 100;
124
139
 
125
140
  let PYTHON_CMD = "python";
@@ -3353,7 +3368,7 @@ export const toGitHubApiUrl = function (repoUrl, repoMetadata) {
3353
3368
  export const getRepoLicense = async function (repoUrl, repoMetadata) {
3354
3369
  let apiUrl = toGitHubApiUrl(repoUrl, repoMetadata);
3355
3370
  // Perform github lookups
3356
- if (apiUrl) {
3371
+ if (apiUrl && get_repo_license_errors < MAX_GET_REPO_LICENSE_ERRORS) {
3357
3372
  let licenseUrl = apiUrl + "/license";
3358
3373
  const headers = {};
3359
3374
  if (process.env.GITHUB_TOKEN) {
@@ -3399,8 +3414,10 @@ export const getRepoLicense = async function (repoUrl, repoMetadata) {
3399
3414
  "Please ensure GITHUB_TOKEN is set as environment variable. " +
3400
3415
  "See: https://docs.github.com/en/rest/overview/rate-limits-for-the-rest-api"
3401
3416
  );
3417
+ get_repo_license_errors++;
3402
3418
  } else if (!err.message.includes("404")) {
3403
3419
  console.log(err);
3420
+ get_repo_license_errors++;
3404
3421
  }
3405
3422
  }
3406
3423
  }
@@ -5189,7 +5206,7 @@ export const parseCsPkgData = async function (pkgData) {
5189
5206
  return pkgList;
5190
5207
  };
5191
5208
 
5192
- export const parseCsProjData = async function (csProjData) {
5209
+ export const parseCsProjData = async function (csProjData, projFile) {
5193
5210
  const pkgList = [];
5194
5211
  if (!csProjData) {
5195
5212
  return pkgList;
@@ -5218,6 +5235,27 @@ export const parseCsProjData = async function (csProjData) {
5218
5235
  }
5219
5236
  pkg.name = pref.Include;
5220
5237
  pkg.version = pref.Version;
5238
+ if (projFile) {
5239
+ pkg.properties = [
5240
+ {
5241
+ name: "SrcFile",
5242
+ value: projFile
5243
+ }
5244
+ ];
5245
+ pkg.evidence = {
5246
+ identity: {
5247
+ field: "purl",
5248
+ confidence: 0.7,
5249
+ methods: [
5250
+ {
5251
+ technique: "manifest-analysis",
5252
+ confidence: 0.7,
5253
+ value: projFile
5254
+ }
5255
+ ]
5256
+ }
5257
+ };
5258
+ }
5221
5259
  pkgList.push(pkg);
5222
5260
  }
5223
5261
  // .net framework use Reference
@@ -5232,6 +5270,27 @@ export const parseCsProjData = async function (csProjData) {
5232
5270
  if (incParts.length > 1 && incParts[1].includes("Version")) {
5233
5271
  pkg.version = incParts[1].replace("Version=", "").trim();
5234
5272
  }
5273
+ if (projFile) {
5274
+ pkg.properties = [
5275
+ {
5276
+ name: "SrcFile",
5277
+ value: projFile
5278
+ }
5279
+ ];
5280
+ pkg.evidence = {
5281
+ identity: {
5282
+ field: "purl",
5283
+ confidence: 0.7,
5284
+ methods: [
5285
+ {
5286
+ technique: "manifest-analysis",
5287
+ confidence: 0.7,
5288
+ value: projFile
5289
+ }
5290
+ ]
5291
+ }
5292
+ };
5293
+ }
5235
5294
  pkgList.push(pkg);
5236
5295
  }
5237
5296
  }
@@ -5239,7 +5298,10 @@ export const parseCsProjData = async function (csProjData) {
5239
5298
  return pkgList;
5240
5299
  };
5241
5300
 
5242
- export const parseCsProjAssetsData = async function (csProjData) {
5301
+ export const parseCsProjAssetsData = async function (
5302
+ csProjData,
5303
+ assetsJsonFile
5304
+ ) {
5243
5305
  // extract name, operator, version from .NET package representation
5244
5306
  // like "NLog >= 4.5.0"
5245
5307
  function extractNameOperatorVersion(inputStr) {
@@ -5356,6 +5418,43 @@ export const parseCsProjAssetsData = async function (csProjData) {
5356
5418
  } else if (lib[rootDep].sha256) {
5357
5419
  pkg["_integrity"] = "sha256-" + lib[rootDep].sha256;
5358
5420
  }
5421
+ if (lib[rootDep].files && Array.isArray(lib[rootDep].files)) {
5422
+ const dllFiles = new Set();
5423
+ lib[rootDep].files.forEach((f) => {
5424
+ if (
5425
+ f.endsWith(".dll") ||
5426
+ f.endsWith(".exe") ||
5427
+ f.endsWith(".so")
5428
+ ) {
5429
+ dllFiles.add(basename(f));
5430
+ }
5431
+ });
5432
+ pkg.properties = [
5433
+ {
5434
+ name: "SrcFile",
5435
+ value: assetsJsonFile
5436
+ },
5437
+ {
5438
+ name: "PackageFiles",
5439
+ value: Array.from(dllFiles).join(", ")
5440
+ }
5441
+ ];
5442
+ }
5443
+ }
5444
+ if (assetsJsonFile) {
5445
+ pkg.evidence = {
5446
+ identity: {
5447
+ field: "purl",
5448
+ confidence: 1,
5449
+ methods: [
5450
+ {
5451
+ technique: "manifest-analysis",
5452
+ confidence: 1,
5453
+ value: assetsJsonFile
5454
+ }
5455
+ ]
5456
+ }
5457
+ };
5359
5458
  }
5360
5459
  pkgList.push(pkg);
5361
5460
  pkgNameVersionMap[name + framework] = version;
@@ -6693,6 +6792,22 @@ export const getPomPropertiesFromMavenDir = function (mavenDir) {
6693
6792
  return pomProperties;
6694
6793
  };
6695
6794
 
6795
+ /**
6796
+ *
6797
+ * @param {string} hashName name of hash algorithm
6798
+ * @param {string} path path to file
6799
+ * @returns {Promise<String>} hex value of hash
6800
+ */
6801
+ async function checksumFile(hashName, path) {
6802
+ return new Promise((resolve, reject) => {
6803
+ const hash = createHash(hashName);
6804
+ const stream = createReadStream(path);
6805
+ stream.on("error", (err) => reject(err));
6806
+ stream.on("data", (chunk) => hash.update(chunk));
6807
+ stream.on("end", () => resolve(hash.digest("hex")));
6808
+ });
6809
+ }
6810
+
6696
6811
  /**
6697
6812
  * Method to extract a war or ear file
6698
6813
  *
@@ -6702,7 +6817,7 @@ export const getPomPropertiesFromMavenDir = function (mavenDir) {
6702
6817
  *
6703
6818
  * @return pkgList Package list
6704
6819
  */
6705
- export const extractJarArchive = function (
6820
+ export const extractJarArchive = async function (
6706
6821
  jarFile,
6707
6822
  tempDir,
6708
6823
  jarNSMapping = {}
@@ -6800,6 +6915,35 @@ export const extractJarArchive = function (
6800
6915
  version = pomProperties["version"],
6801
6916
  confidence = 1,
6802
6917
  technique = "manifest-analysis";
6918
+ if (
6919
+ (!group || !name || !version) &&
6920
+ SEARCH_MAVEN_ORG &&
6921
+ search_maven_org_errors < MAX_SEARCH_MAVEN_ORG_ERRORS
6922
+ ) {
6923
+ try {
6924
+ const sha = await checksumFile("sha1", jf);
6925
+ const searchurl =
6926
+ "https://search.maven.org/solrsearch/select?q=1:%22" +
6927
+ sha +
6928
+ "%22&rows=20&wt=json";
6929
+ const res = await cdxgenAgent.get(searchurl, {
6930
+ responseType: "json"
6931
+ });
6932
+ const data = res && res.body ? res.body["response"] : undefined;
6933
+ if (data && data["numFound"] == 1) {
6934
+ const jarInfo = data["docs"][0];
6935
+ group = jarInfo["g"];
6936
+ name = jarInfo["a"];
6937
+ version = jarInfo["v"];
6938
+ technique = "hash-comparison";
6939
+ }
6940
+ } catch (err) {
6941
+ if (err && err.message && !err.message.includes("404")) {
6942
+ console.log(err);
6943
+ search_maven_org_errors++;
6944
+ }
6945
+ }
6946
+ }
6803
6947
  if ((!group || !name || !version) && existsSync(manifestFile)) {
6804
6948
  confidence = 0.8;
6805
6949
  const jarMetadata = parseJarManifest(
@@ -7208,6 +7352,7 @@ export const executeAtom = (src, args) => {
7208
7352
  let cwd =
7209
7353
  existsSync(src) && lstatSync(src).isDirectory() ? src : dirname(src);
7210
7354
  let ATOM_BIN = getAtomCommand();
7355
+ let isSupported = true;
7211
7356
  if (ATOM_BIN.includes(" ")) {
7212
7357
  const tmpA = ATOM_BIN.split(" ");
7213
7358
  if (tmpA && tmpA.length > 1) {
@@ -7260,6 +7405,12 @@ export const executeAtom = (src, args) => {
7260
7405
  );
7261
7406
  }
7262
7407
  }
7408
+ if (result.stdout) {
7409
+ if (result.stdout.includes("No language frontend supported for language")) {
7410
+ console.log("This language is not yet supported by atom.");
7411
+ isSupported = false;
7412
+ }
7413
+ }
7263
7414
  if (DEBUG_MODE) {
7264
7415
  if (result.stdout) {
7265
7416
  console.log(result.stdout);
@@ -7268,7 +7419,7 @@ export const executeAtom = (src, args) => {
7268
7419
  console.log(result.stderr);
7269
7420
  }
7270
7421
  }
7271
- return !result.error;
7422
+ return isSupported && !result.error;
7272
7423
  };
7273
7424
 
7274
7425
  /**
@@ -8736,3 +8887,103 @@ export const getNugetMetadata = async function (
8736
8887
  dependencies: newDependencies
8737
8888
  };
8738
8889
  };
8890
+
8891
+ export const addEvidenceForDotnet = (pkgList, slicesFile) => {
8892
+ // We need two datastructures.
8893
+ // dll to purl mapping from the pkgList
8894
+ // purl to occurrences list using the slicesFile
8895
+ if (!slicesFile || !existsSync(slicesFile)) {
8896
+ return pkgList;
8897
+ }
8898
+ const pkgFilePurlMap = {};
8899
+ const purlLocationMap = {};
8900
+ const purlModulesMap = {};
8901
+ const purlMethodsMap = {};
8902
+ for (const apkg of pkgList) {
8903
+ if (apkg.properties && Array.isArray(apkg.properties)) {
8904
+ apkg.properties
8905
+ .filter((p) => p.name === "PackageFiles")
8906
+ .forEach((aprop) => {
8907
+ if (aprop.value) {
8908
+ const tmpA = aprop.value.split(", ");
8909
+ if (tmpA && tmpA.length) {
8910
+ tmpA.forEach((dllFile) => {
8911
+ pkgFilePurlMap[dllFile] = apkg.purl;
8912
+ });
8913
+ }
8914
+ }
8915
+ });
8916
+ }
8917
+ }
8918
+ const slicesData = JSON.parse(readFileSync(slicesFile, "utf-8"));
8919
+ if (slicesData && Object.keys(slicesData)) {
8920
+ if (slicesData.Dependencies) {
8921
+ for (const adep of slicesData.Dependencies) {
8922
+ if (
8923
+ adep.Module &&
8924
+ adep.Module.endsWith(".dll") &&
8925
+ pkgFilePurlMap[adep.Module]
8926
+ ) {
8927
+ const modPurl = pkgFilePurlMap[adep.Module];
8928
+ if (!purlLocationMap[modPurl]) {
8929
+ purlLocationMap[modPurl] = new Set();
8930
+ }
8931
+ purlLocationMap[modPurl].add(`${adep.Path}#${adep.LineNumber}`);
8932
+ }
8933
+ }
8934
+ }
8935
+ if (slicesData.MethodCalls) {
8936
+ for (const amethodCall of slicesData.MethodCalls) {
8937
+ if (
8938
+ amethodCall.Module &&
8939
+ amethodCall.Module.endsWith(".dll") &&
8940
+ pkgFilePurlMap[amethodCall.Module]
8941
+ ) {
8942
+ const modPurl = pkgFilePurlMap[amethodCall.Module];
8943
+ if (!purlLocationMap[modPurl]) {
8944
+ purlLocationMap[modPurl] = new Set();
8945
+ }
8946
+ if (!purlModulesMap[modPurl]) {
8947
+ purlModulesMap[modPurl] = new Set();
8948
+ }
8949
+ if (!purlMethodsMap[modPurl]) {
8950
+ purlMethodsMap[modPurl] = new Set();
8951
+ }
8952
+ purlLocationMap[modPurl].add(
8953
+ `${amethodCall.Path}#${amethodCall.LineNumber}`
8954
+ );
8955
+ purlModulesMap[modPurl].add(amethodCall.ClassName);
8956
+ purlMethodsMap[modPurl].add(amethodCall.CalledMethod);
8957
+ }
8958
+ }
8959
+ }
8960
+ }
8961
+ if (Object.keys(purlLocationMap).length) {
8962
+ for (const apkg of pkgList) {
8963
+ if (purlLocationMap[apkg.purl]) {
8964
+ const locationOccurrences = Array.from(
8965
+ purlLocationMap[apkg.purl]
8966
+ ).sort();
8967
+ // Add the occurrences evidence
8968
+ apkg.evidence.occurrences = locationOccurrences.map((l) => ({
8969
+ location: l
8970
+ }));
8971
+ }
8972
+ // Add the imported modules to properties
8973
+ if (purlModulesMap[apkg.purl]) {
8974
+ apkg.properties.push({
8975
+ name: "ImportedModules",
8976
+ value: Array.from(purlModulesMap[apkg.purl]).sort().join(", ")
8977
+ });
8978
+ }
8979
+ // Add the called methods to properties
8980
+ if (purlMethodsMap[apkg.purl]) {
8981
+ apkg.properties.push({
8982
+ name: "CalledMethods",
8983
+ value: Array.from(purlMethodsMap[apkg.purl]).sort().join(", ")
8984
+ });
8985
+ }
8986
+ }
8987
+ }
8988
+ return pkgList;
8989
+ };
package/utils.test.js CHANGED
@@ -1272,8 +1272,9 @@ test("parse project.assets.json", async () => {
1272
1272
  dependenciesList: [],
1273
1273
  pkgList: []
1274
1274
  });
1275
- const dep_list = await parseCsProjAssetsData(
1276
- readFileSync("./test/data/project.assets.json", { encoding: "utf-8" })
1275
+ let dep_list = await parseCsProjAssetsData(
1276
+ readFileSync("./test/data/project.assets.json", { encoding: "utf-8" }),
1277
+ "./test/data/project.assets.json"
1277
1278
  );
1278
1279
  expect(dep_list["pkgList"].length).toEqual(302);
1279
1280
  expect(dep_list["pkgList"][0]).toEqual({
@@ -1309,6 +1310,26 @@ test("parse project.assets.json", async () => {
1309
1310
  ],
1310
1311
  ref: "pkg:nuget/Castle.Core.Tests@0.0.0"
1311
1312
  });
1313
+ dep_list = await parseCsProjAssetsData(
1314
+ readFileSync("./test/data/project.assets1.json", { encoding: "utf-8" }),
1315
+ "./test/data/project.assets1.json"
1316
+ );
1317
+ expect(dep_list["pkgList"].length).toEqual(43);
1318
+ expect(dep_list["pkgList"][0]).toEqual({
1319
+ "bom-ref": "pkg:nuget/Podcast.Server@1.0.0",
1320
+ purl: "pkg:nuget/Podcast.Server@1.0.0",
1321
+ group: "",
1322
+ name: "Podcast.Server",
1323
+ type: "application",
1324
+ version: "1.0.0"
1325
+ });
1326
+ /*
1327
+ const pkgList = addEvidenceForDotnet(
1328
+ dep_list.pkgList,
1329
+ "./test/data/dosai-methods.json"
1330
+ );
1331
+ expect(pkgList.length).toEqual(43);
1332
+ */
1312
1333
  });
1313
1334
 
1314
1335
  test("parse packages.lock.json", async () => {
@@ -1725,8 +1746,8 @@ test("parsePkgLock v3", async () => {
1725
1746
  projectName: "cdxgen"
1726
1747
  });
1727
1748
  deps = parsedList.pkgList;
1728
- expect(deps.length).toEqual(1204);
1729
- expect(parsedList.dependenciesList.length).toEqual(1204);
1749
+ expect(deps.length).toEqual(1205);
1750
+ expect(parsedList.dependenciesList.length).toEqual(1205);
1730
1751
  });
1731
1752
 
1732
1753
  test("parseBowerJson", async () => {