@cyclonedx/cdxgen 12.1.5 → 12.2.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.
- package/README.md +51 -40
- package/bin/cdxgen.js +194 -97
- package/bin/evinse.js +4 -4
- package/bin/repl.js +1 -1
- package/bin/sign.js +102 -0
- package/bin/validate.js +233 -0
- package/bin/verify.js +69 -28
- package/data/queries.json +1 -1
- package/data/rules/ci-permissions.yaml +186 -0
- package/data/rules/dependency-sources.yaml +123 -0
- package/data/rules/package-integrity.yaml +135 -0
- package/data/rules/vscode-extensions.yaml +228 -0
- package/lib/cli/index.js +449 -429
- package/lib/cli/index.poku.js +117 -0
- package/lib/evinser/db.js +137 -0
- package/lib/{helpers → evinser}/db.poku.js +2 -6
- package/lib/evinser/evinser.js +2 -14
- package/lib/helpers/analyzer.js +606 -3
- package/lib/helpers/analyzer.poku.js +230 -0
- package/lib/helpers/bomSigner.js +312 -0
- package/lib/helpers/bomSigner.poku.js +156 -0
- package/lib/helpers/ciParsers/azurePipelines.js +295 -0
- package/lib/helpers/ciParsers/azurePipelines.poku.js +253 -0
- package/lib/helpers/ciParsers/circleCi.js +286 -0
- package/lib/helpers/ciParsers/circleCi.poku.js +230 -0
- package/lib/helpers/ciParsers/common.js +24 -0
- package/lib/helpers/ciParsers/githubActions.js +636 -0
- package/lib/helpers/ciParsers/githubActions.poku.js +802 -0
- package/lib/helpers/ciParsers/gitlabCi.js +213 -0
- package/lib/helpers/ciParsers/gitlabCi.poku.js +247 -0
- package/lib/helpers/ciParsers/jenkins.js +181 -0
- package/lib/helpers/ciParsers/jenkins.poku.js +197 -0
- package/lib/helpers/depsUtils.js +219 -0
- package/lib/helpers/depsUtils.poku.js +207 -0
- package/lib/helpers/display.js +426 -5
- package/lib/helpers/envcontext.js +18 -3
- package/lib/helpers/formulationParsers.js +351 -0
- package/lib/helpers/logger.js +14 -0
- package/lib/helpers/protobom.js +9 -9
- package/lib/helpers/pythonutils.js +9 -0
- package/lib/helpers/remote/dependency-track.js +84 -0
- package/lib/helpers/remote/dependency-track.poku.js +119 -0
- package/lib/helpers/table.js +384 -0
- package/lib/helpers/table.poku.js +186 -0
- package/lib/helpers/utils.js +865 -416
- package/lib/helpers/utils.poku.js +172 -265
- package/lib/helpers/versutils.js +202 -0
- package/lib/helpers/versutils.poku.js +315 -0
- package/lib/helpers/vsixutils.js +1061 -0
- package/lib/helpers/vsixutils.poku.js +2247 -0
- package/lib/managers/binary.js +19 -19
- package/lib/managers/docker.js +108 -1
- package/lib/managers/oci.js +10 -0
- package/lib/managers/piptree.js +3 -9
- package/lib/parsers/npmrc.js +17 -13
- package/lib/parsers/npmrc.poku.js +41 -5
- package/lib/server/openapi.yaml +34 -1
- package/lib/server/server.js +50 -13
- package/lib/server/server.poku.js +332 -144
- package/lib/stages/postgen/annotator.js +1 -1
- package/lib/stages/postgen/auditBom.js +196 -0
- package/lib/stages/postgen/auditBom.poku.js +378 -0
- package/lib/stages/postgen/postgen.js +54 -1
- package/lib/stages/postgen/postgen.poku.js +90 -1
- package/lib/stages/postgen/ruleEngine.js +369 -0
- package/lib/stages/pregen/envAudit.js +299 -0
- package/lib/stages/pregen/envAudit.poku.js +572 -0
- package/lib/stages/pregen/pregen.js +12 -8
- package/lib/{helpers/validator.js → validator/bomValidator.js} +107 -47
- package/lib/validator/complianceEngine.js +241 -0
- package/lib/validator/complianceEngine.poku.js +168 -0
- package/lib/validator/complianceRules.js +1610 -0
- package/lib/validator/complianceRules.poku.js +328 -0
- package/lib/validator/index.js +222 -0
- package/lib/validator/index.poku.js +144 -0
- package/lib/validator/reporters/annotations.js +121 -0
- package/lib/validator/reporters/console.js +149 -0
- package/lib/validator/reporters/index.js +41 -0
- package/lib/validator/reporters/json.js +37 -0
- package/lib/validator/reporters/sarif.js +184 -0
- package/lib/validator/reporters.poku.js +150 -0
- package/package.json +8 -9
- package/types/bin/sign.d.ts +3 -0
- package/types/bin/sign.d.ts.map +1 -0
- package/types/bin/validate.d.ts +3 -0
- package/types/bin/validate.d.ts.map +1 -0
- package/types/helpers/utils.d.ts +0 -1
- package/types/lib/cli/index.d.ts +49 -52
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/evinser/db.d.ts +34 -0
- package/types/lib/evinser/db.d.ts.map +1 -0
- package/types/lib/evinser/evinser.d.ts +63 -16
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/helpers/analyzer.d.ts.map +1 -1
- package/types/lib/helpers/bomSigner.d.ts +27 -0
- package/types/lib/helpers/bomSigner.d.ts.map +1 -0
- package/types/lib/helpers/ciParsers/azurePipelines.d.ts +17 -0
- package/types/lib/helpers/ciParsers/azurePipelines.d.ts.map +1 -0
- package/types/lib/helpers/ciParsers/circleCi.d.ts +17 -0
- package/types/lib/helpers/ciParsers/circleCi.d.ts.map +1 -0
- package/types/lib/helpers/ciParsers/common.d.ts +11 -0
- package/types/lib/helpers/ciParsers/common.d.ts.map +1 -0
- package/types/lib/helpers/ciParsers/githubActions.d.ts +34 -0
- package/types/lib/helpers/ciParsers/githubActions.d.ts.map +1 -0
- package/types/lib/helpers/ciParsers/gitlabCi.d.ts +17 -0
- package/types/lib/helpers/ciParsers/gitlabCi.d.ts.map +1 -0
- package/types/lib/helpers/ciParsers/jenkins.d.ts +17 -0
- package/types/lib/helpers/ciParsers/jenkins.d.ts.map +1 -0
- package/types/lib/helpers/depsUtils.d.ts +21 -0
- package/types/lib/helpers/depsUtils.d.ts.map +1 -0
- package/types/lib/helpers/display.d.ts +111 -11
- package/types/lib/helpers/display.d.ts.map +1 -1
- package/types/lib/helpers/envcontext.d.ts +19 -7
- package/types/lib/helpers/envcontext.d.ts.map +1 -1
- package/types/lib/helpers/formulationParsers.d.ts +50 -0
- package/types/lib/helpers/formulationParsers.d.ts.map +1 -0
- package/types/lib/helpers/logger.d.ts +15 -1
- package/types/lib/helpers/logger.d.ts.map +1 -1
- package/types/lib/helpers/protobom.d.ts +2 -2
- package/types/lib/helpers/pythonutils.d.ts +10 -1
- package/types/lib/helpers/pythonutils.d.ts.map +1 -1
- package/types/lib/helpers/remote/dependency-track.d.ts +16 -0
- package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -0
- package/types/lib/helpers/table.d.ts +6 -0
- package/types/lib/helpers/table.d.ts.map +1 -0
- package/types/lib/helpers/utils.d.ts +533 -128
- package/types/lib/helpers/utils.d.ts.map +1 -1
- package/types/lib/helpers/versutils.d.ts +8 -0
- package/types/lib/helpers/versutils.d.ts.map +1 -0
- package/types/lib/helpers/vsixutils.d.ts +130 -0
- package/types/lib/helpers/vsixutils.d.ts.map +1 -0
- package/types/lib/managers/docker.d.ts +12 -31
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts +11 -1
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/managers/piptree.d.ts.map +1 -1
- package/types/lib/parsers/npmrc.d.ts +4 -1
- package/types/lib/parsers/npmrc.d.ts.map +1 -1
- package/types/lib/server/server.d.ts +22 -2
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/auditBom.d.ts +20 -0
- package/types/lib/stages/postgen/auditBom.d.ts.map +1 -0
- package/types/lib/stages/postgen/postgen.d.ts +8 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts +18 -0
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -0
- package/types/lib/stages/pregen/envAudit.d.ts +8 -0
- package/types/lib/stages/pregen/envAudit.d.ts.map +1 -0
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
- package/types/lib/{helpers/validator.d.ts → validator/bomValidator.d.ts} +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -0
- package/types/lib/validator/complianceEngine.d.ts +66 -0
- package/types/lib/validator/complianceEngine.d.ts.map +1 -0
- package/types/lib/validator/complianceRules.d.ts +70 -0
- package/types/lib/validator/complianceRules.d.ts.map +1 -0
- package/types/lib/validator/index.d.ts +70 -0
- package/types/lib/validator/index.d.ts.map +1 -0
- package/types/lib/validator/reporters/annotations.d.ts +31 -0
- package/types/lib/validator/reporters/annotations.d.ts.map +1 -0
- package/types/lib/validator/reporters/console.d.ts +30 -0
- package/types/lib/validator/reporters/console.d.ts.map +1 -0
- package/types/lib/validator/reporters/index.d.ts +21 -0
- package/types/lib/validator/reporters/index.d.ts.map +1 -0
- package/types/lib/validator/reporters/json.d.ts +11 -0
- package/types/lib/validator/reporters/json.d.ts.map +1 -0
- package/types/lib/validator/reporters/sarif.d.ts +16 -0
- package/types/lib/validator/reporters/sarif.d.ts.map +1 -0
- package/lib/helpers/db.js +0 -162
- package/lib/stages/pregen/env-audit.js +0 -34
- package/lib/stages/pregen/env-audit.poku.js +0 -290
- package/types/helpers/db.d.ts +0 -35
- package/types/helpers/db.d.ts.map +0 -1
- package/types/lib/helpers/db.d.ts +0 -35
- package/types/lib/helpers/db.d.ts.map +0 -1
- package/types/lib/helpers/validator.d.ts.map +0 -1
- package/types/lib/stages/pregen/env-audit.d.ts +0 -2
- package/types/lib/stages/pregen/env-audit.d.ts.map +0 -1
- package/types/managers/binary.d.ts +0 -37
- package/types/managers/binary.d.ts.map +0 -1
- package/types/managers/docker.d.ts +0 -56
- package/types/managers/docker.d.ts.map +0 -1
- package/types/managers/oci.d.ts +0 -2
- package/types/managers/oci.d.ts.map +0 -1
- package/types/managers/piptree.d.ts +0 -2
- package/types/managers/piptree.d.ts.map +0 -1
- package/types/server/server.d.ts +0 -34
- package/types/server/server.d.ts.map +0 -1
- package/types/stages/postgen/annotator.d.ts +0 -27
- package/types/stages/postgen/annotator.d.ts.map +0 -1
- package/types/stages/postgen/postgen.d.ts +0 -51
- package/types/stages/postgen/postgen.d.ts.map +0 -1
- package/types/stages/pregen/pregen.d.ts +0 -59
- package/types/stages/pregen/pregen.d.ts.map +0 -1
package/lib/cli/index.js
CHANGED
|
@@ -2,9 +2,9 @@ import { Buffer } from "node:buffer";
|
|
|
2
2
|
import {
|
|
3
3
|
accessSync,
|
|
4
4
|
constants,
|
|
5
|
-
existsSync,
|
|
6
5
|
lstatSync,
|
|
7
6
|
mkdtempSync,
|
|
7
|
+
readdirSync,
|
|
8
8
|
readFileSync,
|
|
9
9
|
rmSync,
|
|
10
10
|
statSync,
|
|
@@ -19,22 +19,19 @@ import got from "got";
|
|
|
19
19
|
import { PackageURL } from "packageurl-js";
|
|
20
20
|
import { gte, lte } from "semver";
|
|
21
21
|
import { parse } from "ssri";
|
|
22
|
-
import { table } from "table";
|
|
23
22
|
import { v4 as uuidv4 } from "uuid";
|
|
24
23
|
import { parse as loadYaml } from "yaml";
|
|
25
24
|
|
|
26
25
|
import { findJSImportsExports } from "../helpers/analyzer.js";
|
|
27
26
|
import { parseCaxaMetadata } from "../helpers/caxa.js";
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
collectEnvInfo,
|
|
31
|
-
GIT_COMMAND,
|
|
32
|
-
getBranch,
|
|
33
|
-
getOriginUrl,
|
|
34
|
-
gitTreeHashes,
|
|
35
|
-
listFiles,
|
|
36
|
-
} from "../helpers/envcontext.js";
|
|
27
|
+
import { mergeDependencies, trimComponents } from "../helpers/depsUtils.js";
|
|
28
|
+
import { GIT_COMMAND } from "../helpers/envcontext.js";
|
|
37
29
|
import { thoughtLog } from "../helpers/logger.js";
|
|
30
|
+
import {
|
|
31
|
+
buildDependencyTrackBomPayload,
|
|
32
|
+
getDependencyTrackBomUrl,
|
|
33
|
+
} from "../helpers/remote/dependency-track.js";
|
|
34
|
+
import { table } from "../helpers/table.js";
|
|
38
35
|
import {
|
|
39
36
|
addEvidenceForDotnet,
|
|
40
37
|
addEvidenceForImports,
|
|
@@ -176,6 +173,14 @@ import {
|
|
|
176
173
|
shouldFetchLicense,
|
|
177
174
|
splitOutputByGradleProjects,
|
|
178
175
|
} from "../helpers/utils.js";
|
|
176
|
+
import {
|
|
177
|
+
cleanupTempDir,
|
|
178
|
+
collectInstalledExtensions,
|
|
179
|
+
discoverIdeExtensionDirs,
|
|
180
|
+
extractVsixToTempDir,
|
|
181
|
+
parseVsixFile,
|
|
182
|
+
VSCODE_EXTENSION_PURL_TYPE,
|
|
183
|
+
} from "../helpers/vsixutils.js";
|
|
179
184
|
import {
|
|
180
185
|
executeOsQuery,
|
|
181
186
|
getBinaryBom,
|
|
@@ -438,160 +443,6 @@ const addLifecyclesSection = (options) => {
|
|
|
438
443
|
return lifecycles;
|
|
439
444
|
};
|
|
440
445
|
|
|
441
|
-
/**
|
|
442
|
-
* Method to generate the formulation section based on git metadata
|
|
443
|
-
*
|
|
444
|
-
* @param {Object} options
|
|
445
|
-
* @param {Object} context Context
|
|
446
|
-
* @returns {Array} formulation array
|
|
447
|
-
*/
|
|
448
|
-
const addFormulationSection = (options, context) => {
|
|
449
|
-
const formulation = [];
|
|
450
|
-
const provides = [];
|
|
451
|
-
const gitBranch = getBranch();
|
|
452
|
-
const originUrl = getOriginUrl();
|
|
453
|
-
const gitFiles = listFiles();
|
|
454
|
-
const treeHashes = gitTreeHashes();
|
|
455
|
-
let parentOmniborId;
|
|
456
|
-
let treeOmniborId;
|
|
457
|
-
let components = [];
|
|
458
|
-
const aformulation = {};
|
|
459
|
-
// Reuse any existing formulation components
|
|
460
|
-
// See: PR #1172
|
|
461
|
-
if (context?.formulationList?.length) {
|
|
462
|
-
components = components.concat(trimComponents(context.formulationList));
|
|
463
|
-
}
|
|
464
|
-
if (options.specVersion >= 1.6 && Object.keys(treeHashes).length === 2) {
|
|
465
|
-
parentOmniborId = `gitoid:blob:sha1:${treeHashes.parent}`;
|
|
466
|
-
treeOmniborId = `gitoid:blob:sha1:${treeHashes.tree}`;
|
|
467
|
-
components.push({
|
|
468
|
-
type: "file",
|
|
469
|
-
name: "git-parent",
|
|
470
|
-
description: "Git Parent Node.",
|
|
471
|
-
"bom-ref": parentOmniborId,
|
|
472
|
-
omniborId: [parentOmniborId],
|
|
473
|
-
swhid: [`swh:1:rev:${treeHashes.parent}`],
|
|
474
|
-
});
|
|
475
|
-
components.push({
|
|
476
|
-
type: "file",
|
|
477
|
-
name: "git-tree",
|
|
478
|
-
description: "Git Tree Node.",
|
|
479
|
-
"bom-ref": treeOmniborId,
|
|
480
|
-
omniborId: [treeOmniborId],
|
|
481
|
-
swhid: [`swh:1:rev:${treeHashes.tree}`],
|
|
482
|
-
});
|
|
483
|
-
provides.push({
|
|
484
|
-
ref: parentOmniborId,
|
|
485
|
-
provides: [treeOmniborId],
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
// Collect git related components
|
|
489
|
-
if (gitBranch && gitFiles) {
|
|
490
|
-
const gitFileComponents = gitFiles.map((f) =>
|
|
491
|
-
options.specVersion >= 1.6
|
|
492
|
-
? {
|
|
493
|
-
type: "file",
|
|
494
|
-
name: f.name,
|
|
495
|
-
version: f.hash,
|
|
496
|
-
omniborId: [f.omniborId],
|
|
497
|
-
swhid: [f.swhid],
|
|
498
|
-
}
|
|
499
|
-
: {
|
|
500
|
-
type: "file",
|
|
501
|
-
name: f.name,
|
|
502
|
-
version: f.hash,
|
|
503
|
-
},
|
|
504
|
-
);
|
|
505
|
-
components = components.concat(gitFileComponents);
|
|
506
|
-
// Complete the Artifact Dependency Graph
|
|
507
|
-
if (options.specVersion >= 1.6 && treeOmniborId) {
|
|
508
|
-
provides.push({
|
|
509
|
-
ref: treeOmniborId,
|
|
510
|
-
provides: gitFiles.map((f) => f.ref),
|
|
511
|
-
});
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
// Collect build environment details
|
|
515
|
-
const infoComponents = collectEnvInfo(options.path);
|
|
516
|
-
if (infoComponents?.length) {
|
|
517
|
-
components = components.concat(infoComponents);
|
|
518
|
-
}
|
|
519
|
-
// Should we include the OS crypto libraries
|
|
520
|
-
if (options.includeCrypto) {
|
|
521
|
-
const cryptoLibs = collectOSCryptoLibs(options);
|
|
522
|
-
if (cryptoLibs?.length) {
|
|
523
|
-
components = components.concat(cryptoLibs);
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
aformulation["bom-ref"] = uuidv4();
|
|
527
|
-
aformulation.components = trimComponents(components);
|
|
528
|
-
let environmentVars = gitBranch?.length
|
|
529
|
-
? [{ name: "GIT_BRANCH", value: gitBranch }]
|
|
530
|
-
: [];
|
|
531
|
-
const envPrefixes = [
|
|
532
|
-
"GIT",
|
|
533
|
-
"ANDROID",
|
|
534
|
-
"DENO",
|
|
535
|
-
"DOTNET",
|
|
536
|
-
"JAVA_",
|
|
537
|
-
"SDKMAN",
|
|
538
|
-
"CARGO",
|
|
539
|
-
"CONDA",
|
|
540
|
-
"RUST",
|
|
541
|
-
"GEM_",
|
|
542
|
-
"SCALA_",
|
|
543
|
-
"MAVEN_",
|
|
544
|
-
"GRADLE_",
|
|
545
|
-
"NODE_",
|
|
546
|
-
];
|
|
547
|
-
const envBlocklist = [
|
|
548
|
-
"key",
|
|
549
|
-
"token",
|
|
550
|
-
"pass",
|
|
551
|
-
"secret",
|
|
552
|
-
"user",
|
|
553
|
-
"email",
|
|
554
|
-
"auth",
|
|
555
|
-
"session",
|
|
556
|
-
"proxy",
|
|
557
|
-
"cred",
|
|
558
|
-
];
|
|
559
|
-
|
|
560
|
-
for (const aevar of Object.keys(process.env)) {
|
|
561
|
-
const lower = aevar.toLowerCase();
|
|
562
|
-
const value = process.env[aevar] ?? "";
|
|
563
|
-
if (
|
|
564
|
-
envPrefixes.some((p) => aevar.startsWith(p)) &&
|
|
565
|
-
!envBlocklist.some((b) => lower.includes(b)) &&
|
|
566
|
-
!envBlocklist.some((b) => value.includes(b)) &&
|
|
567
|
-
value.length
|
|
568
|
-
) {
|
|
569
|
-
environmentVars.push({
|
|
570
|
-
name: aevar,
|
|
571
|
-
value,
|
|
572
|
-
});
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
if (!environmentVars.length) {
|
|
576
|
-
environmentVars = undefined;
|
|
577
|
-
}
|
|
578
|
-
let sourceInput;
|
|
579
|
-
if (environmentVars) {
|
|
580
|
-
sourceInput = { environmentVars };
|
|
581
|
-
}
|
|
582
|
-
const sourceWorkflow = {
|
|
583
|
-
"bom-ref": uuidv4(),
|
|
584
|
-
uid: uuidv4(),
|
|
585
|
-
taskTypes: originUrl ? ["build", "clone"] : ["build"],
|
|
586
|
-
};
|
|
587
|
-
if (sourceInput) {
|
|
588
|
-
sourceWorkflow.inputs = [sourceInput];
|
|
589
|
-
}
|
|
590
|
-
aformulation.workflows = [sourceWorkflow];
|
|
591
|
-
formulation.push(aformulation);
|
|
592
|
-
return { formulation, provides };
|
|
593
|
-
};
|
|
594
|
-
|
|
595
446
|
/**
|
|
596
447
|
* Function to create metadata block
|
|
597
448
|
*
|
|
@@ -1000,6 +851,7 @@ function addExternalReferences(opkg) {
|
|
|
1000
851
|
* @param {Object} allImports All imports
|
|
1001
852
|
* @param {Object} pkg Package object
|
|
1002
853
|
* @param {string} ptype Package type
|
|
854
|
+
* @returns {Object[]} Array of component objects
|
|
1003
855
|
*/
|
|
1004
856
|
export function listComponents(options, allImports, pkg, ptype = "npm") {
|
|
1005
857
|
const compMap = {};
|
|
@@ -1067,6 +919,15 @@ function addComponent(
|
|
|
1067
919
|
purl = undefined;
|
|
1068
920
|
purlString = undefined;
|
|
1069
921
|
}
|
|
922
|
+
// Some applications like github workflow steps and commands do not have purl
|
|
923
|
+
if (
|
|
924
|
+
pkg.purl === undefined &&
|
|
925
|
+
!pkg?.["bom-ref"]?.startsWith("pkg:") &&
|
|
926
|
+
pkg?.type === "application"
|
|
927
|
+
) {
|
|
928
|
+
purl = undefined;
|
|
929
|
+
purlString = undefined;
|
|
930
|
+
}
|
|
1070
931
|
const description = pkg.description || undefined;
|
|
1071
932
|
let compScope = pkg.scope;
|
|
1072
933
|
if (allImports) {
|
|
@@ -1106,7 +967,12 @@ function addComponent(
|
|
|
1106
967
|
component.data = pkg.data || undefined;
|
|
1107
968
|
}
|
|
1108
969
|
component["type"] = determinePackageType(pkg);
|
|
1109
|
-
|
|
970
|
+
if (purlString) {
|
|
971
|
+
component["bom-ref"] = decodeURIComponent(purlString);
|
|
972
|
+
} else if (pkg["bom-ref"]) {
|
|
973
|
+
component["bom-ref"] = pkg["bom-ref"];
|
|
974
|
+
}
|
|
975
|
+
|
|
1110
976
|
if (
|
|
1111
977
|
component.externalReferences === undefined ||
|
|
1112
978
|
component.externalReferences.length === 0
|
|
@@ -1136,6 +1002,18 @@ function addComponent(
|
|
|
1136
1002
|
}
|
|
1137
1003
|
delete component.authors;
|
|
1138
1004
|
}
|
|
1005
|
+
// Downgrade from 1.7
|
|
1006
|
+
if (options.specVersion < 1.7) {
|
|
1007
|
+
if (component.isExternal) {
|
|
1008
|
+
delete component.isExternal;
|
|
1009
|
+
}
|
|
1010
|
+
if (component.versionRange) {
|
|
1011
|
+
console.warn(
|
|
1012
|
+
`Version Range is not supported in ${options.specVersion} specifications. Please run cdxgen with --spec-version 1.7`,
|
|
1013
|
+
);
|
|
1014
|
+
delete component.versionRange;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1139
1017
|
// Retain any tags
|
|
1140
1018
|
if (
|
|
1141
1019
|
options.specVersion >= 1.6 &&
|
|
@@ -1155,12 +1033,10 @@ function addComponent(
|
|
|
1155
1033
|
if (pkg.components) {
|
|
1156
1034
|
component.components = pkg.components;
|
|
1157
1035
|
}
|
|
1036
|
+
const compMapKey = component.purl || component["bom-ref"];
|
|
1158
1037
|
// Issue: 1353. We need to keep merging the properties
|
|
1159
|
-
if (compMap[
|
|
1160
|
-
const mergedComponents = trimComponents([
|
|
1161
|
-
compMap[component.purl],
|
|
1162
|
-
component,
|
|
1163
|
-
]);
|
|
1038
|
+
if (compMap[compMapKey]) {
|
|
1039
|
+
const mergedComponents = trimComponents([compMap[compMapKey], component]);
|
|
1164
1040
|
if (mergedComponents?.length === 1) {
|
|
1165
1041
|
component = mergedComponents[0];
|
|
1166
1042
|
}
|
|
@@ -1196,7 +1072,7 @@ function addComponent(
|
|
|
1196
1072
|
component.evidence.identity = pkg.evidence.identity[0];
|
|
1197
1073
|
}
|
|
1198
1074
|
}
|
|
1199
|
-
compMap[
|
|
1075
|
+
compMap[compMapKey] = component;
|
|
1200
1076
|
}
|
|
1201
1077
|
if (pkg.dependencies) {
|
|
1202
1078
|
Object.keys(pkg.dependencies)
|
|
@@ -1380,24 +1256,22 @@ const buildBomNSData = (options, pkgInfo, ptype, context) => {
|
|
|
1380
1256
|
// CycloneDX Json Template
|
|
1381
1257
|
const jsonTpl = {
|
|
1382
1258
|
bomFormat: "CycloneDX",
|
|
1383
|
-
specVersion: `${options.specVersion || "1.
|
|
1259
|
+
specVersion: `${options.specVersion || "1.7"}`,
|
|
1384
1260
|
serialNumber: serialNum,
|
|
1385
1261
|
version: 1,
|
|
1386
1262
|
metadata: metadata,
|
|
1387
1263
|
components,
|
|
1388
1264
|
dependencies,
|
|
1389
1265
|
};
|
|
1390
|
-
const formulationData =
|
|
1391
|
-
options.includeFormulation && options.specVersion >= 1.5
|
|
1392
|
-
? addFormulationSection(options, context)
|
|
1393
|
-
: undefined;
|
|
1394
|
-
if (formulationData) {
|
|
1395
|
-
jsonTpl.formulation = formulationData.formulation;
|
|
1396
|
-
}
|
|
1397
1266
|
bomNSData.bomJson = jsonTpl;
|
|
1398
1267
|
bomNSData.nsMapping = nsMapping;
|
|
1399
1268
|
bomNSData.dependencies = dependencies;
|
|
1400
1269
|
bomNSData.parentComponent = parentComponent;
|
|
1270
|
+
// Carry language-specific formulation data (e.g. Pixi) so that
|
|
1271
|
+
// postProcess can merge it when building the final formulation section.
|
|
1272
|
+
if (context?.formulationList?.length) {
|
|
1273
|
+
bomNSData.formulationList = context.formulationList;
|
|
1274
|
+
}
|
|
1401
1275
|
}
|
|
1402
1276
|
return bomNSData;
|
|
1403
1277
|
};
|
|
@@ -1489,6 +1363,7 @@ export async function createJarBom(path, options) {
|
|
|
1489
1363
|
*
|
|
1490
1364
|
* @param {string} path to the project
|
|
1491
1365
|
* @param {Object} options Parse options from the cli
|
|
1366
|
+
* @returns {Object|undefined} BOM object
|
|
1492
1367
|
*/
|
|
1493
1368
|
export function createAndroidBom(path, options) {
|
|
1494
1369
|
return createBinaryBom(path, options);
|
|
@@ -1499,6 +1374,7 @@ export function createAndroidBom(path, options) {
|
|
|
1499
1374
|
*
|
|
1500
1375
|
* @param {string} path to the project
|
|
1501
1376
|
* @param {Object} options Parse options from the cli
|
|
1377
|
+
* @returns {Object|undefined} BOM object
|
|
1502
1378
|
*/
|
|
1503
1379
|
export function createBinaryBom(path, options) {
|
|
1504
1380
|
const tempDir = mkdtempSync(join(getTmpDir(), "blint-tmp-"));
|
|
@@ -1522,6 +1398,7 @@ export function createBinaryBom(path, options) {
|
|
|
1522
1398
|
*
|
|
1523
1399
|
* @param {string} path to the project
|
|
1524
1400
|
* @param {Object} options Parse options from the cli
|
|
1401
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
1525
1402
|
*/
|
|
1526
1403
|
export async function createJavaBom(path, options) {
|
|
1527
1404
|
let jarNSMapping = {};
|
|
@@ -1617,6 +1494,8 @@ export async function createJavaBom(path, options) {
|
|
|
1617
1494
|
}
|
|
1618
1495
|
let result;
|
|
1619
1496
|
let mvnArgs;
|
|
1497
|
+
// FIXME: How do we motivate everyone to upgrade to 1.7?
|
|
1498
|
+
const toolsSpecVersion = 1.6;
|
|
1620
1499
|
if (isQuarkus) {
|
|
1621
1500
|
thoughtLog(
|
|
1622
1501
|
"This appears to be a Quarkus project. Let's use the right Maven plugin.",
|
|
@@ -1627,12 +1506,14 @@ export async function createJavaBom(path, options) {
|
|
|
1627
1506
|
"quarkus:dependency-sbom",
|
|
1628
1507
|
"-Dquarkus.analytics.disabled=true",
|
|
1629
1508
|
];
|
|
1630
|
-
if (options.specVersion) {
|
|
1509
|
+
if (options.specVersion >= 1.6) {
|
|
1631
1510
|
mvnArgs = mvnArgs.concat(
|
|
1632
|
-
`-Dquarkus.dependency.sbom.schema-version=${
|
|
1511
|
+
`-Dquarkus.dependency.sbom.schema-version=${toolsSpecVersion}`,
|
|
1633
1512
|
);
|
|
1634
1513
|
}
|
|
1635
1514
|
} else {
|
|
1515
|
+
// FIXME: The last maven plugin release was on November 28th, 2024.
|
|
1516
|
+
// Should we fork this repo and maintain it ourselves?
|
|
1636
1517
|
const cdxMavenPlugin =
|
|
1637
1518
|
process.env.CDX_MAVEN_PLUGIN ||
|
|
1638
1519
|
"org.cyclonedx:cyclonedx-maven-plugin:2.9.1";
|
|
@@ -1654,9 +1535,11 @@ export async function createJavaBom(path, options) {
|
|
|
1654
1535
|
const addArgs = process.env.MVN_ARGS.split(" ");
|
|
1655
1536
|
mvnArgs = mvnArgs.concat(addArgs);
|
|
1656
1537
|
}
|
|
1657
|
-
// specVersion 1.4 doesn't support externalReferences.type=
|
|
1538
|
+
// specVersion 1.4 doesn't support externalReferences.type=distribution-intake
|
|
1658
1539
|
// so we need to run the plugin with the correct version
|
|
1659
|
-
if (options.specVersion) {
|
|
1540
|
+
if (options.specVersion >= 1.6) {
|
|
1541
|
+
mvnArgs = mvnArgs.concat(`-DschemaVersion=${toolsSpecVersion}`);
|
|
1542
|
+
} else if (options.specVersion > 1.4) {
|
|
1660
1543
|
mvnArgs = mvnArgs.concat(`-DschemaVersion=${options.specVersion}`);
|
|
1661
1544
|
}
|
|
1662
1545
|
}
|
|
@@ -2769,6 +2652,7 @@ export async function createJavaBom(path, options) {
|
|
|
2769
2652
|
*
|
|
2770
2653
|
* @param {string} path to the project
|
|
2771
2654
|
* @param {Object} options Parse options from the cli
|
|
2655
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
2772
2656
|
*/
|
|
2773
2657
|
export async function createNodejsBom(path, options) {
|
|
2774
2658
|
let pkgList = [];
|
|
@@ -2849,6 +2733,21 @@ export async function createNodejsBom(path, options) {
|
|
|
2849
2733
|
`${options.multiProject ? "**/" : ""}bower.json`,
|
|
2850
2734
|
options,
|
|
2851
2735
|
);
|
|
2736
|
+
if (DEBUG_MODE) {
|
|
2737
|
+
const wasmFiles = getAllFiles(
|
|
2738
|
+
path,
|
|
2739
|
+
`${options.multiProject ? "**/" : ""}*.wasm`,
|
|
2740
|
+
{
|
|
2741
|
+
...options,
|
|
2742
|
+
includeNodeModulesDir: true,
|
|
2743
|
+
},
|
|
2744
|
+
);
|
|
2745
|
+
if (wasmFiles?.length) {
|
|
2746
|
+
console.log(
|
|
2747
|
+
`Found ${wasmFiles.length} wasm files in this project. cdxgen will make a best attempt to identify the exports and imports from these files.`,
|
|
2748
|
+
);
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2852
2751
|
// Parse min js files
|
|
2853
2752
|
if (minJsFiles?.length) {
|
|
2854
2753
|
manifestFiles = manifestFiles.concat(minJsFiles);
|
|
@@ -3203,8 +3102,9 @@ export async function createNodejsBom(path, options) {
|
|
|
3203
3102
|
const basePath = dirname(f);
|
|
3204
3103
|
// Determine the parent component
|
|
3205
3104
|
const packageJsonF = join(basePath, "package.json");
|
|
3206
|
-
const
|
|
3207
|
-
|
|
3105
|
+
const pnpmCjsHooks = join(basePath, ".pnpmfile.cjs");
|
|
3106
|
+
const pnpmMjsHooks = join(basePath, ".pnpmfile.mjs");
|
|
3107
|
+
if (safeExistsSync(pnpmMjsHooks) || safeExistsSync(pnpmCjsHooks)) {
|
|
3208
3108
|
thoughtLog("Wait, this pnpm project uses install hooks.");
|
|
3209
3109
|
}
|
|
3210
3110
|
if (!Object.keys(parentComponent).length) {
|
|
@@ -3367,6 +3267,7 @@ export async function createNodejsBom(path, options) {
|
|
|
3367
3267
|
const pnpmLock = join(path, "common", "config", "rush", "pnpm-lock.yaml");
|
|
3368
3268
|
if (safeExistsSync(swFile)) {
|
|
3369
3269
|
let pkgList = await parseNodeShrinkwrap(swFile);
|
|
3270
|
+
pkgList = addWasmComponentsFromImports(pkgList, allImports);
|
|
3370
3271
|
if (allImports && Object.keys(allImports).length) {
|
|
3371
3272
|
pkgList = await addEvidenceForImports(
|
|
3372
3273
|
pkgList,
|
|
@@ -3383,9 +3284,13 @@ export async function createNodejsBom(path, options) {
|
|
|
3383
3284
|
}
|
|
3384
3285
|
if (safeExistsSync(pnpmLock)) {
|
|
3385
3286
|
const pnpmLockObj = await parsePnpmLock(pnpmLock);
|
|
3287
|
+
let pkgList = addWasmComponentsFromImports(
|
|
3288
|
+
pnpmLockObj.pkgList,
|
|
3289
|
+
allImports,
|
|
3290
|
+
);
|
|
3386
3291
|
if (allImports && Object.keys(allImports).length) {
|
|
3387
3292
|
pkgList = await addEvidenceForImports(
|
|
3388
|
-
|
|
3293
|
+
pkgList,
|
|
3389
3294
|
allImports,
|
|
3390
3295
|
allExports,
|
|
3391
3296
|
options.deep,
|
|
@@ -3662,6 +3567,7 @@ export async function createNodejsBom(path, options) {
|
|
|
3662
3567
|
options.parentComponent = parentComponent;
|
|
3663
3568
|
}
|
|
3664
3569
|
if (allImports && Object.keys(allImports).length) {
|
|
3570
|
+
pkgList = addWasmComponentsFromImports(pkgList, allImports);
|
|
3665
3571
|
pkgList = await addEvidenceForImports(
|
|
3666
3572
|
pkgList,
|
|
3667
3573
|
allImports,
|
|
@@ -3677,6 +3583,84 @@ export async function createNodejsBom(path, options) {
|
|
|
3677
3583
|
});
|
|
3678
3584
|
}
|
|
3679
3585
|
|
|
3586
|
+
const WASM_IMPORT_PATTERN = /\.wasm([?#].*)?$/i;
|
|
3587
|
+
|
|
3588
|
+
/**
|
|
3589
|
+
* Adds generic wasm components from discovered source imports.
|
|
3590
|
+
*
|
|
3591
|
+
* @param {Array<Object>} pkgList Node.js package list
|
|
3592
|
+
* @param {Object} allImports analyzer imports map
|
|
3593
|
+
* @returns {Array<Object>} pkgList enriched with wasm components
|
|
3594
|
+
*/
|
|
3595
|
+
const addWasmComponentsFromImports = (pkgList, allImports) => {
|
|
3596
|
+
if (!allImports || !Object.keys(allImports).length) {
|
|
3597
|
+
return pkgList;
|
|
3598
|
+
}
|
|
3599
|
+
const existingPurls = new Set();
|
|
3600
|
+
for (const pkg of pkgList) {
|
|
3601
|
+
if (pkg?.purl) {
|
|
3602
|
+
existingPurls.add(pkg.purl);
|
|
3603
|
+
}
|
|
3604
|
+
}
|
|
3605
|
+
for (const [importPath, occurrences] of Object.entries(allImports)) {
|
|
3606
|
+
if (!WASM_IMPORT_PATTERN.test(importPath)) {
|
|
3607
|
+
continue;
|
|
3608
|
+
}
|
|
3609
|
+
const cleanImportPath = importPath.replace(/[?#].*$/, "");
|
|
3610
|
+
const normalizedImportPath = cleanImportPath
|
|
3611
|
+
.replace(/\\/g, "/")
|
|
3612
|
+
.replace(/^\.\//, "");
|
|
3613
|
+
const wasmComponentName = normalizedImportPath || cleanImportPath;
|
|
3614
|
+
const wasmFileName = basename(cleanImportPath);
|
|
3615
|
+
if (!allImports[wasmComponentName]) {
|
|
3616
|
+
allImports[wasmComponentName] = new Set();
|
|
3617
|
+
}
|
|
3618
|
+
for (const occurrence of occurrences) {
|
|
3619
|
+
allImports[wasmComponentName].add(occurrence);
|
|
3620
|
+
}
|
|
3621
|
+
const wasmPurl = new PackageURL(
|
|
3622
|
+
"generic",
|
|
3623
|
+
"",
|
|
3624
|
+
wasmFileName,
|
|
3625
|
+
"",
|
|
3626
|
+
normalizedImportPath ? { path: normalizedImportPath } : undefined,
|
|
3627
|
+
undefined,
|
|
3628
|
+
).toString();
|
|
3629
|
+
if (existingPurls.has(wasmPurl)) {
|
|
3630
|
+
continue;
|
|
3631
|
+
}
|
|
3632
|
+
const firstOccurrence = Array.from(occurrences)[0];
|
|
3633
|
+
const srcFile = firstOccurrence?.importedAs || importPath;
|
|
3634
|
+
pkgList.push({
|
|
3635
|
+
name: wasmComponentName,
|
|
3636
|
+
type: "library",
|
|
3637
|
+
purl: wasmPurl,
|
|
3638
|
+
"bom-ref": wasmPurl,
|
|
3639
|
+
properties: [
|
|
3640
|
+
{
|
|
3641
|
+
name: "SrcFile",
|
|
3642
|
+
value: srcFile,
|
|
3643
|
+
},
|
|
3644
|
+
],
|
|
3645
|
+
evidence: {
|
|
3646
|
+
identity: {
|
|
3647
|
+
field: "purl",
|
|
3648
|
+
confidence: 0.3,
|
|
3649
|
+
methods: [
|
|
3650
|
+
{
|
|
3651
|
+
technique: "filename",
|
|
3652
|
+
confidence: 0.3,
|
|
3653
|
+
value: srcFile,
|
|
3654
|
+
},
|
|
3655
|
+
],
|
|
3656
|
+
},
|
|
3657
|
+
},
|
|
3658
|
+
});
|
|
3659
|
+
existingPurls.add(wasmPurl);
|
|
3660
|
+
}
|
|
3661
|
+
return pkgList;
|
|
3662
|
+
};
|
|
3663
|
+
|
|
3680
3664
|
/**
|
|
3681
3665
|
* Function to create bom string for Projects that use Pixi package manager.
|
|
3682
3666
|
* createPixiBom is based on createPythonBom.
|
|
@@ -3687,6 +3671,7 @@ export async function createNodejsBom(path, options) {
|
|
|
3687
3671
|
*
|
|
3688
3672
|
* @param {String} path
|
|
3689
3673
|
* @param {Object} options
|
|
3674
|
+
* @returns {Object | null} BOM object, or `null` when `pixi.lock` is absent and `options.installDeps` is false
|
|
3690
3675
|
*/
|
|
3691
3676
|
export function createPixiBom(path, options) {
|
|
3692
3677
|
const allImports = {};
|
|
@@ -3765,6 +3750,7 @@ export function createPixiBom(path, options) {
|
|
|
3765
3750
|
*
|
|
3766
3751
|
* @param {string} path to the project
|
|
3767
3752
|
* @param {Object} options Parse options from the cli
|
|
3753
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
3768
3754
|
*/
|
|
3769
3755
|
export async function createPythonBom(path, options) {
|
|
3770
3756
|
let allImports = {};
|
|
@@ -4306,6 +4292,7 @@ export async function createPythonBom(path, options) {
|
|
|
4306
4292
|
*
|
|
4307
4293
|
* @param {string} path to the project
|
|
4308
4294
|
* @param {Object} options Parse options from the cli
|
|
4295
|
+
* @returns {Promise<Object | undefined>} Promise resolving to a BOM object or `undefined`
|
|
4309
4296
|
*/
|
|
4310
4297
|
export async function createGoBom(path, options) {
|
|
4311
4298
|
let pkgList = [];
|
|
@@ -4732,6 +4719,7 @@ export async function createGoBom(path, options) {
|
|
|
4732
4719
|
*
|
|
4733
4720
|
* @param {string} path to the project
|
|
4734
4721
|
* @param {Object} options Parse options from the cli
|
|
4722
|
+
* @returns {Promise<Object|undefined>} Promise resolving to a BOM object or undefined
|
|
4735
4723
|
*/
|
|
4736
4724
|
export async function createRustBom(path, options) {
|
|
4737
4725
|
let pkgList = [];
|
|
@@ -4871,6 +4859,7 @@ export async function createRustBom(path, options) {
|
|
|
4871
4859
|
*
|
|
4872
4860
|
* @param {string} path to the project
|
|
4873
4861
|
* @param {Object} options Parse options from the cli
|
|
4862
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
4874
4863
|
*/
|
|
4875
4864
|
export async function createDartBom(path, options) {
|
|
4876
4865
|
const pubFiles = getAllFiles(
|
|
@@ -4942,6 +4931,7 @@ export async function createDartBom(path, options) {
|
|
|
4942
4931
|
*
|
|
4943
4932
|
* @param {string} path to the project
|
|
4944
4933
|
* @param {Object} options Parse options from the cli
|
|
4934
|
+
* @returns {Object} BOM object
|
|
4945
4935
|
*/
|
|
4946
4936
|
export function createCppBom(path, options) {
|
|
4947
4937
|
let parentComponent;
|
|
@@ -5153,6 +5143,7 @@ export function createCppBom(path, options) {
|
|
|
5153
5143
|
*
|
|
5154
5144
|
* @param {string} path to the project
|
|
5155
5145
|
* @param {Object} options Parse options from the cli
|
|
5146
|
+
* @returns {Object} BOM object
|
|
5156
5147
|
*/
|
|
5157
5148
|
export function createClojureBom(path, options) {
|
|
5158
5149
|
const ednFiles = getAllFiles(
|
|
@@ -5270,6 +5261,7 @@ export function createClojureBom(path, options) {
|
|
|
5270
5261
|
*
|
|
5271
5262
|
* @param {string} path to the project
|
|
5272
5263
|
* @param {Object} options Parse options from the cli
|
|
5264
|
+
* @returns {Object} BOM object
|
|
5273
5265
|
*/
|
|
5274
5266
|
export function createHaskellBom(path, options) {
|
|
5275
5267
|
const cabalFiles = getAllFiles(
|
|
@@ -5302,6 +5294,7 @@ export function createHaskellBom(path, options) {
|
|
|
5302
5294
|
*
|
|
5303
5295
|
* @param {string} path to the project
|
|
5304
5296
|
* @param {Object} options Parse options from the cli
|
|
5297
|
+
* @returns {Object} BOM object
|
|
5305
5298
|
*/
|
|
5306
5299
|
export function createElixirBom(path, options) {
|
|
5307
5300
|
const mixFiles = getAllFiles(
|
|
@@ -5334,6 +5327,7 @@ export function createElixirBom(path, options) {
|
|
|
5334
5327
|
*
|
|
5335
5328
|
* @param {string} path to the project
|
|
5336
5329
|
* @param {Object} options Parse options from the cli
|
|
5330
|
+
* @returns {Object} BOM object
|
|
5337
5331
|
*/
|
|
5338
5332
|
export function createGitHubBom(path, options) {
|
|
5339
5333
|
const ghactionFiles = getAllFiles(
|
|
@@ -5365,6 +5359,7 @@ export function createGitHubBom(path, options) {
|
|
|
5365
5359
|
*
|
|
5366
5360
|
* @param {string} path to the project
|
|
5367
5361
|
* @param {Object} options Parse options from the cli
|
|
5362
|
+
* @returns {Object} BOM object
|
|
5368
5363
|
*/
|
|
5369
5364
|
export function createCloudBuildBom(path, options) {
|
|
5370
5365
|
const cbFiles = getAllFiles(path, "cloudbuild.yml", options);
|
|
@@ -5393,6 +5388,7 @@ export function createCloudBuildBom(path, options) {
|
|
|
5393
5388
|
*
|
|
5394
5389
|
* @param {string} _path to the project
|
|
5395
5390
|
* @param {Object} options Parse options from the cli
|
|
5391
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
5396
5392
|
*/
|
|
5397
5393
|
export function createOSBom(_path, options) {
|
|
5398
5394
|
console.warn(
|
|
@@ -5451,6 +5447,7 @@ export function createOSBom(_path, options) {
|
|
|
5451
5447
|
*
|
|
5452
5448
|
* @param {string} path to the project
|
|
5453
5449
|
* @param {Object} options Parse options from the cli
|
|
5450
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
5454
5451
|
*/
|
|
5455
5452
|
export async function createJenkinsBom(path, options) {
|
|
5456
5453
|
let pkgList = [];
|
|
@@ -5500,6 +5497,7 @@ export async function createJenkinsBom(path, options) {
|
|
|
5500
5497
|
*
|
|
5501
5498
|
* @param {string} path to the project
|
|
5502
5499
|
* @param {Object} options Parse options from the cli
|
|
5500
|
+
* @returns {Object} BOM object
|
|
5503
5501
|
*/
|
|
5504
5502
|
export function createHelmBom(path, options) {
|
|
5505
5503
|
let pkgList = [];
|
|
@@ -5532,6 +5530,7 @@ export function createHelmBom(path, options) {
|
|
|
5532
5530
|
*
|
|
5533
5531
|
* @param {string} path to the project
|
|
5534
5532
|
* @param {Object} options Parse options from the cli
|
|
5533
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
5535
5534
|
*/
|
|
5536
5535
|
export async function createSwiftBom(path, options) {
|
|
5537
5536
|
const swiftFiles = getAllFiles(
|
|
@@ -5676,6 +5675,7 @@ export async function createSwiftBom(path, options) {
|
|
|
5676
5675
|
*
|
|
5677
5676
|
* @param {string} path to the project
|
|
5678
5677
|
* @param {Object} options Parse options from the cli
|
|
5678
|
+
* @returns {Promise<Object | undefined>} Promise resolving to a BOM object, or `undefined` when no Podfiles are found
|
|
5679
5679
|
*/
|
|
5680
5680
|
export async function createCocoaBom(path, options) {
|
|
5681
5681
|
const cocoaFiles = getAllFiles(
|
|
@@ -5692,7 +5692,7 @@ export async function createCocoaBom(path, options) {
|
|
|
5692
5692
|
for (const podFile of cocoaFiles) {
|
|
5693
5693
|
const projectPath = dirname(podFile);
|
|
5694
5694
|
const lockFile = `${podFile}.lock`;
|
|
5695
|
-
if (!
|
|
5695
|
+
if (!safeExistsSync(lockFile) || options.deep) {
|
|
5696
5696
|
if (options.installDeps) {
|
|
5697
5697
|
executePodCommand(["install"], projectPath, options);
|
|
5698
5698
|
} else {
|
|
@@ -5830,6 +5830,7 @@ export async function createCocoaBom(path, options) {
|
|
|
5830
5830
|
*
|
|
5831
5831
|
* @param {string} path to the project
|
|
5832
5832
|
* @param {Object} options Parse options from the cli
|
|
5833
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
5833
5834
|
*/
|
|
5834
5835
|
export async function createNixBom(path, options) {
|
|
5835
5836
|
let pkgList = [];
|
|
@@ -5952,6 +5953,7 @@ export async function createNixBom(path, options) {
|
|
|
5952
5953
|
*
|
|
5953
5954
|
* @param {string} path to the project
|
|
5954
5955
|
* @param {Object} options Parse options from the cli
|
|
5956
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
5955
5957
|
*/
|
|
5956
5958
|
export async function createCaxaBom(path, options) {
|
|
5957
5959
|
let pkgList = [];
|
|
@@ -6003,6 +6005,7 @@ export async function createCaxaBom(path, options) {
|
|
|
6003
6005
|
*
|
|
6004
6006
|
* @param {string} path to the project
|
|
6005
6007
|
* @param {Object} options Parse options from the cli
|
|
6008
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
6006
6009
|
*/
|
|
6007
6010
|
export async function createContainerSpecLikeBom(path, options) {
|
|
6008
6011
|
let services = [];
|
|
@@ -6331,6 +6334,7 @@ export async function createContainerSpecLikeBom(path, options) {
|
|
|
6331
6334
|
*
|
|
6332
6335
|
* @param {string} path to the project
|
|
6333
6336
|
* @param {Object} options Parse options from the cli
|
|
6337
|
+
* @returns {Object} BOM object
|
|
6334
6338
|
*/
|
|
6335
6339
|
export function createPHPBom(path, options) {
|
|
6336
6340
|
let dependencies = [];
|
|
@@ -6438,7 +6442,7 @@ export function createPHPBom(path, options) {
|
|
|
6438
6442
|
// Track all the modules in a mono-repo
|
|
6439
6443
|
if (!Object.keys(parentComponent).length) {
|
|
6440
6444
|
parentComponent = { ...moduleParent };
|
|
6441
|
-
} else {
|
|
6445
|
+
} else if (moduleParent?.["bom-ref"]) {
|
|
6442
6446
|
parentComponent.components = parentComponent.components || [];
|
|
6443
6447
|
parentComponent.components.push(moduleParent);
|
|
6444
6448
|
}
|
|
@@ -6454,7 +6458,9 @@ export function createPHPBom(path, options) {
|
|
|
6454
6458
|
dependencies.splice(0, 0, {
|
|
6455
6459
|
ref: moduleParent["bom-ref"],
|
|
6456
6460
|
dependsOn: [
|
|
6457
|
-
...new Set(
|
|
6461
|
+
...new Set(
|
|
6462
|
+
retMap.rootList.map((p) => p["bom-ref"]).filter(Boolean),
|
|
6463
|
+
),
|
|
6458
6464
|
].sort(),
|
|
6459
6465
|
});
|
|
6460
6466
|
}
|
|
@@ -6467,9 +6473,9 @@ export function createPHPBom(path, options) {
|
|
|
6467
6473
|
}
|
|
6468
6474
|
// Complete the root dependency tree
|
|
6469
6475
|
if (parentComponent?.components?.length) {
|
|
6470
|
-
const parentDependsOn = parentComponent.components
|
|
6471
|
-
(d) => d["bom-ref"]
|
|
6472
|
-
|
|
6476
|
+
const parentDependsOn = parentComponent.components
|
|
6477
|
+
.map((d) => d["bom-ref"])
|
|
6478
|
+
.filter(Boolean);
|
|
6473
6479
|
dependencies = mergeDependencies(
|
|
6474
6480
|
[{ ref: parentComponent["bom-ref"], dependsOn: parentDependsOn }],
|
|
6475
6481
|
dependencies,
|
|
@@ -6491,6 +6497,7 @@ export function createPHPBom(path, options) {
|
|
|
6491
6497
|
*
|
|
6492
6498
|
* @param {string} path to the project
|
|
6493
6499
|
* @param {Object} options Parse options from the cli
|
|
6500
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
6494
6501
|
*/
|
|
6495
6502
|
export async function createRubyBom(path, options) {
|
|
6496
6503
|
// We can look for gem files within node_modules directory
|
|
@@ -6744,6 +6751,7 @@ export async function createRubyBom(path, options) {
|
|
|
6744
6751
|
*
|
|
6745
6752
|
* @param {string} path to the project
|
|
6746
6753
|
* @param {Object} options Parse options from the cli
|
|
6754
|
+
* @returns {Promise<Object|undefined>} Promise resolving to BOM object
|
|
6747
6755
|
*/
|
|
6748
6756
|
export async function createCsharpBom(path, options) {
|
|
6749
6757
|
let manifestFiles = [];
|
|
@@ -7179,11 +7187,202 @@ export async function createCsharpBom(path, options) {
|
|
|
7179
7187
|
});
|
|
7180
7188
|
}
|
|
7181
7189
|
|
|
7190
|
+
/**
|
|
7191
|
+
* Function to create BOM for VS Code / IDE extensions.
|
|
7192
|
+
* Supports two modes:
|
|
7193
|
+
* 1. Directory scan: Discovers `.vsix` files and installed extension directories
|
|
7194
|
+
* 2. IDE discovery: Automatically finds extensions installed by known IDEs
|
|
7195
|
+
*
|
|
7196
|
+
* @param {string} path to the project or directory to scan
|
|
7197
|
+
* @param {Object} options Parse options from the cli
|
|
7198
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
7199
|
+
*/
|
|
7200
|
+
export async function createVscodeExtensionBom(path, options) {
|
|
7201
|
+
let pkgList = [];
|
|
7202
|
+
let dependencies = [];
|
|
7203
|
+
const tempDirs = [];
|
|
7204
|
+
|
|
7205
|
+
// Mode 1: Scan for .vsix files in the given directory, or treat the input
|
|
7206
|
+
// path as a single .vsix file.
|
|
7207
|
+
let vsixFiles = [];
|
|
7208
|
+
if (path.endsWith(".vsix")) {
|
|
7209
|
+
vsixFiles = [resolve(path)];
|
|
7210
|
+
} else {
|
|
7211
|
+
vsixFiles = getAllFiles(
|
|
7212
|
+
path,
|
|
7213
|
+
`${options.multiProject ? "**/" : ""}*.vsix`,
|
|
7214
|
+
options,
|
|
7215
|
+
);
|
|
7216
|
+
}
|
|
7217
|
+
if (vsixFiles.length) {
|
|
7218
|
+
if (DEBUG_MODE) {
|
|
7219
|
+
console.log(`Found ${vsixFiles.length} .vsix file(s) to parse`);
|
|
7220
|
+
}
|
|
7221
|
+
for (const f of vsixFiles) {
|
|
7222
|
+
if (DEBUG_MODE) {
|
|
7223
|
+
console.log(`Parsing ${f}`);
|
|
7224
|
+
}
|
|
7225
|
+
// Get the extension component metadata
|
|
7226
|
+
const component = await parseVsixFile(f);
|
|
7227
|
+
if (component) {
|
|
7228
|
+
pkgList.push(component);
|
|
7229
|
+
}
|
|
7230
|
+
// Extract the vsix to a temp dir and run deep analysis
|
|
7231
|
+
const extractedDir = await extractVsixToTempDir(f);
|
|
7232
|
+
if (extractedDir) {
|
|
7233
|
+
tempDirs.push(extractedDir);
|
|
7234
|
+
const deepResult = await analyzeExtensionDir(extractedDir, options);
|
|
7235
|
+
if (deepResult.pkgList.length) {
|
|
7236
|
+
pkgList = pkgList.concat(deepResult.pkgList);
|
|
7237
|
+
}
|
|
7238
|
+
if (deepResult.dependencies.length) {
|
|
7239
|
+
dependencies = mergeDependencies(
|
|
7240
|
+
dependencies,
|
|
7241
|
+
deepResult.dependencies,
|
|
7242
|
+
);
|
|
7243
|
+
}
|
|
7244
|
+
}
|
|
7245
|
+
}
|
|
7246
|
+
}
|
|
7247
|
+
|
|
7248
|
+
// Mode 2: Auto-discover extensions from known IDE locations
|
|
7249
|
+
if (options.deep || options.projectType?.includes("ide-extensions")) {
|
|
7250
|
+
const ideDirs = discoverIdeExtensionDirs();
|
|
7251
|
+
if (ideDirs.length) {
|
|
7252
|
+
if (DEBUG_MODE) {
|
|
7253
|
+
console.log(
|
|
7254
|
+
`Discovered IDE extension directories: ${ideDirs.map((d) => `${d.name}: ${d.dir}`).join(", ")}`,
|
|
7255
|
+
);
|
|
7256
|
+
}
|
|
7257
|
+
const ideExtensions = collectInstalledExtensions(ideDirs);
|
|
7258
|
+
if (ideExtensions.length) {
|
|
7259
|
+
if (DEBUG_MODE) {
|
|
7260
|
+
console.log(
|
|
7261
|
+
`Found ${ideExtensions.length} IDE extension(s) from ${ideDirs.length} IDE location(s)`,
|
|
7262
|
+
);
|
|
7263
|
+
}
|
|
7264
|
+
pkgList = pkgList.concat(ideExtensions);
|
|
7265
|
+
// Deep analysis for IDE extension directories
|
|
7266
|
+
for (const ideDir of ideDirs) {
|
|
7267
|
+
await analyzeInstalledExtensionDirs(
|
|
7268
|
+
ideDir.dir,
|
|
7269
|
+
options,
|
|
7270
|
+
pkgList,
|
|
7271
|
+
dependencies,
|
|
7272
|
+
);
|
|
7273
|
+
}
|
|
7274
|
+
}
|
|
7275
|
+
}
|
|
7276
|
+
}
|
|
7277
|
+
|
|
7278
|
+
// Clean up temp directories from vsix extraction
|
|
7279
|
+
for (const td of tempDirs) {
|
|
7280
|
+
cleanupTempDir(td);
|
|
7281
|
+
}
|
|
7282
|
+
pkgList = trimComponents(pkgList);
|
|
7283
|
+
return buildBomNSData(options, pkgList, VSCODE_EXTENSION_PURL_TYPE, {
|
|
7284
|
+
src: path,
|
|
7285
|
+
filename: vsixFiles.join(", "),
|
|
7286
|
+
nsMapping: {},
|
|
7287
|
+
dependencies,
|
|
7288
|
+
});
|
|
7289
|
+
}
|
|
7290
|
+
|
|
7291
|
+
/**
|
|
7292
|
+
* Analyze an extracted extension directory for bundled dependencies.
|
|
7293
|
+
* Looks for npm lock files, node_modules, package.json files, minified JS,
|
|
7294
|
+
* and runs the babel-based analyzer on the source.
|
|
7295
|
+
*
|
|
7296
|
+
* @param {string} extDir Path to the extracted extension directory
|
|
7297
|
+
* @param {Object} options CLI options
|
|
7298
|
+
* @returns {Promise<{pkgList: Object[], dependencies: Object[]}>}
|
|
7299
|
+
*/
|
|
7300
|
+
async function analyzeExtensionDir(extDir, options) {
|
|
7301
|
+
const pkgList = [];
|
|
7302
|
+
let dependencies = [];
|
|
7303
|
+
// Check if the extension directory contains node.js project artifacts
|
|
7304
|
+
const hasPackageJson = safeExistsSync(join(extDir, "package.json"));
|
|
7305
|
+
const hasNodeModules = safeExistsSync(join(extDir, "node_modules"));
|
|
7306
|
+
const hasLockFile =
|
|
7307
|
+
safeExistsSync(join(extDir, "package-lock.json")) ||
|
|
7308
|
+
safeExistsSync(join(extDir, "yarn.lock")) ||
|
|
7309
|
+
safeExistsSync(join(extDir, "pnpm-lock.yaml"));
|
|
7310
|
+
|
|
7311
|
+
// If there are lock files or node_modules, run the full Node.js BOM generator
|
|
7312
|
+
if (hasPackageJson && (hasLockFile || hasNodeModules)) {
|
|
7313
|
+
if (DEBUG_MODE) {
|
|
7314
|
+
console.log(
|
|
7315
|
+
`Running Node.js BOM analysis on extension directory: ${extDir}`,
|
|
7316
|
+
);
|
|
7317
|
+
}
|
|
7318
|
+
const nodeBomOptions = {
|
|
7319
|
+
...options,
|
|
7320
|
+
path: extDir,
|
|
7321
|
+
multiProject: true,
|
|
7322
|
+
installDeps: false,
|
|
7323
|
+
noBabel: false,
|
|
7324
|
+
projectType: ["js"],
|
|
7325
|
+
};
|
|
7326
|
+
const bomData = await createNodejsBom(extDir, nodeBomOptions);
|
|
7327
|
+
if (bomData?.bomJson?.components?.length) {
|
|
7328
|
+
for (const comp of bomData.bomJson.components) {
|
|
7329
|
+
pkgList.push(comp);
|
|
7330
|
+
}
|
|
7331
|
+
}
|
|
7332
|
+
if (bomData?.bomJson?.dependencies?.length) {
|
|
7333
|
+
dependencies = mergeDependencies(
|
|
7334
|
+
dependencies,
|
|
7335
|
+
bomData.bomJson.dependencies,
|
|
7336
|
+
);
|
|
7337
|
+
}
|
|
7338
|
+
return { pkgList, dependencies };
|
|
7339
|
+
}
|
|
7340
|
+
return { pkgList, dependencies };
|
|
7341
|
+
}
|
|
7342
|
+
|
|
7343
|
+
/**
|
|
7344
|
+
* Run deep analysis on installed extension subdirectories within a parent
|
|
7345
|
+
* extensions directory. Each subdirectory represents an installed extension.
|
|
7346
|
+
*
|
|
7347
|
+
* @param {string} extensionsDir Parent directory containing extension subdirs
|
|
7348
|
+
* @param {Object} options CLI options
|
|
7349
|
+
* @param {Object[]} pkgList Mutable array to push discovered components into
|
|
7350
|
+
* @param {Object[]} dependencies Mutable array to merge dependencies into
|
|
7351
|
+
*/
|
|
7352
|
+
async function analyzeInstalledExtensionDirs(
|
|
7353
|
+
extensionsDir,
|
|
7354
|
+
options,
|
|
7355
|
+
pkgList,
|
|
7356
|
+
dependencies,
|
|
7357
|
+
) {
|
|
7358
|
+
let entries;
|
|
7359
|
+
try {
|
|
7360
|
+
entries = readdirSync(extensionsDir, { withFileTypes: true });
|
|
7361
|
+
} catch (_e) {
|
|
7362
|
+
return;
|
|
7363
|
+
}
|
|
7364
|
+
for (const entry of entries) {
|
|
7365
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) {
|
|
7366
|
+
continue;
|
|
7367
|
+
}
|
|
7368
|
+
const extDir = join(extensionsDir, entry.name);
|
|
7369
|
+
const deepResult = await analyzeExtensionDir(extDir, options);
|
|
7370
|
+
if (deepResult.pkgList.length) {
|
|
7371
|
+
pkgList.push(...deepResult.pkgList);
|
|
7372
|
+
}
|
|
7373
|
+
if (deepResult.dependencies.length) {
|
|
7374
|
+
const merged = mergeDependencies(dependencies, deepResult.dependencies);
|
|
7375
|
+
dependencies.splice(0, dependencies.length, ...merged);
|
|
7376
|
+
}
|
|
7377
|
+
}
|
|
7378
|
+
}
|
|
7379
|
+
|
|
7182
7380
|
/**
|
|
7183
7381
|
* Function to create bom object for cryptographic certificate files
|
|
7184
7382
|
*
|
|
7185
7383
|
* @param {string} path to the project
|
|
7186
7384
|
* @param {Object} options Parse options from the cli
|
|
7385
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
7187
7386
|
*/
|
|
7188
7387
|
export async function createCryptoCertsBom(path, options) {
|
|
7189
7388
|
const pkgList = [];
|
|
@@ -7220,191 +7419,6 @@ export async function createCryptoCertsBom(path, options) {
|
|
|
7220
7419
|
};
|
|
7221
7420
|
}
|
|
7222
7421
|
|
|
7223
|
-
export function mergeDependencies(
|
|
7224
|
-
dependencies,
|
|
7225
|
-
newDependencies,
|
|
7226
|
-
parentComponent = {},
|
|
7227
|
-
) {
|
|
7228
|
-
if (!parentComponent && DEBUG_MODE) {
|
|
7229
|
-
console.log(
|
|
7230
|
-
"Unable to determine parent component. Dependencies will be flattened.",
|
|
7231
|
-
);
|
|
7232
|
-
}
|
|
7233
|
-
let providesFound = false;
|
|
7234
|
-
const deps_map = {};
|
|
7235
|
-
const provides_map = {};
|
|
7236
|
-
const parentRef = parentComponent?.["bom-ref"]
|
|
7237
|
-
? parentComponent["bom-ref"]
|
|
7238
|
-
: undefined;
|
|
7239
|
-
const combinedDeps = dependencies.concat(newDependencies || []);
|
|
7240
|
-
for (const adep of combinedDeps) {
|
|
7241
|
-
if (!deps_map[adep.ref]) {
|
|
7242
|
-
deps_map[adep.ref] = new Set();
|
|
7243
|
-
}
|
|
7244
|
-
if (!provides_map[adep.ref]) {
|
|
7245
|
-
provides_map[adep.ref] = new Set();
|
|
7246
|
-
}
|
|
7247
|
-
if (adep["dependsOn"]) {
|
|
7248
|
-
for (const eachDepends of adep["dependsOn"]) {
|
|
7249
|
-
if (parentRef && eachDepends) {
|
|
7250
|
-
if (eachDepends.toLowerCase() !== parentRef.toLowerCase()) {
|
|
7251
|
-
deps_map[adep.ref].add(eachDepends);
|
|
7252
|
-
}
|
|
7253
|
-
} else {
|
|
7254
|
-
deps_map[adep.ref].add(eachDepends);
|
|
7255
|
-
}
|
|
7256
|
-
}
|
|
7257
|
-
}
|
|
7258
|
-
if (adep["provides"]) {
|
|
7259
|
-
providesFound = true;
|
|
7260
|
-
for (const eachProvides of adep["provides"]) {
|
|
7261
|
-
if (
|
|
7262
|
-
parentRef &&
|
|
7263
|
-
eachProvides.toLowerCase() !== parentRef.toLowerCase()
|
|
7264
|
-
) {
|
|
7265
|
-
provides_map[adep.ref].add(eachProvides);
|
|
7266
|
-
}
|
|
7267
|
-
}
|
|
7268
|
-
}
|
|
7269
|
-
}
|
|
7270
|
-
const retlist = [];
|
|
7271
|
-
for (const akey of Object.keys(deps_map)) {
|
|
7272
|
-
if (providesFound) {
|
|
7273
|
-
retlist.push({
|
|
7274
|
-
ref: akey,
|
|
7275
|
-
dependsOn: Array.from(deps_map[akey]).sort(),
|
|
7276
|
-
provides: Array.from(provides_map[akey]).sort(),
|
|
7277
|
-
});
|
|
7278
|
-
} else {
|
|
7279
|
-
retlist.push({
|
|
7280
|
-
ref: akey,
|
|
7281
|
-
dependsOn: Array.from(deps_map[akey]).sort(),
|
|
7282
|
-
});
|
|
7283
|
-
}
|
|
7284
|
-
}
|
|
7285
|
-
return retlist;
|
|
7286
|
-
}
|
|
7287
|
-
|
|
7288
|
-
/**
|
|
7289
|
-
* Trim duplicate components by retaining all the properties
|
|
7290
|
-
*
|
|
7291
|
-
* @param {Array} components Components
|
|
7292
|
-
*
|
|
7293
|
-
* @returns {Array} Filtered components
|
|
7294
|
-
*/
|
|
7295
|
-
export function trimComponents(components) {
|
|
7296
|
-
const keyCache = {};
|
|
7297
|
-
const filteredComponents = [];
|
|
7298
|
-
for (const comp of components) {
|
|
7299
|
-
const key = (
|
|
7300
|
-
comp.purl ||
|
|
7301
|
-
comp["bom-ref"] ||
|
|
7302
|
-
comp.name + comp.version
|
|
7303
|
-
).toLowerCase();
|
|
7304
|
-
if (!keyCache[key]) {
|
|
7305
|
-
keyCache[key] = comp;
|
|
7306
|
-
} else {
|
|
7307
|
-
const existingComponent = keyCache[key];
|
|
7308
|
-
// We need to retain any properties that differ
|
|
7309
|
-
if (comp.properties) {
|
|
7310
|
-
if (existingComponent.properties) {
|
|
7311
|
-
for (const newprop of comp.properties) {
|
|
7312
|
-
if (
|
|
7313
|
-
!existingComponent.properties.find(
|
|
7314
|
-
(prop) =>
|
|
7315
|
-
prop.name === newprop.name && prop.value === newprop.value,
|
|
7316
|
-
)
|
|
7317
|
-
) {
|
|
7318
|
-
existingComponent.properties.push(newprop);
|
|
7319
|
-
}
|
|
7320
|
-
}
|
|
7321
|
-
} else {
|
|
7322
|
-
existingComponent.properties = comp.properties;
|
|
7323
|
-
}
|
|
7324
|
-
}
|
|
7325
|
-
// Retain all component.evidence.identity
|
|
7326
|
-
if (comp?.evidence?.identity) {
|
|
7327
|
-
if (!existingComponent.evidence) {
|
|
7328
|
-
existingComponent.evidence = { identity: [] };
|
|
7329
|
-
} else if (!existingComponent?.evidence?.identity) {
|
|
7330
|
-
existingComponent.evidence.identity = [];
|
|
7331
|
-
} else if (
|
|
7332
|
-
existingComponent?.evidence?.identity &&
|
|
7333
|
-
!Array.isArray(existingComponent.evidence.identity)
|
|
7334
|
-
) {
|
|
7335
|
-
existingComponent.evidence.identity = [
|
|
7336
|
-
existingComponent.evidence.identity,
|
|
7337
|
-
];
|
|
7338
|
-
}
|
|
7339
|
-
// comp.evidence.identity can be an array or object
|
|
7340
|
-
// Merge the evidence.identity based on methods or objects
|
|
7341
|
-
const isIdentityArray = Array.isArray(comp.evidence.identity);
|
|
7342
|
-
const identities = isIdentityArray
|
|
7343
|
-
? comp.evidence.identity
|
|
7344
|
-
: [comp.evidence.identity];
|
|
7345
|
-
for (const aident of identities) {
|
|
7346
|
-
let methodBasedMerge = false;
|
|
7347
|
-
if (aident?.methods?.length) {
|
|
7348
|
-
for (const amethod of aident.methods) {
|
|
7349
|
-
for (const existIdent of existingComponent.evidence.identity) {
|
|
7350
|
-
if (existIdent.field === aident.field) {
|
|
7351
|
-
if (!existIdent.methods) {
|
|
7352
|
-
existIdent.methods = [];
|
|
7353
|
-
}
|
|
7354
|
-
let isDup = false;
|
|
7355
|
-
for (const emethod of existIdent.methods) {
|
|
7356
|
-
if (emethod?.value === amethod?.value) {
|
|
7357
|
-
isDup = true;
|
|
7358
|
-
break;
|
|
7359
|
-
}
|
|
7360
|
-
}
|
|
7361
|
-
if (!isDup) {
|
|
7362
|
-
existIdent.methods.push(amethod);
|
|
7363
|
-
}
|
|
7364
|
-
methodBasedMerge = true;
|
|
7365
|
-
}
|
|
7366
|
-
}
|
|
7367
|
-
}
|
|
7368
|
-
}
|
|
7369
|
-
if (!methodBasedMerge && aident.field && aident.confidence) {
|
|
7370
|
-
existingComponent.evidence.identity.push(aident);
|
|
7371
|
-
}
|
|
7372
|
-
}
|
|
7373
|
-
if (!isIdentityArray) {
|
|
7374
|
-
const firstIdentity = existingComponent.evidence.identity[0];
|
|
7375
|
-
let identConfidence = firstIdentity?.confidence;
|
|
7376
|
-
// We need to set the confidence to the max of all confidences
|
|
7377
|
-
if (firstIdentity?.methods?.length > 1) {
|
|
7378
|
-
for (const aidentMethod of firstIdentity.methods) {
|
|
7379
|
-
if (
|
|
7380
|
-
aidentMethod?.confidence &&
|
|
7381
|
-
aidentMethod.confidence > identConfidence
|
|
7382
|
-
) {
|
|
7383
|
-
identConfidence = aidentMethod.confidence;
|
|
7384
|
-
}
|
|
7385
|
-
}
|
|
7386
|
-
}
|
|
7387
|
-
firstIdentity.confidence = identConfidence;
|
|
7388
|
-
existingComponent.evidence = {
|
|
7389
|
-
identity: firstIdentity,
|
|
7390
|
-
};
|
|
7391
|
-
}
|
|
7392
|
-
}
|
|
7393
|
-
// If the component is required in any of the child projects, then make it required
|
|
7394
|
-
if (
|
|
7395
|
-
existingComponent?.scope !== "required" &&
|
|
7396
|
-
comp?.scope === "required"
|
|
7397
|
-
) {
|
|
7398
|
-
existingComponent.scope = "required";
|
|
7399
|
-
}
|
|
7400
|
-
}
|
|
7401
|
-
}
|
|
7402
|
-
for (const akey of Object.keys(keyCache)) {
|
|
7403
|
-
filteredComponents.push(keyCache[akey]);
|
|
7404
|
-
}
|
|
7405
|
-
return filteredComponents;
|
|
7406
|
-
}
|
|
7407
|
-
|
|
7408
7422
|
/**
|
|
7409
7423
|
* Dedupe components
|
|
7410
7424
|
*
|
|
@@ -7445,7 +7459,7 @@ export function dedupeBom(options, components, parentComponent, dependencies) {
|
|
|
7445
7459
|
components,
|
|
7446
7460
|
bomJson: {
|
|
7447
7461
|
bomFormat: "CycloneDX",
|
|
7448
|
-
specVersion: `${options.specVersion || 1.
|
|
7462
|
+
specVersion: `${options.specVersion || 1.7}`,
|
|
7449
7463
|
serialNumber: serialNum,
|
|
7450
7464
|
version: 1,
|
|
7451
7465
|
metadata: addMetadata(parentComponent, options, {}),
|
|
@@ -7461,11 +7475,13 @@ export function dedupeBom(options, components, parentComponent, dependencies) {
|
|
|
7461
7475
|
*
|
|
7462
7476
|
* @param {string[]} pathList list of to the project
|
|
7463
7477
|
* @param {Object} options Parse options from the cli
|
|
7478
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
7464
7479
|
*/
|
|
7465
7480
|
export async function createMultiXBom(pathList, options) {
|
|
7466
7481
|
let components = [];
|
|
7467
7482
|
let dependencies = [];
|
|
7468
7483
|
let bomData;
|
|
7484
|
+
let formulationList = [];
|
|
7469
7485
|
let parentComponent = determineParentComponent(options) || {};
|
|
7470
7486
|
let parentSubComponents = [];
|
|
7471
7487
|
options.createMultiXBom = true;
|
|
@@ -7676,6 +7692,9 @@ export async function createMultiXBom(pathList, options) {
|
|
|
7676
7692
|
parentSubComponents.push(bomData.parentComponent);
|
|
7677
7693
|
}
|
|
7678
7694
|
}
|
|
7695
|
+
if (bomData?.formulationList?.length) {
|
|
7696
|
+
formulationList = formulationList.concat(bomData.formulationList);
|
|
7697
|
+
}
|
|
7679
7698
|
}
|
|
7680
7699
|
if (hasAnyProjectType(["oci", "go"], options)) {
|
|
7681
7700
|
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
@@ -8168,6 +8187,27 @@ export async function createMultiXBom(pathList, options) {
|
|
|
8168
8187
|
}
|
|
8169
8188
|
}
|
|
8170
8189
|
}
|
|
8190
|
+
if (hasAnyProjectType(["vscode-extension"], options)) {
|
|
8191
|
+
bomData = await createVscodeExtensionBom(path, options);
|
|
8192
|
+
if (bomData?.bomJson?.components?.length) {
|
|
8193
|
+
if (DEBUG_MODE) {
|
|
8194
|
+
console.log(
|
|
8195
|
+
`Found ${bomData.bomJson.components.length} VS Code extension(s) at ${path}`,
|
|
8196
|
+
);
|
|
8197
|
+
}
|
|
8198
|
+
components = components.concat(bomData.bomJson.components);
|
|
8199
|
+
dependencies = mergeDependencies(
|
|
8200
|
+
dependencies,
|
|
8201
|
+
bomData.bomJson.dependencies,
|
|
8202
|
+
);
|
|
8203
|
+
if (
|
|
8204
|
+
bomData.parentComponent &&
|
|
8205
|
+
Object.keys(bomData.parentComponent).length
|
|
8206
|
+
) {
|
|
8207
|
+
parentSubComponents.push(bomData.parentComponent);
|
|
8208
|
+
}
|
|
8209
|
+
}
|
|
8210
|
+
}
|
|
8171
8211
|
// Collect any crypto keys
|
|
8172
8212
|
if (options.specVersion >= 1.6 && options.includeCrypto) {
|
|
8173
8213
|
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
@@ -8266,7 +8306,16 @@ export async function createMultiXBom(pathList, options) {
|
|
|
8266
8306
|
}
|
|
8267
8307
|
}
|
|
8268
8308
|
}
|
|
8269
|
-
|
|
8309
|
+
const multiResult = dedupeBom(
|
|
8310
|
+
options,
|
|
8311
|
+
components,
|
|
8312
|
+
parentComponent,
|
|
8313
|
+
dependencies,
|
|
8314
|
+
);
|
|
8315
|
+
if (formulationList.length) {
|
|
8316
|
+
multiResult.formulationList = formulationList;
|
|
8317
|
+
}
|
|
8318
|
+
return multiResult;
|
|
8270
8319
|
}
|
|
8271
8320
|
|
|
8272
8321
|
/**
|
|
@@ -8274,6 +8323,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
8274
8323
|
*
|
|
8275
8324
|
* @param {string} path to the project
|
|
8276
8325
|
* @param {Object} options Parse options from the cli
|
|
8326
|
+
* @returns {Promise<Object|undefined>} Promise resolving to BOM object, or undefined if path is not readable
|
|
8277
8327
|
*/
|
|
8278
8328
|
export async function createXBom(path, options) {
|
|
8279
8329
|
try {
|
|
@@ -8512,6 +8562,16 @@ export async function createXBom(path, options) {
|
|
|
8512
8562
|
return await createJenkinsBom(path, options);
|
|
8513
8563
|
}
|
|
8514
8564
|
|
|
8565
|
+
// VS Code extensions (.vsix files)
|
|
8566
|
+
const vsixFiles = getAllFiles(
|
|
8567
|
+
path,
|
|
8568
|
+
`${options.multiProject ? "**/" : ""}*.vsix`,
|
|
8569
|
+
options,
|
|
8570
|
+
);
|
|
8571
|
+
if (vsixFiles.length) {
|
|
8572
|
+
return await createVscodeExtensionBom(path, options);
|
|
8573
|
+
}
|
|
8574
|
+
|
|
8515
8575
|
// Helm charts
|
|
8516
8576
|
const chartFiles = getAllFiles(
|
|
8517
8577
|
path,
|
|
@@ -8619,6 +8679,7 @@ export async function createXBom(path, options) {
|
|
|
8619
8679
|
*
|
|
8620
8680
|
* @param {string} path to the project
|
|
8621
8681
|
* @param {Object} options Parse options from the cli
|
|
8682
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
8622
8683
|
*/
|
|
8623
8684
|
export async function createBom(path, options) {
|
|
8624
8685
|
let { projectType } = options;
|
|
@@ -8863,6 +8924,9 @@ export async function createBom(path, options) {
|
|
|
8863
8924
|
if (PROJECT_TYPE_ALIASES["caxa"].includes(projectType[0])) {
|
|
8864
8925
|
return await createCaxaBom(path, options);
|
|
8865
8926
|
}
|
|
8927
|
+
if (PROJECT_TYPE_ALIASES["vscode-extension"].includes(projectType[0])) {
|
|
8928
|
+
return await createVscodeExtensionBom(path, options);
|
|
8929
|
+
}
|
|
8866
8930
|
switch (projectType[0]) {
|
|
8867
8931
|
case "jar":
|
|
8868
8932
|
return createJarBom(path, options);
|
|
@@ -8901,66 +8965,22 @@ export async function createBom(path, options) {
|
|
|
8901
8965
|
* @throws {Error} if the request fails
|
|
8902
8966
|
*/
|
|
8903
8967
|
export async function submitBom(args, bomContents) {
|
|
8904
|
-
const serverUrl =
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
);
|
|
8908
|
-
if (encodedBomContents.startsWith("77u/")) {
|
|
8909
|
-
encodedBomContents = encodedBomContents.substring(4);
|
|
8910
|
-
}
|
|
8911
|
-
const bomPayload = {
|
|
8912
|
-
autoCreate: "true",
|
|
8913
|
-
bom: encodedBomContents,
|
|
8914
|
-
};
|
|
8915
|
-
const projectVersion = args.projectVersion || "main";
|
|
8916
|
-
if (
|
|
8917
|
-
typeof args.projectId !== "undefined" ||
|
|
8918
|
-
(typeof args.projectName !== "undefined" &&
|
|
8919
|
-
typeof projectVersion !== "undefined")
|
|
8920
|
-
) {
|
|
8921
|
-
if (typeof args.projectId !== "undefined") {
|
|
8922
|
-
bomPayload.project = args.projectId;
|
|
8923
|
-
}
|
|
8924
|
-
if (typeof args.projectName !== "undefined") {
|
|
8925
|
-
bomPayload.projectName = args.projectName;
|
|
8926
|
-
}
|
|
8927
|
-
if (typeof projectVersion !== "undefined") {
|
|
8928
|
-
bomPayload.projectVersion = projectVersion;
|
|
8929
|
-
}
|
|
8930
|
-
} else {
|
|
8968
|
+
const serverUrl = getDependencyTrackBomUrl(args.serverUrl);
|
|
8969
|
+
const bomPayload = buildDependencyTrackBomPayload(args, bomContents);
|
|
8970
|
+
if (!bomPayload) {
|
|
8931
8971
|
console.log(
|
|
8932
|
-
"projectId
|
|
8972
|
+
"Invalid Dependency-Track submission arguments. Provide projectId or projectName (projectVersion defaults to main) and specify parent project either by UUID or by parent project name + version.",
|
|
8933
8973
|
);
|
|
8934
8974
|
args.failOnError && process.exit(1);
|
|
8935
8975
|
return;
|
|
8936
8976
|
}
|
|
8937
|
-
if (
|
|
8938
|
-
typeof args.parentProjectId !== "undefined" ||
|
|
8939
|
-
typeof args.parentUUID !== "undefined"
|
|
8940
|
-
) {
|
|
8941
|
-
bomPayload.parentUUID = args.parentProjectId || args.parentUUID;
|
|
8942
|
-
}
|
|
8943
|
-
// Add project tags if provided
|
|
8944
|
-
// see https://docs.dependencytrack.org/2024/10/01/v4.12.0/
|
|
8945
|
-
// corresponding API usage documentation can be found on the
|
|
8946
|
-
// API docs site of your instance, see
|
|
8947
|
-
// https://docs.dependencytrack.org/integrations/rest-api/
|
|
8948
|
-
// or public instance see https://yoursky.blue/documentation/rest-api
|
|
8949
|
-
if (typeof args.projectTag !== "undefined") {
|
|
8950
|
-
// If args.projectTag is not an array, convert it to an array
|
|
8951
|
-
// Attention, array items should be of form { name: "tagName " }
|
|
8952
|
-
// see https://yoursky.blue/documentation/rest-api#tag/bom/operation/UploadBomBase64Encoded
|
|
8953
|
-
bomPayload.projectTags = (
|
|
8954
|
-
Array.isArray(args.projectTag) ? args.projectTag : [args.projectTag]
|
|
8955
|
-
).map((tag) => ({ name: tag }));
|
|
8956
|
-
}
|
|
8957
8977
|
if (DEBUG_MODE) {
|
|
8958
8978
|
console.log(
|
|
8959
8979
|
"Submitting BOM to",
|
|
8960
8980
|
serverUrl,
|
|
8961
8981
|
"params",
|
|
8962
8982
|
args.projectName,
|
|
8963
|
-
projectVersion,
|
|
8983
|
+
bomPayload.projectVersion,
|
|
8964
8984
|
);
|
|
8965
8985
|
}
|
|
8966
8986
|
try {
|