@cyclonedx/cdxgen 9.9.4 → 9.9.6

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/utils.js CHANGED
@@ -19,7 +19,8 @@ import {
19
19
  readFileSync,
20
20
  rmSync,
21
21
  unlinkSync,
22
- writeFileSync
22
+ writeFileSync,
23
+ readdirSync
23
24
  } from "node:fs";
24
25
  import got from "got";
25
26
  import Arborist from "@npmcli/arborist";
@@ -216,20 +217,10 @@ export function getLicenses(pkg, format = "xml") {
216
217
  licenseContent.id = l;
217
218
  licenseContent.url = "https://opensource.org/licenses/" + l;
218
219
  } else if (l.startsWith("http")) {
219
- if (!l.includes("opensource.org")) {
220
- licenseContent.name = "CUSTOM";
221
- } else {
222
- const possibleId = l
223
- .replace("http://www.opensource.org/licenses/", "")
224
- .toUpperCase();
225
- spdxLicenses.forEach((v) => {
226
- if (v.toUpperCase() === possibleId) {
227
- licenseContent.id = v;
228
- }
229
- });
230
- }
231
- if (l.includes("mit-license")) {
232
- licenseContent.id = "MIT";
220
+ let knownLicense = getKnownLicense(l, pkg);
221
+ if (knownLicense) {
222
+ licenseContent.id = knownLicense.id;
223
+ licenseContent.name = knownLicense.name;
233
224
  }
234
225
  // We always need a name to avoid validation errors
235
226
  // Issue: #469
@@ -251,10 +242,82 @@ export function getLicenses(pkg, format = "xml") {
251
242
  return licenseContent;
252
243
  })
253
244
  .map((l) => ({ license: l }));
245
+ } else {
246
+ let knownLicense = getKnownLicense(undefined, pkg);
247
+ if (knownLicense) {
248
+ return [{ license: knownLicense }];
249
+ }
254
250
  }
255
251
  return undefined;
256
252
  }
257
253
 
254
+ /**
255
+ * Method to retrieve known license by known-licenses.json
256
+ *
257
+ * @param {String} repoUrl Repository url
258
+ * @param {String} pkg Bom ref
259
+ * @return {Object>} Objetct with SPDX license id or license name
260
+ */
261
+ export const getKnownLicense = function (licenseUrl, pkg) {
262
+ if (licenseUrl && licenseUrl.includes("opensource.org")) {
263
+ const possibleId = licenseUrl
264
+ .toLowerCase()
265
+ .replace("https://", "http://")
266
+ .replace("http://www.opensource.org/licenses/", "");
267
+ for (const spdxLicense of spdxLicenses) {
268
+ if (spdxLicense.toLowerCase() === possibleId) {
269
+ return { id: spdxLicense };
270
+ }
271
+ }
272
+ } else if (licenseUrl && licenseUrl.includes("apache.org")) {
273
+ const possibleId = licenseUrl
274
+ .toLowerCase()
275
+ .replace("https://", "http://")
276
+ .replace("http://www.apache.org/licenses/license-", "apache-")
277
+ .replace(".txt", "");
278
+ for (const spdxLicense of spdxLicenses) {
279
+ if (spdxLicense.toLowerCase() === possibleId) {
280
+ return { id: spdxLicense };
281
+ }
282
+ }
283
+ }
284
+ for (const akLicGroup of knownLicenses) {
285
+ if (
286
+ akLicGroup.packageNamespace === "*" ||
287
+ (pkg.purl && pkg.purl.startsWith(akLicGroup.packageNamespace))
288
+ ) {
289
+ for (const akLic of akLicGroup.knownLicenses) {
290
+ if (akLic.group && akLic.name) {
291
+ if (akLic.group === "." && akLic.name === pkg.name) {
292
+ return { id: akLic.license, name: akLic.licenseName };
293
+ } else if (
294
+ pkg.group &&
295
+ pkg.group.includes(akLic.group) &&
296
+ (akLic.name === pkg.name || akLic.name === "*")
297
+ ) {
298
+ return { id: akLic.license, name: akLic.licenseName };
299
+ }
300
+ }
301
+ if (
302
+ akLic.urlIncludes &&
303
+ licenseUrl &&
304
+ licenseUrl.includes(akLic.urlIncludes)
305
+ ) {
306
+ return { id: akLic.license, name: akLic.licenseName };
307
+ }
308
+ if (
309
+ akLic.urlEndswith &&
310
+ licenseUrl &&
311
+ licenseUrl.endsWith(akLic.urlEndswith)
312
+ ) {
313
+ return { id: akLic.license, name: akLic.licenseName };
314
+ }
315
+ }
316
+ }
317
+ }
318
+ return undefined;
319
+ };
320
+
258
321
  /**
259
322
  * Tries to find a file containing the license text based on commonly
260
323
  * used naming and content types. If a candidate file is found, add
@@ -2429,7 +2492,7 @@ export const fetchPomXmlAsJson = async function ({
2429
2492
  * @param {String} name
2430
2493
  * @param {String} version
2431
2494
  *
2432
- * @return {String}
2495
+ * @return {Promise<String>}
2433
2496
  */
2434
2497
  export const fetchPomXml = async function ({
2435
2498
  urlPrefix,
@@ -2466,7 +2529,7 @@ export const parseLicenseEntryOrArrayFromPomXml = function (license) {
2466
2529
  * @param {String} name
2467
2530
  * @param {String} version
2468
2531
  *
2469
- * @return {String} License ID
2532
+ * @return {Promise<String>} License ID
2470
2533
  */
2471
2534
  export const extractLicenseCommentFromPomXml = async function ({
2472
2535
  urlPrefix,
@@ -3286,7 +3349,7 @@ export const toGitHubApiUrl = function (repoUrl, repoMetadata) {
3286
3349
  *
3287
3350
  * @param {String} repoUrl Repository url
3288
3351
  * @param {Object} repoMetadata Object containing group and package name strings
3289
- * @return {String} SPDX license id
3352
+ * @return {Promise<String>} SPDX license id
3290
3353
  */
3291
3354
  export const getRepoLicense = async function (repoUrl, repoMetadata) {
3292
3355
  let apiUrl = toGitHubApiUrl(repoUrl, repoMetadata);
@@ -3322,23 +3385,23 @@ export const getRepoLicense = async function (repoUrl, repoMetadata) {
3322
3385
  }
3323
3386
  }
3324
3387
  licObj["id"] = licenseId;
3325
- return licObj;
3388
+ if (licObj["id"] || licObj["name"]) {
3389
+ return licObj;
3390
+ }
3326
3391
  }
3327
3392
  } catch (err) {
3328
- return undefined;
3329
- }
3330
- } else if (repoMetadata) {
3331
- const group = repoMetadata.group;
3332
- const name = repoMetadata.name;
3333
- if (group && name) {
3334
- for (const akLic of knownLicenses) {
3335
- if (akLic.group === "." && akLic.name === name) {
3336
- return akLic.license;
3337
- } else if (
3338
- group.includes(akLic.group) &&
3339
- (akLic.name === name || akLic.name === "*")
3393
+ if (err && err.message) {
3394
+ if (
3395
+ err.message.includes("rate limit exceeded") &&
3396
+ !process.env.GITHUB_TOKEN
3340
3397
  ) {
3341
- return akLic.license;
3398
+ console.log(
3399
+ "Rate limit exceeded for REST API of github.com. " +
3400
+ "Please ensure GITHUB_TOKEN is set as environment variable. " +
3401
+ "See: https://docs.github.com/en/rest/overview/rate-limits-for-the-rest-api"
3402
+ );
3403
+ } else if (!err.message.includes("404")) {
3404
+ console.log(err);
3342
3405
  }
3343
3406
  }
3344
3407
  }
@@ -4392,12 +4455,14 @@ export const parseContainerFile = function (fileContents) {
4392
4455
  const imgList = [];
4393
4456
 
4394
4457
  let buildStageNames = [];
4395
- for (const line of fileContents.split("\n")) {
4396
- if (line.trim().startsWith("#")) {
4458
+ for (let line of fileContents.split("\n")) {
4459
+ line = line.trim();
4460
+
4461
+ if (line.startsWith("#")) {
4397
4462
  continue; // skip commented out lines
4398
4463
  }
4399
4464
 
4400
- if (line.includes("FROM")) {
4465
+ if (line.startsWith("FROM")) {
4401
4466
  const fromStatement = line.split("FROM")[1].split("AS");
4402
4467
 
4403
4468
  const imageStatement = fromStatement[0].trim();
@@ -4425,6 +4490,68 @@ export const parseContainerFile = function (fileContents) {
4425
4490
  return imgList;
4426
4491
  };
4427
4492
 
4493
+ export const parseBitbucketPipelinesFile = function (fileContents) {
4494
+ const imgList = [];
4495
+
4496
+ let privateImageBlockFound = false;
4497
+
4498
+ for (let line of fileContents.split("\n")) {
4499
+ line = line.trim();
4500
+ if (line.startsWith("#")) {
4501
+ continue; // skip commented out lines
4502
+ }
4503
+
4504
+ // Assume this is a private build image object
4505
+ if (line.startsWith("name:") && privateImageBlockFound) {
4506
+ const imageName = line.split("name:").pop().trim();
4507
+
4508
+ imgList.push({
4509
+ image: imageName
4510
+ });
4511
+
4512
+ privateImageBlockFound = false;
4513
+ }
4514
+
4515
+ // Docker image usage
4516
+ if (line.startsWith("image:")) {
4517
+ const imageName = line.split("image:").pop().trim();
4518
+
4519
+ /**
4520
+ * Assume this is a private build image object
4521
+ * See: https://support.atlassian.com/bitbucket-cloud/docs/use-docker-images-as-build-environments/#Using-private-build-images
4522
+ */
4523
+ if (imageName === "") {
4524
+ privateImageBlockFound = true;
4525
+ continue;
4526
+ } else {
4527
+ /**
4528
+ * Assume this is a public build image
4529
+ * See: https://support.atlassian.com/bitbucket-cloud/docs/use-docker-images-as-build-environments/#Using-public-build-images
4530
+ */
4531
+
4532
+ imgList.push({
4533
+ image: imageName
4534
+ });
4535
+ }
4536
+ }
4537
+
4538
+ // Pipe usage
4539
+ if (line.startsWith("- pipe:")) {
4540
+ let pipeName = line.split("- pipe:").pop().trim();
4541
+
4542
+ if (pipeName.startsWith("docker://")) {
4543
+ pipeName = pipeName.replace("docker://", "");
4544
+ }
4545
+
4546
+ imgList.push({
4547
+ image: pipeName
4548
+ });
4549
+ }
4550
+ }
4551
+
4552
+ return imgList;
4553
+ };
4554
+
4428
4555
  export const parseContainerSpecData = function (dcData) {
4429
4556
  const pkgList = [];
4430
4557
  const imgList = [];
@@ -4617,8 +4744,13 @@ export const parseOpenapiSpecData = function (oaData) {
4617
4744
  } catch (e) {
4618
4745
  return servlist;
4619
4746
  }
4620
- const name = oaData.info.title.replace(/ /g, "-");
4621
- const version = oaData.info.version || "latest";
4747
+
4748
+ const name =
4749
+ oaData.info && oaData.info.title
4750
+ ? oaData.info.title.replace(/ /g, "-")
4751
+ : "default-name";
4752
+ const version =
4753
+ oaData.info && oaData.info.version ? oaData.info.version : "latest";
4622
4754
  const aservice = {
4623
4755
  "bom-ref": `urn:service:${name}:${version}`,
4624
4756
  name,
@@ -5404,7 +5536,7 @@ export const parseComposerLock = function (pkgLockFile) {
5404
5536
  if (existsSync(pkgLockFile)) {
5405
5537
  let lockData = {};
5406
5538
  try {
5407
- lockData = JSON.parse(readFileSync(pkgLockFile, "utf8"));
5539
+ lockData = JSON.parse(readFileSync(pkgLockFile, { encoding: "utf-8" }));
5408
5540
  } catch (e) {
5409
5541
  console.error("Invalid composer.lock file:", pkgLockFile);
5410
5542
  return [];
@@ -5473,7 +5605,7 @@ export const parseSbtTree = (sbtTreeFile) => {
5473
5605
  const dependenciesList = [];
5474
5606
  const keys_cache = {};
5475
5607
  const level_trees = {};
5476
- const tmpA = readFileSync(sbtTreeFile, "utf-8").split("\n");
5608
+ const tmpA = readFileSync(sbtTreeFile, { encoding: "utf-8" }).split("\n");
5477
5609
  let last_level = 0;
5478
5610
  let last_purl = "";
5479
5611
  let stack = [];
@@ -5605,7 +5737,9 @@ export const parseSbtTree = (sbtTreeFile) => {
5605
5737
  export const parseSbtLock = function (pkgLockFile) {
5606
5738
  const pkgList = [];
5607
5739
  if (existsSync(pkgLockFile)) {
5608
- const lockData = JSON.parse(readFileSync(pkgLockFile, "utf8"));
5740
+ const lockData = JSON.parse(
5741
+ readFileSync(pkgLockFile, { encoding: "utf-8" })
5742
+ );
5609
5743
  if (lockData && lockData.dependencies) {
5610
5744
  for (const pkg of lockData.dependencies) {
5611
5745
  const artifacts = pkg.artifacts || undefined;
@@ -6062,7 +6196,9 @@ export const parseSwiftResolved = (resolvedFile) => {
6062
6196
  const pkgList = [];
6063
6197
  if (existsSync(resolvedFile)) {
6064
6198
  try {
6065
- const pkgData = JSON.parse(readFileSync(resolvedFile, "utf8"));
6199
+ const pkgData = JSON.parse(
6200
+ readFileSync(resolvedFile, { encoding: "utf-8" })
6201
+ );
6066
6202
  let resolvedList = [];
6067
6203
  if (pkgData.pins) {
6068
6204
  resolvedList = pkgData.pins;
@@ -6255,7 +6391,7 @@ export const collectJarNS = function (jarPath, pomPathMap = {}) {
6255
6391
  }
6256
6392
  }
6257
6393
  if (existsSync(pomname)) {
6258
- pomData = parsePomXml(readFileSync(pomname, "utf-8"));
6394
+ pomData = parsePomXml(readFileSync(pomname, { encoding: "utf-8" }));
6259
6395
  if (pomData) {
6260
6396
  const purlObj = new PackageURL(
6261
6397
  "maven",
@@ -6514,12 +6650,67 @@ export const parseJarManifest = function (jarMetadata) {
6514
6650
  return metadata;
6515
6651
  };
6516
6652
 
6653
+ export const parsePomProperties = function (pomProperties) {
6654
+ const properties = {};
6655
+ if (!pomProperties) {
6656
+ return properties;
6657
+ }
6658
+ pomProperties.split("\n").forEach((l) => {
6659
+ l = l.replace("\r", "");
6660
+ if (l.includes("=")) {
6661
+ const tmpA = l.split("=");
6662
+ if (tmpA && tmpA.length === 2) {
6663
+ properties[tmpA[0]] = tmpA[1].replace("\r", "");
6664
+ }
6665
+ }
6666
+ });
6667
+ return properties;
6668
+ };
6669
+
6517
6670
  export const encodeForPurl = (s) => {
6518
6671
  return s && !s.includes("%40")
6519
6672
  ? encodeURIComponent(s).replace(/%3A/g, ":").replace(/%2F/g, "/")
6520
6673
  : s;
6521
6674
  };
6522
6675
 
6676
+ /**
6677
+ * Method to get pom properties from maven directory
6678
+ *
6679
+ * @param {string} mavenDir Path to maven directory
6680
+ *
6681
+ * @return array with pom properties
6682
+ */
6683
+ export const getPomPropertiesFromMavenDir = function (mavenDir) {
6684
+ let pomProperties = {};
6685
+ if (existsSync(mavenDir) && lstatSync(mavenDir).isDirectory()) {
6686
+ let mavenDirEntries = readdirSync(mavenDir, { withFileTypes: true });
6687
+ mavenDirEntries.forEach((mavenDirEntry) => {
6688
+ if (mavenDirEntry.isDirectory()) {
6689
+ let groupDirEntries = readdirSync(
6690
+ join(mavenDirEntry.path, mavenDirEntry.name),
6691
+ { withFileTypes: true }
6692
+ );
6693
+ groupDirEntries.forEach((groupDirEntry) => {
6694
+ if (groupDirEntry.isDirectory()) {
6695
+ let pomPropertiesFile = join(
6696
+ groupDirEntry.path,
6697
+ groupDirEntry.name,
6698
+ "pom.properties"
6699
+ );
6700
+ if (existsSync(pomPropertiesFile)) {
6701
+ const pomPropertiesString = readFileSync(pomPropertiesFile, {
6702
+ encoding: "utf-8"
6703
+ });
6704
+ pomProperties = parsePomProperties(pomPropertiesString);
6705
+ }
6706
+ }
6707
+ });
6708
+ }
6709
+ });
6710
+ }
6711
+ return pomProperties;
6712
+ };
6713
+
6523
6714
  /**
6524
6715
  * Method to extract a war or ear file
6525
6716
  *
@@ -6601,13 +6792,14 @@ export const extractJarArchive = function (
6601
6792
  }
6602
6793
  const manifestDir = join(tempDir, "META-INF");
6603
6794
  const manifestFile = join(manifestDir, "MANIFEST.MF");
6795
+ const mavenDir = join(manifestDir, "maven");
6604
6796
  let jarResult = {
6605
6797
  status: 1
6606
6798
  };
6607
6799
  if (existsSync(pomname)) {
6608
6800
  jarResult = { status: 0 };
6609
6801
  } else {
6610
- jarResult = spawnSync("jar", ["-xf", jf], {
6802
+ jarResult = spawnSync("jar", ["-xf", jf, "META-INF"], {
6611
6803
  encoding: "utf-8",
6612
6804
  cwd: tempDir,
6613
6805
  shell: isWin,
@@ -6617,29 +6809,42 @@ export const extractJarArchive = function (
6617
6809
  if (jarResult.status !== 0) {
6618
6810
  console.error(jarResult.stdout, jarResult.stderr);
6619
6811
  } else {
6620
- if (existsSync(manifestFile)) {
6812
+ // When maven descriptor is available take group, name and version from pom.properties
6813
+ // META-INF/maven/${groupId}/${artifactId}/pom.properties
6814
+ // see https://maven.apache.org/shared/maven-archiver/index.html
6815
+ const pomProperties = getPomPropertiesFromMavenDir(mavenDir);
6816
+ let group = pomProperties["groupId"],
6817
+ name = pomProperties["artifactId"],
6818
+ version = pomProperties["version"],
6819
+ confidence = 1,
6820
+ technique = "manifest-analysis";
6821
+ if ((!group || !name || !version) && existsSync(manifestFile)) {
6822
+ confidence = 0.8;
6621
6823
  const jarMetadata = parseJarManifest(
6622
6824
  readFileSync(manifestFile, {
6623
6825
  encoding: "utf-8"
6624
6826
  })
6625
6827
  );
6626
- let group =
6828
+ group =
6829
+ group ||
6627
6830
  jarMetadata["Extension-Name"] ||
6628
6831
  jarMetadata["Implementation-Vendor-Id"] ||
6629
6832
  jarMetadata["Bundle-SymbolicName"] ||
6630
6833
  jarMetadata["Bundle-Vendor"] ||
6631
6834
  jarMetadata["Automatic-Module-Name"] ||
6632
6835
  "";
6633
- let version =
6836
+ version =
6837
+ version ||
6634
6838
  jarMetadata["Bundle-Version"] ||
6635
6839
  jarMetadata["Implementation-Version"] ||
6636
6840
  jarMetadata["Specification-Version"];
6637
6841
  if (version && version.includes(" ")) {
6638
6842
  version = version.split(" ")[0];
6639
6843
  }
6640
- let name = "";
6641
6844
  // Prefer jar filename to construct name and version
6642
6845
  if (!name || !version || name === "" || version === "") {
6846
+ confidence = 0.5;
6847
+ technique = "filename";
6643
6848
  const tmpA = jarname.split("-");
6644
6849
  if (tmpA && tmpA.length > 1) {
6645
6850
  const lastPart = tmpA[tmpA.length - 1];
@@ -6688,56 +6893,56 @@ export const extractJarArchive = function (
6688
6893
  break;
6689
6894
  }
6690
6895
  }
6691
- if (name && version) {
6692
- // if group is empty use name as group
6693
- group = encodeForPurl(group === "." ? name : group || name) || "";
6694
- let apkg = {
6896
+ // if group is empty use name as group
6897
+ group = group === "." ? name : group || name;
6898
+ }
6899
+ if (name && version) {
6900
+ let apkg = {
6901
+ group: group ? encodeForPurl(group) : "",
6902
+ name: name ? encodeForPurl(name) : "",
6903
+ version,
6904
+ purl: new PackageURL(
6905
+ "maven",
6695
6906
  group,
6696
- name: name ? encodeForPurl(name) : "",
6907
+ name,
6697
6908
  version,
6698
- purl: new PackageURL(
6699
- "maven",
6700
- group,
6701
- name,
6702
- version,
6703
- { type: "jar" },
6704
- null
6705
- ).toString(),
6706
- evidence: {
6707
- identity: {
6708
- field: "purl",
6709
- confidence: 0.5,
6710
- methods: [
6711
- {
6712
- technique: "filename",
6713
- confidence: 0.5,
6714
- value: jarname
6715
- }
6716
- ]
6717
- }
6718
- },
6719
- properties: [
6720
- {
6721
- name: "SrcFile",
6722
- value: jarname
6723
- }
6724
- ]
6725
- };
6726
- if (
6727
- jarNSMapping &&
6728
- jarNSMapping[apkg.purl] &&
6729
- jarNSMapping[apkg.purl].namespaces
6730
- ) {
6731
- apkg.properties.push({
6732
- name: "Namespaces",
6733
- value: jarNSMapping[apkg.purl].namespaces.join("\n")
6734
- });
6735
- }
6736
- pkgList.push(apkg);
6737
- } else {
6738
- if (DEBUG_MODE) {
6739
- console.log(`Ignored jar ${jarname}`, jarMetadata, name, version);
6740
- }
6909
+ { type: "jar" },
6910
+ null
6911
+ ).toString(),
6912
+ evidence: {
6913
+ identity: {
6914
+ field: "purl",
6915
+ confidence: confidence,
6916
+ methods: [
6917
+ {
6918
+ technique: technique,
6919
+ confidence: confidence,
6920
+ value: jarname
6921
+ }
6922
+ ]
6923
+ }
6924
+ },
6925
+ properties: [
6926
+ {
6927
+ name: "SrcFile",
6928
+ value: jarname
6929
+ }
6930
+ ]
6931
+ };
6932
+ if (
6933
+ jarNSMapping &&
6934
+ jarNSMapping[apkg.purl] &&
6935
+ jarNSMapping[apkg.purl].namespaces
6936
+ ) {
6937
+ apkg.properties.push({
6938
+ name: "Namespaces",
6939
+ value: jarNSMapping[apkg.purl].namespaces.join("\n")
6940
+ });
6941
+ }
6942
+ pkgList.push(apkg);
6943
+ } else {
6944
+ if (DEBUG_MODE) {
6945
+ console.log(`Ignored jar ${jarname}`, name, version);
6741
6946
  }
6742
6947
  }
6743
6948
  try {
@@ -6929,6 +7134,7 @@ export const getMavenCommand = (srcPath, rootPath) => {
6929
7134
  let isWrapperReady = false;
6930
7135
  let isWrapperFound = false;
6931
7136
  let findMavenFile = "mvnw";
7137
+ let mavenWrapperCmd = null;
6932
7138
  if (platform() == "win32") {
6933
7139
  findMavenFile = "mvnw.bat";
6934
7140
  if (
@@ -6947,7 +7153,7 @@ export const getMavenCommand = (srcPath, rootPath) => {
6947
7153
  } catch (e) {
6948
7154
  // continue regardless of error
6949
7155
  }
6950
- mavenCmd = resolve(join(srcPath, findMavenFile));
7156
+ mavenWrapperCmd = resolve(join(srcPath, findMavenFile));
6951
7157
  isWrapperFound = true;
6952
7158
  } else if (rootPath && existsSync(join(rootPath, findMavenFile))) {
6953
7159
  // Check if the root directory has a wrapper script
@@ -6956,7 +7162,7 @@ export const getMavenCommand = (srcPath, rootPath) => {
6956
7162
  } catch (e) {
6957
7163
  // continue regardless of error
6958
7164
  }
6959
- mavenCmd = resolve(join(rootPath, findMavenFile));
7165
+ mavenWrapperCmd = resolve(join(rootPath, findMavenFile));
6960
7166
  isWrapperFound = true;
6961
7167
  }
6962
7168
  if (isWrapperFound) {
@@ -6965,14 +7171,15 @@ export const getMavenCommand = (srcPath, rootPath) => {
6965
7171
  "Testing the wrapper script by invoking wrapper:wrapper task"
6966
7172
  );
6967
7173
  }
6968
- const result = spawnSync(mavenCmd, ["wrapper:wrapper"], {
7174
+ const result = spawnSync(mavenWrapperCmd, ["wrapper:wrapper"], {
6969
7175
  encoding: "utf-8",
6970
7176
  cwd: rootPath,
6971
7177
  timeout: TIMEOUT_MS,
6972
7178
  shell: isWin
6973
7179
  });
6974
- if (!result.error) {
7180
+ if (!result.error && !result.status) {
6975
7181
  isWrapperReady = true;
7182
+ mavenCmd = mavenWrapperCmd;
6976
7183
  } else {
6977
7184
  if (DEBUG_MODE) {
6978
7185
  console.log(
@@ -7115,7 +7322,9 @@ export const findAppModules = function (
7115
7322
  ];
7116
7323
  executeAtom(src, args);
7117
7324
  if (existsSync(slicesFile)) {
7118
- const slicesData = JSON.parse(readFileSync(slicesFile), "utf8");
7325
+ const slicesData = JSON.parse(readFileSync(slicesFile), {
7326
+ encoding: "utf-8"
7327
+ });
7119
7328
  if (slicesData && Object.keys(slicesData) && slicesData.modules) {
7120
7329
  retList = slicesData.modules;
7121
7330
  } else {
@@ -7588,7 +7797,7 @@ export const componentSorter = (a, b) => {
7588
7797
  };
7589
7798
 
7590
7799
  export const parseCmakeDotFile = (dotFile, pkgType, options = {}) => {
7591
- const dotGraphData = readFileSync(dotFile, "utf-8");
7800
+ const dotGraphData = readFileSync(dotFile, { encoding: "utf-8" });
7592
7801
  const pkgList = [];
7593
7802
  const dependenciesMap = {};
7594
7803
  const pkgBomRefMap = {};
@@ -7697,12 +7906,13 @@ export const parseCmakeDotFile = (dotFile, pkgType, options = {}) => {
7697
7906
  };
7698
7907
 
7699
7908
  export const parseCmakeLikeFile = (cmakeListFile, pkgType, options = {}) => {
7700
- let cmakeListData = readFileSync(cmakeListFile, "utf-8");
7909
+ let cmakeListData = readFileSync(cmakeListFile, { encoding: "utf-8" });
7701
7910
  const pkgList = [];
7702
7911
  const pkgAddedMap = {};
7703
7912
  const versionSpecifiersMap = {};
7704
7913
  const versionsMap = {};
7705
7914
  let parentComponent = {};
7915
+ const templateValues = {};
7706
7916
  cmakeListData = cmakeListData
7707
7917
  .replace(/^ {2}/g, "")
7708
7918
  .replace(/\(\r\n/g, "(")
@@ -7717,7 +7927,20 @@ export const parseCmakeLikeFile = (cmakeListFile, pkgType, options = {}) => {
7717
7927
  let group = "";
7718
7928
  let path = undefined;
7719
7929
  let name_list = [];
7720
- if (l.startsWith("project") && !Object.keys(parentComponent).length) {
7930
+ if (l.startsWith("set")) {
7931
+ const tmpA = l.replace("set(", "").replace(")", "").trim().split(" ");
7932
+ if (tmpA && tmpA.length === 2) {
7933
+ templateValues[tmpA[0]] = tmpA[1];
7934
+ }
7935
+ } else if (
7936
+ l.startsWith("project") &&
7937
+ !Object.keys(parentComponent).length
7938
+ ) {
7939
+ if (l.includes("${")) {
7940
+ for (const tmplKey of Object.keys(templateValues)) {
7941
+ l = l.replace("${" + tmplKey + "}", templateValues[tmplKey] || "");
7942
+ }
7943
+ }
7721
7944
  const tmpA = l.replace("project (", "project(").split("project(");
7722
7945
  if (tmpA && tmpA.length > 1) {
7723
7946
  const tmpB = (tmpA[1] || "")
@@ -7735,7 +7958,7 @@ export const parseCmakeLikeFile = (cmakeListFile, pkgType, options = {}) => {
7735
7958
  if (versionIndex > -1 && tmpB.length > versionIndex) {
7736
7959
  parentVersion = tmpB[versionIndex + 1];
7737
7960
  }
7738
- if (parentName && parentName.length) {
7961
+ if (parentName && parentName.length && !parentName.includes("$")) {
7739
7962
  parentComponent = {
7740
7963
  group: options.projectGroup || "",
7741
7964
  name: parentName,
@@ -7917,7 +8140,7 @@ export const parseCmakeLikeFile = (cmakeListFile, pkgType, options = {}) => {
7917
8140
  methods: [
7918
8141
  {
7919
8142
  technique: "source-code-analysis",
7920
- confidence: 0,
8143
+ confidence: 0.5,
7921
8144
  value: `Filename ${cmakeListFile}`
7922
8145
  }
7923
8146
  ]
@@ -7951,7 +8174,7 @@ export const getOSPackageForFile = (afile, osPkgsList) => {
7951
8174
  ospkg.evidence = {
7952
8175
  identity: {
7953
8176
  field: "purl",
7954
- confidence: 0,
8177
+ confidence: 0.8,
7955
8178
  methods: [
7956
8179
  {
7957
8180
  technique: "filename",
@@ -7985,47 +8208,122 @@ export const getCppModules = (src, options, osPkgsList, epkgList) => {
7985
8208
  const epkgMap = {};
7986
8209
  let parentComponent = undefined;
7987
8210
  const dependsOn = [];
8211
+ (epkgList || []).forEach((p) => {
8212
+ epkgMap[p.group + "/" + p.name] = p;
8213
+ });
7988
8214
  // Let's look for any vcpkg.json file to tell us about the directory we're scanning
7989
8215
  // users can use this file to give us a clue even if they do not use vcpkg library manager
7990
8216
  if (existsSync(join(src, "vcpkg.json"))) {
7991
- const vcPkgData = JSON.parse(join(src, "vcpkg.json"));
7992
- if (
7993
- vcPkgData &&
7994
- Object.keys(vcPkgData).length &&
7995
- vcPkgData.name &&
7996
- vcPkgData.version
7997
- ) {
8217
+ const vcPkgData = JSON.parse(
8218
+ readFileSync(join(src, "vcpkg.json"), { encoding: "utf-8" })
8219
+ );
8220
+ if (vcPkgData && Object.keys(vcPkgData).length && vcPkgData.name) {
7998
8221
  const parentPurl = new PackageURL(
7999
8222
  pkgType,
8000
8223
  "",
8001
8224
  vcPkgData.name,
8002
- vcPkgData.version,
8225
+ vcPkgData.version || "",
8003
8226
  null,
8004
8227
  null
8005
8228
  ).toString();
8006
8229
  parentComponent = {
8007
8230
  name: vcPkgData.name,
8008
- version: vcPkgData.version,
8231
+ version: vcPkgData.version || "",
8009
8232
  description: vcPkgData.description,
8010
8233
  license: vcPkgData.license,
8011
8234
  purl: parentPurl,
8235
+ type: "application",
8012
8236
  "bom-ref": decodeURIComponent(parentPurl)
8013
8237
  };
8014
8238
  if (vcPkgData.homepage) {
8015
8239
  parentComponent.homepage = { url: vcPkgData.homepage };
8016
8240
  }
8017
- }
8241
+ // Are there any dependencies declared in vcpkg.json
8242
+ if (vcPkgData.dependencies && Array.isArray(vcPkgData.dependencies)) {
8243
+ for (const avcdep of vcPkgData.dependencies) {
8244
+ let avcpkgName = undefined;
8245
+ let scope = undefined;
8246
+ if (typeof avcdep === "string" || avcdep instanceof String) {
8247
+ avcpkgName = avcdep;
8248
+ } else if (Object.keys(avcdep).length && avcdep.name) {
8249
+ avcpkgName = avcdep.name;
8250
+ if (avcdep.host) {
8251
+ scope = "optional";
8252
+ }
8253
+ }
8254
+ // Is this a dependency we haven't seen before including the all lower and upper case version?
8255
+ if (
8256
+ avcpkgName &&
8257
+ !epkgMap["/" + avcpkgName] &&
8258
+ !epkgMap["/" + avcpkgName.toLowerCase()] &&
8259
+ !epkgMap["/" + avcpkgName.toUpperCase()]
8260
+ ) {
8261
+ const pkgPurl = new PackageURL(
8262
+ pkgType,
8263
+ "",
8264
+ avcpkgName,
8265
+ "",
8266
+ null,
8267
+ null
8268
+ ).toString();
8269
+ const apkg = {
8270
+ group: "",
8271
+ name: avcpkgName,
8272
+ type: pkgType,
8273
+ version: "",
8274
+ purl: pkgPurl,
8275
+ scope,
8276
+ "bom-ref": decodeURIComponent(pkgPurl),
8277
+ evidence: {
8278
+ identity: {
8279
+ field: "purl",
8280
+ confidence: 0.5,
8281
+ methods: [
8282
+ {
8283
+ technique: "source-code-analysis",
8284
+ confidence: 0.5,
8285
+ value: `Filename ${join(src, "vcpkg.json")}`
8286
+ }
8287
+ ]
8288
+ }
8289
+ }
8290
+ };
8291
+ if (!pkgAddedMap[avcpkgName]) {
8292
+ pkgList.push(apkg);
8293
+ dependsOn.push(apkg["bom-ref"]);
8294
+ pkgAddedMap[avcpkgName] = true;
8295
+ }
8296
+ }
8297
+ }
8298
+ }
8299
+ } // if
8018
8300
  } else if (existsSync(join(src, "CMakeLists.txt"))) {
8019
8301
  const retMap = parseCmakeLikeFile(join(src, "CMakeLists.txt"), pkgType);
8020
8302
  if (retMap.parentComponent && Object.keys(retMap.parentComponent).length) {
8021
8303
  parentComponent = retMap.parentComponent;
8022
8304
  }
8305
+ } else if (options.projectName && options.projectVersion) {
8306
+ parentComponent = {
8307
+ group: options.projectGroup || "",
8308
+ name: options.projectName || "",
8309
+ version: "" + options.projectVersion || "latest",
8310
+ type: "application"
8311
+ };
8312
+ const parentPurl = new PackageURL(
8313
+ pkgType,
8314
+ parentComponent.group,
8315
+ parentComponent.name,
8316
+ parentComponent.version,
8317
+ null,
8318
+ null
8319
+ ).toString();
8320
+ parentComponent.purl = parentPurl;
8321
+ parentComponent["bom-ref"] = decodeURIComponent(parentPurl);
8023
8322
  }
8024
- (epkgList || []).forEach((p) => {
8025
- epkgMap[p.name] = p;
8026
- });
8027
8323
  if (options.usagesSlicesFile && existsSync(options.usagesSlicesFile)) {
8028
- sliceData = JSON.parse(readFileSync(options.usagesSlicesFile));
8324
+ sliceData = JSON.parse(
8325
+ readFileSync(options.usagesSlicesFile, { encoding: "utf-8" })
8326
+ );
8029
8327
  if (DEBUG_MODE) {
8030
8328
  console.log("Re-using existing slices file", options.usagesSlicesFile);
8031
8329
  }
@@ -8038,7 +8336,9 @@ export const getCppModules = (src, options, osPkgsList, epkgList) => {
8038
8336
  );
8039
8337
  }
8040
8338
  const usageData = parseCUsageSlice(sliceData);
8041
- for (const afile of Object.keys(usageData)) {
8339
+ for (let afile of Object.keys(usageData)) {
8340
+ // Normalize windows separator
8341
+ afile = afile.replace("..\\", "").replace(/\\/g, "/");
8042
8342
  let fileName = basename(afile);
8043
8343
  if (!fileName || !fileName.length) {
8044
8344
  continue;
@@ -8057,14 +8357,14 @@ export const getCppModules = (src, options, osPkgsList, epkgList) => {
8057
8357
  // We need to resolve the name to an os package here
8058
8358
  let name = fileName.replace(extn, "");
8059
8359
  let apkg = getOSPackageForFile(afile, osPkgsList) ||
8060
- epkgMap[name] || {
8360
+ epkgMap[group + "/" + name] || {
8061
8361
  name,
8062
8362
  group,
8063
8363
  version: "",
8064
8364
  type: pkgType
8065
8365
  };
8066
8366
  // If this is a relative file, there is a good chance we can reuse the project group
8067
- if (!afile.startsWith(_sep)) {
8367
+ if (!afile.startsWith(_sep) && !group.length) {
8068
8368
  group = options.projectGroup || "";
8069
8369
  }
8070
8370
  if (!apkg.purl) {
@@ -8092,9 +8392,16 @@ export const getCppModules = (src, options, osPkgsList, epkgList) => {
8092
8392
  apkg["bom-ref"] = decodeURIComponent(apkg["purl"]);
8093
8393
  }
8094
8394
  if (usageData[afile]) {
8095
- const usymbols = Array.from(usageData[afile]).filter(
8096
- (v) => !v.startsWith("<") && !v.startsWith("__")
8097
- );
8395
+ const usymbols = Array.from(usageData[afile])
8396
+ .filter(
8397
+ (v) =>
8398
+ !v.startsWith("<") &&
8399
+ !v.startsWith("__") &&
8400
+ v !== "main" &&
8401
+ !v.includes("anonymous_") &&
8402
+ !v.includes(afile)
8403
+ )
8404
+ .sort();
8098
8405
  if (!apkg["properties"] && usymbols.length) {
8099
8406
  apkg["properties"] = [
8100
8407
  { name: "ImportedSymbols", value: usymbols.join(", ") }
@@ -8141,7 +8448,9 @@ export const getCppModules = (src, options, osPkgsList, epkgList) => {
8141
8448
  : [];
8142
8449
  return {
8143
8450
  parentComponent,
8144
- pkgList,
8451
+ pkgList: pkgList.sort(function (a, b) {
8452
+ return a.purl.localeCompare(b.purl);
8453
+ }),
8145
8454
  dependenciesList
8146
8455
  };
8147
8456
  };
@@ -8174,34 +8483,21 @@ export const parseCUsageSlice = (sliceData) => {
8174
8483
  continue;
8175
8484
  }
8176
8485
  const slFileName = slice.fileName;
8177
- const slLineNumber = slice.lineNumber || 0;
8178
8486
  const allLines = usageData[slFileName] || new Set();
8179
8487
  if (slice.fullName && slice.fullName.length > 3) {
8180
- allLines.add(slice.fullName + "|" + slLineNumber);
8181
8488
  if (slice.code && slice.code.startsWith("#include")) {
8182
8489
  usageData[slice.fullName] = new Set();
8490
+ } else {
8491
+ allLines.add(slice.fullName);
8183
8492
  }
8184
8493
  }
8185
8494
  for (const ausage of slice.usages) {
8186
- if (ausage.targetObj.resolvedMethod) {
8187
- allLines.add(ausage.targetObj.resolvedMethod + "|" + slLineNumber);
8188
- } else {
8189
- const targetObjName = ausage.targetObj.name.replace(/\n/g, " ");
8190
- // We need to still filter out <global>, <clinit> style targets
8191
- if (
8192
- ausage.targetObj.lineNumber === slLineNumber ||
8193
- targetObjName.startsWith("<") ||
8194
- (slice.fullName.length > 3 &&
8195
- targetObjName.includes(slice.fullName))
8196
- ) {
8197
- continue;
8198
- }
8199
- allLines.add(targetObjName + "|" + slLineNumber);
8200
- }
8201
8495
  let calls = ausage?.invokedCalls || [];
8202
8496
  calls = calls.concat(ausage?.argToCalls || []);
8203
8497
  for (const acall of calls) {
8204
- allLines.add(acall.resolvedMethod + "|" + slLineNumber);
8498
+ if (!acall.resolvedMethod.includes("->")) {
8499
+ allLines.add(acall.resolvedMethod);
8500
+ }
8205
8501
  }
8206
8502
  }
8207
8503
  if (Array.from(allLines).length) {
@@ -8393,6 +8689,13 @@ export const getNugetMetadata = async function (
8393
8689
  p.license = findLicenseId(body.catalogEntry.licenseExpression);
8394
8690
  } else if (body.catalogEntry.licenseUrl) {
8395
8691
  p.license = findLicenseId(body.catalogEntry.licenseUrl);
8692
+ if (
8693
+ typeof p.license === "string" &&
8694
+ p.license.includes("://github.com/")
8695
+ ) {
8696
+ p.license =
8697
+ (await getRepoLicense(p.license, undefined)) || p.license;
8698
+ }
8396
8699
  }
8397
8700
  if (body.catalogEntry.projectUrl) {
8398
8701
  p.repository = { url: body.catalogEntry.projectUrl };
@@ -8404,6 +8707,17 @@ export const getNugetMetadata = async function (
8404
8707
  p.version +
8405
8708
  "/"
8406
8709
  };
8710
+ if (
8711
+ (!p.license || typeof p.license === "string") &&
8712
+ typeof p.repository.url === "string" &&
8713
+ p.repository.url.includes("://github.com/")
8714
+ ) {
8715
+ // license couldn't be properly identified and is still a url,
8716
+ // therefore trying to resolve license via repository
8717
+ p.license =
8718
+ (await getRepoLicense(p.repository.url, undefined)) ||
8719
+ p.license;
8720
+ }
8407
8721
  }
8408
8722
  cdepList.push(p);
8409
8723
  }