@cyclonedx/cdxgen 12.1.5 → 12.2.0
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 +47 -39
- package/bin/cdxgen.js +175 -96
- 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 +327 -372
- 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/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 +203 -0
- package/lib/helpers/depsUtils.poku.js +150 -0
- package/lib/helpers/display.js +423 -4
- 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/utils.js +681 -406
- package/lib/helpers/utils.poku.js +55 -255
- 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 +1 -1
- package/lib/server/server.js +40 -11
- package/lib/server/server.poku.js +123 -144
- package/lib/stages/postgen/annotator.js +1 -1
- package/lib/stages/postgen/auditBom.js +197 -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 -8
- 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/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/utils.d.ts +532 -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 +21 -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,
|
|
@@ -25,15 +25,8 @@ import { parse as loadYaml } from "yaml";
|
|
|
25
25
|
|
|
26
26
|
import { findJSImportsExports } from "../helpers/analyzer.js";
|
|
27
27
|
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";
|
|
28
|
+
import { mergeDependencies, trimComponents } from "../helpers/depsUtils.js";
|
|
29
|
+
import { GIT_COMMAND } from "../helpers/envcontext.js";
|
|
37
30
|
import { thoughtLog } from "../helpers/logger.js";
|
|
38
31
|
import {
|
|
39
32
|
addEvidenceForDotnet,
|
|
@@ -176,6 +169,14 @@ import {
|
|
|
176
169
|
shouldFetchLicense,
|
|
177
170
|
splitOutputByGradleProjects,
|
|
178
171
|
} from "../helpers/utils.js";
|
|
172
|
+
import {
|
|
173
|
+
cleanupTempDir,
|
|
174
|
+
collectInstalledExtensions,
|
|
175
|
+
discoverIdeExtensionDirs,
|
|
176
|
+
extractVsixToTempDir,
|
|
177
|
+
parseVsixFile,
|
|
178
|
+
VSCODE_EXTENSION_PURL_TYPE,
|
|
179
|
+
} from "../helpers/vsixutils.js";
|
|
179
180
|
import {
|
|
180
181
|
executeOsQuery,
|
|
181
182
|
getBinaryBom,
|
|
@@ -438,160 +439,6 @@ const addLifecyclesSection = (options) => {
|
|
|
438
439
|
return lifecycles;
|
|
439
440
|
};
|
|
440
441
|
|
|
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
442
|
/**
|
|
596
443
|
* Function to create metadata block
|
|
597
444
|
*
|
|
@@ -1000,6 +847,7 @@ function addExternalReferences(opkg) {
|
|
|
1000
847
|
* @param {Object} allImports All imports
|
|
1001
848
|
* @param {Object} pkg Package object
|
|
1002
849
|
* @param {string} ptype Package type
|
|
850
|
+
* @returns {Object[]} Array of component objects
|
|
1003
851
|
*/
|
|
1004
852
|
export function listComponents(options, allImports, pkg, ptype = "npm") {
|
|
1005
853
|
const compMap = {};
|
|
@@ -1067,6 +915,15 @@ function addComponent(
|
|
|
1067
915
|
purl = undefined;
|
|
1068
916
|
purlString = undefined;
|
|
1069
917
|
}
|
|
918
|
+
// Some applications like github workflow steps and commands do not have purl
|
|
919
|
+
if (
|
|
920
|
+
pkg.purl === undefined &&
|
|
921
|
+
!pkg?.["bom-ref"]?.startsWith("pkg:") &&
|
|
922
|
+
pkg?.type === "application"
|
|
923
|
+
) {
|
|
924
|
+
purl = undefined;
|
|
925
|
+
purlString = undefined;
|
|
926
|
+
}
|
|
1070
927
|
const description = pkg.description || undefined;
|
|
1071
928
|
let compScope = pkg.scope;
|
|
1072
929
|
if (allImports) {
|
|
@@ -1106,7 +963,12 @@ function addComponent(
|
|
|
1106
963
|
component.data = pkg.data || undefined;
|
|
1107
964
|
}
|
|
1108
965
|
component["type"] = determinePackageType(pkg);
|
|
1109
|
-
|
|
966
|
+
if (purlString) {
|
|
967
|
+
component["bom-ref"] = decodeURIComponent(purlString);
|
|
968
|
+
} else if (pkg["bom-ref"]) {
|
|
969
|
+
component["bom-ref"] = pkg["bom-ref"];
|
|
970
|
+
}
|
|
971
|
+
|
|
1110
972
|
if (
|
|
1111
973
|
component.externalReferences === undefined ||
|
|
1112
974
|
component.externalReferences.length === 0
|
|
@@ -1136,6 +998,18 @@ function addComponent(
|
|
|
1136
998
|
}
|
|
1137
999
|
delete component.authors;
|
|
1138
1000
|
}
|
|
1001
|
+
// Downgrade from 1.7
|
|
1002
|
+
if (options.specVersion < 1.7) {
|
|
1003
|
+
if (component.isExternal) {
|
|
1004
|
+
delete component.isExternal;
|
|
1005
|
+
}
|
|
1006
|
+
if (component.versionRange) {
|
|
1007
|
+
console.warn(
|
|
1008
|
+
`Version Range is not supported in ${options.specVersion} specifications. Please run cdxgen with --spec-version 1.7`,
|
|
1009
|
+
);
|
|
1010
|
+
delete component.versionRange;
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1139
1013
|
// Retain any tags
|
|
1140
1014
|
if (
|
|
1141
1015
|
options.specVersion >= 1.6 &&
|
|
@@ -1155,12 +1029,10 @@ function addComponent(
|
|
|
1155
1029
|
if (pkg.components) {
|
|
1156
1030
|
component.components = pkg.components;
|
|
1157
1031
|
}
|
|
1032
|
+
const compMapKey = component.purl || component["bom-ref"];
|
|
1158
1033
|
// Issue: 1353. We need to keep merging the properties
|
|
1159
|
-
if (compMap[
|
|
1160
|
-
const mergedComponents = trimComponents([
|
|
1161
|
-
compMap[component.purl],
|
|
1162
|
-
component,
|
|
1163
|
-
]);
|
|
1034
|
+
if (compMap[compMapKey]) {
|
|
1035
|
+
const mergedComponents = trimComponents([compMap[compMapKey], component]);
|
|
1164
1036
|
if (mergedComponents?.length === 1) {
|
|
1165
1037
|
component = mergedComponents[0];
|
|
1166
1038
|
}
|
|
@@ -1196,7 +1068,7 @@ function addComponent(
|
|
|
1196
1068
|
component.evidence.identity = pkg.evidence.identity[0];
|
|
1197
1069
|
}
|
|
1198
1070
|
}
|
|
1199
|
-
compMap[
|
|
1071
|
+
compMap[compMapKey] = component;
|
|
1200
1072
|
}
|
|
1201
1073
|
if (pkg.dependencies) {
|
|
1202
1074
|
Object.keys(pkg.dependencies)
|
|
@@ -1387,17 +1259,15 @@ const buildBomNSData = (options, pkgInfo, ptype, context) => {
|
|
|
1387
1259
|
components,
|
|
1388
1260
|
dependencies,
|
|
1389
1261
|
};
|
|
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
1262
|
bomNSData.bomJson = jsonTpl;
|
|
1398
1263
|
bomNSData.nsMapping = nsMapping;
|
|
1399
1264
|
bomNSData.dependencies = dependencies;
|
|
1400
1265
|
bomNSData.parentComponent = parentComponent;
|
|
1266
|
+
// Carry language-specific formulation data (e.g. Pixi) so that
|
|
1267
|
+
// postProcess can merge it when building the final formulation section.
|
|
1268
|
+
if (context?.formulationList?.length) {
|
|
1269
|
+
bomNSData.formulationList = context.formulationList;
|
|
1270
|
+
}
|
|
1401
1271
|
}
|
|
1402
1272
|
return bomNSData;
|
|
1403
1273
|
};
|
|
@@ -1489,6 +1359,7 @@ export async function createJarBom(path, options) {
|
|
|
1489
1359
|
*
|
|
1490
1360
|
* @param {string} path to the project
|
|
1491
1361
|
* @param {Object} options Parse options from the cli
|
|
1362
|
+
* @returns {Object|undefined} BOM object
|
|
1492
1363
|
*/
|
|
1493
1364
|
export function createAndroidBom(path, options) {
|
|
1494
1365
|
return createBinaryBom(path, options);
|
|
@@ -1499,6 +1370,7 @@ export function createAndroidBom(path, options) {
|
|
|
1499
1370
|
*
|
|
1500
1371
|
* @param {string} path to the project
|
|
1501
1372
|
* @param {Object} options Parse options from the cli
|
|
1373
|
+
* @returns {Object|undefined} BOM object
|
|
1502
1374
|
*/
|
|
1503
1375
|
export function createBinaryBom(path, options) {
|
|
1504
1376
|
const tempDir = mkdtempSync(join(getTmpDir(), "blint-tmp-"));
|
|
@@ -1522,6 +1394,7 @@ export function createBinaryBom(path, options) {
|
|
|
1522
1394
|
*
|
|
1523
1395
|
* @param {string} path to the project
|
|
1524
1396
|
* @param {Object} options Parse options from the cli
|
|
1397
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
1525
1398
|
*/
|
|
1526
1399
|
export async function createJavaBom(path, options) {
|
|
1527
1400
|
let jarNSMapping = {};
|
|
@@ -2769,6 +2642,7 @@ export async function createJavaBom(path, options) {
|
|
|
2769
2642
|
*
|
|
2770
2643
|
* @param {string} path to the project
|
|
2771
2644
|
* @param {Object} options Parse options from the cli
|
|
2645
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
2772
2646
|
*/
|
|
2773
2647
|
export async function createNodejsBom(path, options) {
|
|
2774
2648
|
let pkgList = [];
|
|
@@ -3203,8 +3077,9 @@ export async function createNodejsBom(path, options) {
|
|
|
3203
3077
|
const basePath = dirname(f);
|
|
3204
3078
|
// Determine the parent component
|
|
3205
3079
|
const packageJsonF = join(basePath, "package.json");
|
|
3206
|
-
const
|
|
3207
|
-
|
|
3080
|
+
const pnpmCjsHooks = join(basePath, ".pnpmfile.cjs");
|
|
3081
|
+
const pnpmMjsHooks = join(basePath, ".pnpmfile.mjs");
|
|
3082
|
+
if (safeExistsSync(pnpmMjsHooks) || safeExistsSync(pnpmCjsHooks)) {
|
|
3208
3083
|
thoughtLog("Wait, this pnpm project uses install hooks.");
|
|
3209
3084
|
}
|
|
3210
3085
|
if (!Object.keys(parentComponent).length) {
|
|
@@ -3687,6 +3562,7 @@ export async function createNodejsBom(path, options) {
|
|
|
3687
3562
|
*
|
|
3688
3563
|
* @param {String} path
|
|
3689
3564
|
* @param {Object} options
|
|
3565
|
+
* @returns {Object | null} BOM object, or `null` when `pixi.lock` is absent and `options.installDeps` is false
|
|
3690
3566
|
*/
|
|
3691
3567
|
export function createPixiBom(path, options) {
|
|
3692
3568
|
const allImports = {};
|
|
@@ -3765,6 +3641,7 @@ export function createPixiBom(path, options) {
|
|
|
3765
3641
|
*
|
|
3766
3642
|
* @param {string} path to the project
|
|
3767
3643
|
* @param {Object} options Parse options from the cli
|
|
3644
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
3768
3645
|
*/
|
|
3769
3646
|
export async function createPythonBom(path, options) {
|
|
3770
3647
|
let allImports = {};
|
|
@@ -4306,6 +4183,7 @@ export async function createPythonBom(path, options) {
|
|
|
4306
4183
|
*
|
|
4307
4184
|
* @param {string} path to the project
|
|
4308
4185
|
* @param {Object} options Parse options from the cli
|
|
4186
|
+
* @returns {Promise<Object | undefined>} Promise resolving to a BOM object or `undefined`
|
|
4309
4187
|
*/
|
|
4310
4188
|
export async function createGoBom(path, options) {
|
|
4311
4189
|
let pkgList = [];
|
|
@@ -4732,6 +4610,7 @@ export async function createGoBom(path, options) {
|
|
|
4732
4610
|
*
|
|
4733
4611
|
* @param {string} path to the project
|
|
4734
4612
|
* @param {Object} options Parse options from the cli
|
|
4613
|
+
* @returns {Promise<Object|undefined>} Promise resolving to a BOM object or undefined
|
|
4735
4614
|
*/
|
|
4736
4615
|
export async function createRustBom(path, options) {
|
|
4737
4616
|
let pkgList = [];
|
|
@@ -4871,6 +4750,7 @@ export async function createRustBom(path, options) {
|
|
|
4871
4750
|
*
|
|
4872
4751
|
* @param {string} path to the project
|
|
4873
4752
|
* @param {Object} options Parse options from the cli
|
|
4753
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
4874
4754
|
*/
|
|
4875
4755
|
export async function createDartBom(path, options) {
|
|
4876
4756
|
const pubFiles = getAllFiles(
|
|
@@ -4942,6 +4822,7 @@ export async function createDartBom(path, options) {
|
|
|
4942
4822
|
*
|
|
4943
4823
|
* @param {string} path to the project
|
|
4944
4824
|
* @param {Object} options Parse options from the cli
|
|
4825
|
+
* @returns {Object} BOM object
|
|
4945
4826
|
*/
|
|
4946
4827
|
export function createCppBom(path, options) {
|
|
4947
4828
|
let parentComponent;
|
|
@@ -5153,6 +5034,7 @@ export function createCppBom(path, options) {
|
|
|
5153
5034
|
*
|
|
5154
5035
|
* @param {string} path to the project
|
|
5155
5036
|
* @param {Object} options Parse options from the cli
|
|
5037
|
+
* @returns {Object} BOM object
|
|
5156
5038
|
*/
|
|
5157
5039
|
export function createClojureBom(path, options) {
|
|
5158
5040
|
const ednFiles = getAllFiles(
|
|
@@ -5270,6 +5152,7 @@ export function createClojureBom(path, options) {
|
|
|
5270
5152
|
*
|
|
5271
5153
|
* @param {string} path to the project
|
|
5272
5154
|
* @param {Object} options Parse options from the cli
|
|
5155
|
+
* @returns {Object} BOM object
|
|
5273
5156
|
*/
|
|
5274
5157
|
export function createHaskellBom(path, options) {
|
|
5275
5158
|
const cabalFiles = getAllFiles(
|
|
@@ -5302,6 +5185,7 @@ export function createHaskellBom(path, options) {
|
|
|
5302
5185
|
*
|
|
5303
5186
|
* @param {string} path to the project
|
|
5304
5187
|
* @param {Object} options Parse options from the cli
|
|
5188
|
+
* @returns {Object} BOM object
|
|
5305
5189
|
*/
|
|
5306
5190
|
export function createElixirBom(path, options) {
|
|
5307
5191
|
const mixFiles = getAllFiles(
|
|
@@ -5334,6 +5218,7 @@ export function createElixirBom(path, options) {
|
|
|
5334
5218
|
*
|
|
5335
5219
|
* @param {string} path to the project
|
|
5336
5220
|
* @param {Object} options Parse options from the cli
|
|
5221
|
+
* @returns {Object} BOM object
|
|
5337
5222
|
*/
|
|
5338
5223
|
export function createGitHubBom(path, options) {
|
|
5339
5224
|
const ghactionFiles = getAllFiles(
|
|
@@ -5365,6 +5250,7 @@ export function createGitHubBom(path, options) {
|
|
|
5365
5250
|
*
|
|
5366
5251
|
* @param {string} path to the project
|
|
5367
5252
|
* @param {Object} options Parse options from the cli
|
|
5253
|
+
* @returns {Object} BOM object
|
|
5368
5254
|
*/
|
|
5369
5255
|
export function createCloudBuildBom(path, options) {
|
|
5370
5256
|
const cbFiles = getAllFiles(path, "cloudbuild.yml", options);
|
|
@@ -5393,6 +5279,7 @@ export function createCloudBuildBom(path, options) {
|
|
|
5393
5279
|
*
|
|
5394
5280
|
* @param {string} _path to the project
|
|
5395
5281
|
* @param {Object} options Parse options from the cli
|
|
5282
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
5396
5283
|
*/
|
|
5397
5284
|
export function createOSBom(_path, options) {
|
|
5398
5285
|
console.warn(
|
|
@@ -5451,6 +5338,7 @@ export function createOSBom(_path, options) {
|
|
|
5451
5338
|
*
|
|
5452
5339
|
* @param {string} path to the project
|
|
5453
5340
|
* @param {Object} options Parse options from the cli
|
|
5341
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
5454
5342
|
*/
|
|
5455
5343
|
export async function createJenkinsBom(path, options) {
|
|
5456
5344
|
let pkgList = [];
|
|
@@ -5500,6 +5388,7 @@ export async function createJenkinsBom(path, options) {
|
|
|
5500
5388
|
*
|
|
5501
5389
|
* @param {string} path to the project
|
|
5502
5390
|
* @param {Object} options Parse options from the cli
|
|
5391
|
+
* @returns {Object} BOM object
|
|
5503
5392
|
*/
|
|
5504
5393
|
export function createHelmBom(path, options) {
|
|
5505
5394
|
let pkgList = [];
|
|
@@ -5532,6 +5421,7 @@ export function createHelmBom(path, options) {
|
|
|
5532
5421
|
*
|
|
5533
5422
|
* @param {string} path to the project
|
|
5534
5423
|
* @param {Object} options Parse options from the cli
|
|
5424
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
5535
5425
|
*/
|
|
5536
5426
|
export async function createSwiftBom(path, options) {
|
|
5537
5427
|
const swiftFiles = getAllFiles(
|
|
@@ -5676,6 +5566,7 @@ export async function createSwiftBom(path, options) {
|
|
|
5676
5566
|
*
|
|
5677
5567
|
* @param {string} path to the project
|
|
5678
5568
|
* @param {Object} options Parse options from the cli
|
|
5569
|
+
* @returns {Promise<Object | undefined>} Promise resolving to a BOM object, or `undefined` when no Podfiles are found
|
|
5679
5570
|
*/
|
|
5680
5571
|
export async function createCocoaBom(path, options) {
|
|
5681
5572
|
const cocoaFiles = getAllFiles(
|
|
@@ -5692,7 +5583,7 @@ export async function createCocoaBom(path, options) {
|
|
|
5692
5583
|
for (const podFile of cocoaFiles) {
|
|
5693
5584
|
const projectPath = dirname(podFile);
|
|
5694
5585
|
const lockFile = `${podFile}.lock`;
|
|
5695
|
-
if (!
|
|
5586
|
+
if (!safeExistsSync(lockFile) || options.deep) {
|
|
5696
5587
|
if (options.installDeps) {
|
|
5697
5588
|
executePodCommand(["install"], projectPath, options);
|
|
5698
5589
|
} else {
|
|
@@ -5830,6 +5721,7 @@ export async function createCocoaBom(path, options) {
|
|
|
5830
5721
|
*
|
|
5831
5722
|
* @param {string} path to the project
|
|
5832
5723
|
* @param {Object} options Parse options from the cli
|
|
5724
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
5833
5725
|
*/
|
|
5834
5726
|
export async function createNixBom(path, options) {
|
|
5835
5727
|
let pkgList = [];
|
|
@@ -5952,6 +5844,7 @@ export async function createNixBom(path, options) {
|
|
|
5952
5844
|
*
|
|
5953
5845
|
* @param {string} path to the project
|
|
5954
5846
|
* @param {Object} options Parse options from the cli
|
|
5847
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
5955
5848
|
*/
|
|
5956
5849
|
export async function createCaxaBom(path, options) {
|
|
5957
5850
|
let pkgList = [];
|
|
@@ -6003,6 +5896,7 @@ export async function createCaxaBom(path, options) {
|
|
|
6003
5896
|
*
|
|
6004
5897
|
* @param {string} path to the project
|
|
6005
5898
|
* @param {Object} options Parse options from the cli
|
|
5899
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
6006
5900
|
*/
|
|
6007
5901
|
export async function createContainerSpecLikeBom(path, options) {
|
|
6008
5902
|
let services = [];
|
|
@@ -6331,6 +6225,7 @@ export async function createContainerSpecLikeBom(path, options) {
|
|
|
6331
6225
|
*
|
|
6332
6226
|
* @param {string} path to the project
|
|
6333
6227
|
* @param {Object} options Parse options from the cli
|
|
6228
|
+
* @returns {Object} BOM object
|
|
6334
6229
|
*/
|
|
6335
6230
|
export function createPHPBom(path, options) {
|
|
6336
6231
|
let dependencies = [];
|
|
@@ -6438,7 +6333,7 @@ export function createPHPBom(path, options) {
|
|
|
6438
6333
|
// Track all the modules in a mono-repo
|
|
6439
6334
|
if (!Object.keys(parentComponent).length) {
|
|
6440
6335
|
parentComponent = { ...moduleParent };
|
|
6441
|
-
} else {
|
|
6336
|
+
} else if (moduleParent?.["bom-ref"]) {
|
|
6442
6337
|
parentComponent.components = parentComponent.components || [];
|
|
6443
6338
|
parentComponent.components.push(moduleParent);
|
|
6444
6339
|
}
|
|
@@ -6454,7 +6349,9 @@ export function createPHPBom(path, options) {
|
|
|
6454
6349
|
dependencies.splice(0, 0, {
|
|
6455
6350
|
ref: moduleParent["bom-ref"],
|
|
6456
6351
|
dependsOn: [
|
|
6457
|
-
...new Set(
|
|
6352
|
+
...new Set(
|
|
6353
|
+
retMap.rootList.map((p) => p["bom-ref"]).filter(Boolean),
|
|
6354
|
+
),
|
|
6458
6355
|
].sort(),
|
|
6459
6356
|
});
|
|
6460
6357
|
}
|
|
@@ -6467,9 +6364,9 @@ export function createPHPBom(path, options) {
|
|
|
6467
6364
|
}
|
|
6468
6365
|
// Complete the root dependency tree
|
|
6469
6366
|
if (parentComponent?.components?.length) {
|
|
6470
|
-
const parentDependsOn = parentComponent.components
|
|
6471
|
-
(d) => d["bom-ref"]
|
|
6472
|
-
|
|
6367
|
+
const parentDependsOn = parentComponent.components
|
|
6368
|
+
.map((d) => d["bom-ref"])
|
|
6369
|
+
.filter(Boolean);
|
|
6473
6370
|
dependencies = mergeDependencies(
|
|
6474
6371
|
[{ ref: parentComponent["bom-ref"], dependsOn: parentDependsOn }],
|
|
6475
6372
|
dependencies,
|
|
@@ -6491,6 +6388,7 @@ export function createPHPBom(path, options) {
|
|
|
6491
6388
|
*
|
|
6492
6389
|
* @param {string} path to the project
|
|
6493
6390
|
* @param {Object} options Parse options from the cli
|
|
6391
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
6494
6392
|
*/
|
|
6495
6393
|
export async function createRubyBom(path, options) {
|
|
6496
6394
|
// We can look for gem files within node_modules directory
|
|
@@ -6744,6 +6642,7 @@ export async function createRubyBom(path, options) {
|
|
|
6744
6642
|
*
|
|
6745
6643
|
* @param {string} path to the project
|
|
6746
6644
|
* @param {Object} options Parse options from the cli
|
|
6645
|
+
* @returns {Promise<Object|undefined>} Promise resolving to BOM object
|
|
6747
6646
|
*/
|
|
6748
6647
|
export async function createCsharpBom(path, options) {
|
|
6749
6648
|
let manifestFiles = [];
|
|
@@ -7179,11 +7078,202 @@ export async function createCsharpBom(path, options) {
|
|
|
7179
7078
|
});
|
|
7180
7079
|
}
|
|
7181
7080
|
|
|
7081
|
+
/**
|
|
7082
|
+
* Function to create BOM for VS Code / IDE extensions.
|
|
7083
|
+
* Supports two modes:
|
|
7084
|
+
* 1. Directory scan: Discovers `.vsix` files and installed extension directories
|
|
7085
|
+
* 2. IDE discovery: Automatically finds extensions installed by known IDEs
|
|
7086
|
+
*
|
|
7087
|
+
* @param {string} path to the project or directory to scan
|
|
7088
|
+
* @param {Object} options Parse options from the cli
|
|
7089
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
7090
|
+
*/
|
|
7091
|
+
export async function createVscodeExtensionBom(path, options) {
|
|
7092
|
+
let pkgList = [];
|
|
7093
|
+
let dependencies = [];
|
|
7094
|
+
const tempDirs = [];
|
|
7095
|
+
|
|
7096
|
+
// Mode 1: Scan for .vsix files in the given directory, or treat the input
|
|
7097
|
+
// path as a single .vsix file.
|
|
7098
|
+
let vsixFiles = [];
|
|
7099
|
+
if (path.endsWith(".vsix")) {
|
|
7100
|
+
vsixFiles = [resolve(path)];
|
|
7101
|
+
} else {
|
|
7102
|
+
vsixFiles = getAllFiles(
|
|
7103
|
+
path,
|
|
7104
|
+
`${options.multiProject ? "**/" : ""}*.vsix`,
|
|
7105
|
+
options,
|
|
7106
|
+
);
|
|
7107
|
+
}
|
|
7108
|
+
if (vsixFiles.length) {
|
|
7109
|
+
if (DEBUG_MODE) {
|
|
7110
|
+
console.log(`Found ${vsixFiles.length} .vsix file(s) to parse`);
|
|
7111
|
+
}
|
|
7112
|
+
for (const f of vsixFiles) {
|
|
7113
|
+
if (DEBUG_MODE) {
|
|
7114
|
+
console.log(`Parsing ${f}`);
|
|
7115
|
+
}
|
|
7116
|
+
// Get the extension component metadata
|
|
7117
|
+
const component = await parseVsixFile(f);
|
|
7118
|
+
if (component) {
|
|
7119
|
+
pkgList.push(component);
|
|
7120
|
+
}
|
|
7121
|
+
// Extract the vsix to a temp dir and run deep analysis
|
|
7122
|
+
const extractedDir = await extractVsixToTempDir(f);
|
|
7123
|
+
if (extractedDir) {
|
|
7124
|
+
tempDirs.push(extractedDir);
|
|
7125
|
+
const deepResult = await analyzeExtensionDir(extractedDir, options);
|
|
7126
|
+
if (deepResult.pkgList.length) {
|
|
7127
|
+
pkgList = pkgList.concat(deepResult.pkgList);
|
|
7128
|
+
}
|
|
7129
|
+
if (deepResult.dependencies.length) {
|
|
7130
|
+
dependencies = mergeDependencies(
|
|
7131
|
+
dependencies,
|
|
7132
|
+
deepResult.dependencies,
|
|
7133
|
+
);
|
|
7134
|
+
}
|
|
7135
|
+
}
|
|
7136
|
+
}
|
|
7137
|
+
}
|
|
7138
|
+
|
|
7139
|
+
// Mode 2: Auto-discover extensions from known IDE locations
|
|
7140
|
+
if (options.deep || options.projectType?.includes("ide-extensions")) {
|
|
7141
|
+
const ideDirs = discoverIdeExtensionDirs();
|
|
7142
|
+
if (ideDirs.length) {
|
|
7143
|
+
if (DEBUG_MODE) {
|
|
7144
|
+
console.log(
|
|
7145
|
+
`Discovered IDE extension directories: ${ideDirs.map((d) => `${d.name}: ${d.dir}`).join(", ")}`,
|
|
7146
|
+
);
|
|
7147
|
+
}
|
|
7148
|
+
const ideExtensions = collectInstalledExtensions(ideDirs);
|
|
7149
|
+
if (ideExtensions.length) {
|
|
7150
|
+
if (DEBUG_MODE) {
|
|
7151
|
+
console.log(
|
|
7152
|
+
`Found ${ideExtensions.length} IDE extension(s) from ${ideDirs.length} IDE location(s)`,
|
|
7153
|
+
);
|
|
7154
|
+
}
|
|
7155
|
+
pkgList = pkgList.concat(ideExtensions);
|
|
7156
|
+
// Deep analysis for IDE extension directories
|
|
7157
|
+
for (const ideDir of ideDirs) {
|
|
7158
|
+
await analyzeInstalledExtensionDirs(
|
|
7159
|
+
ideDir.dir,
|
|
7160
|
+
options,
|
|
7161
|
+
pkgList,
|
|
7162
|
+
dependencies,
|
|
7163
|
+
);
|
|
7164
|
+
}
|
|
7165
|
+
}
|
|
7166
|
+
}
|
|
7167
|
+
}
|
|
7168
|
+
|
|
7169
|
+
// Clean up temp directories from vsix extraction
|
|
7170
|
+
for (const td of tempDirs) {
|
|
7171
|
+
cleanupTempDir(td);
|
|
7172
|
+
}
|
|
7173
|
+
pkgList = trimComponents(pkgList);
|
|
7174
|
+
return buildBomNSData(options, pkgList, VSCODE_EXTENSION_PURL_TYPE, {
|
|
7175
|
+
src: path,
|
|
7176
|
+
filename: vsixFiles.join(", "),
|
|
7177
|
+
nsMapping: {},
|
|
7178
|
+
dependencies,
|
|
7179
|
+
});
|
|
7180
|
+
}
|
|
7181
|
+
|
|
7182
|
+
/**
|
|
7183
|
+
* Analyze an extracted extension directory for bundled dependencies.
|
|
7184
|
+
* Looks for npm lock files, node_modules, package.json files, minified JS,
|
|
7185
|
+
* and runs the babel-based analyzer on the source.
|
|
7186
|
+
*
|
|
7187
|
+
* @param {string} extDir Path to the extracted extension directory
|
|
7188
|
+
* @param {Object} options CLI options
|
|
7189
|
+
* @returns {Promise<{pkgList: Object[], dependencies: Object[]}>}
|
|
7190
|
+
*/
|
|
7191
|
+
async function analyzeExtensionDir(extDir, options) {
|
|
7192
|
+
const pkgList = [];
|
|
7193
|
+
let dependencies = [];
|
|
7194
|
+
// Check if the extension directory contains node.js project artifacts
|
|
7195
|
+
const hasPackageJson = safeExistsSync(join(extDir, "package.json"));
|
|
7196
|
+
const hasNodeModules = safeExistsSync(join(extDir, "node_modules"));
|
|
7197
|
+
const hasLockFile =
|
|
7198
|
+
safeExistsSync(join(extDir, "package-lock.json")) ||
|
|
7199
|
+
safeExistsSync(join(extDir, "yarn.lock")) ||
|
|
7200
|
+
safeExistsSync(join(extDir, "pnpm-lock.yaml"));
|
|
7201
|
+
|
|
7202
|
+
// If there are lock files or node_modules, run the full Node.js BOM generator
|
|
7203
|
+
if (hasPackageJson && (hasLockFile || hasNodeModules)) {
|
|
7204
|
+
if (DEBUG_MODE) {
|
|
7205
|
+
console.log(
|
|
7206
|
+
`Running Node.js BOM analysis on extension directory: ${extDir}`,
|
|
7207
|
+
);
|
|
7208
|
+
}
|
|
7209
|
+
const nodeBomOptions = {
|
|
7210
|
+
...options,
|
|
7211
|
+
path: extDir,
|
|
7212
|
+
multiProject: true,
|
|
7213
|
+
installDeps: false,
|
|
7214
|
+
noBabel: false,
|
|
7215
|
+
projectType: ["js"],
|
|
7216
|
+
};
|
|
7217
|
+
const bomData = await createNodejsBom(extDir, nodeBomOptions);
|
|
7218
|
+
if (bomData?.bomJson?.components?.length) {
|
|
7219
|
+
for (const comp of bomData.bomJson.components) {
|
|
7220
|
+
pkgList.push(comp);
|
|
7221
|
+
}
|
|
7222
|
+
}
|
|
7223
|
+
if (bomData?.bomJson?.dependencies?.length) {
|
|
7224
|
+
dependencies = mergeDependencies(
|
|
7225
|
+
dependencies,
|
|
7226
|
+
bomData.bomJson.dependencies,
|
|
7227
|
+
);
|
|
7228
|
+
}
|
|
7229
|
+
return { pkgList, dependencies };
|
|
7230
|
+
}
|
|
7231
|
+
return { pkgList, dependencies };
|
|
7232
|
+
}
|
|
7233
|
+
|
|
7234
|
+
/**
|
|
7235
|
+
* Run deep analysis on installed extension subdirectories within a parent
|
|
7236
|
+
* extensions directory. Each subdirectory represents an installed extension.
|
|
7237
|
+
*
|
|
7238
|
+
* @param {string} extensionsDir Parent directory containing extension subdirs
|
|
7239
|
+
* @param {Object} options CLI options
|
|
7240
|
+
* @param {Object[]} pkgList Mutable array to push discovered components into
|
|
7241
|
+
* @param {Object[]} dependencies Mutable array to merge dependencies into
|
|
7242
|
+
*/
|
|
7243
|
+
async function analyzeInstalledExtensionDirs(
|
|
7244
|
+
extensionsDir,
|
|
7245
|
+
options,
|
|
7246
|
+
pkgList,
|
|
7247
|
+
dependencies,
|
|
7248
|
+
) {
|
|
7249
|
+
let entries;
|
|
7250
|
+
try {
|
|
7251
|
+
entries = readdirSync(extensionsDir, { withFileTypes: true });
|
|
7252
|
+
} catch (_e) {
|
|
7253
|
+
return;
|
|
7254
|
+
}
|
|
7255
|
+
for (const entry of entries) {
|
|
7256
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) {
|
|
7257
|
+
continue;
|
|
7258
|
+
}
|
|
7259
|
+
const extDir = join(extensionsDir, entry.name);
|
|
7260
|
+
const deepResult = await analyzeExtensionDir(extDir, options);
|
|
7261
|
+
if (deepResult.pkgList.length) {
|
|
7262
|
+
pkgList.push(...deepResult.pkgList);
|
|
7263
|
+
}
|
|
7264
|
+
if (deepResult.dependencies.length) {
|
|
7265
|
+
const merged = mergeDependencies(dependencies, deepResult.dependencies);
|
|
7266
|
+
dependencies.splice(0, dependencies.length, ...merged);
|
|
7267
|
+
}
|
|
7268
|
+
}
|
|
7269
|
+
}
|
|
7270
|
+
|
|
7182
7271
|
/**
|
|
7183
7272
|
* Function to create bom object for cryptographic certificate files
|
|
7184
7273
|
*
|
|
7185
7274
|
* @param {string} path to the project
|
|
7186
7275
|
* @param {Object} options Parse options from the cli
|
|
7276
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
7187
7277
|
*/
|
|
7188
7278
|
export async function createCryptoCertsBom(path, options) {
|
|
7189
7279
|
const pkgList = [];
|
|
@@ -7220,191 +7310,6 @@ export async function createCryptoCertsBom(path, options) {
|
|
|
7220
7310
|
};
|
|
7221
7311
|
}
|
|
7222
7312
|
|
|
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
7313
|
/**
|
|
7409
7314
|
* Dedupe components
|
|
7410
7315
|
*
|
|
@@ -7461,11 +7366,13 @@ export function dedupeBom(options, components, parentComponent, dependencies) {
|
|
|
7461
7366
|
*
|
|
7462
7367
|
* @param {string[]} pathList list of to the project
|
|
7463
7368
|
* @param {Object} options Parse options from the cli
|
|
7369
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
7464
7370
|
*/
|
|
7465
7371
|
export async function createMultiXBom(pathList, options) {
|
|
7466
7372
|
let components = [];
|
|
7467
7373
|
let dependencies = [];
|
|
7468
7374
|
let bomData;
|
|
7375
|
+
let formulationList = [];
|
|
7469
7376
|
let parentComponent = determineParentComponent(options) || {};
|
|
7470
7377
|
let parentSubComponents = [];
|
|
7471
7378
|
options.createMultiXBom = true;
|
|
@@ -7676,6 +7583,9 @@ export async function createMultiXBom(pathList, options) {
|
|
|
7676
7583
|
parentSubComponents.push(bomData.parentComponent);
|
|
7677
7584
|
}
|
|
7678
7585
|
}
|
|
7586
|
+
if (bomData?.formulationList?.length) {
|
|
7587
|
+
formulationList = formulationList.concat(bomData.formulationList);
|
|
7588
|
+
}
|
|
7679
7589
|
}
|
|
7680
7590
|
if (hasAnyProjectType(["oci", "go"], options)) {
|
|
7681
7591
|
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
@@ -8168,6 +8078,27 @@ export async function createMultiXBom(pathList, options) {
|
|
|
8168
8078
|
}
|
|
8169
8079
|
}
|
|
8170
8080
|
}
|
|
8081
|
+
if (hasAnyProjectType(["vscode-extension"], options)) {
|
|
8082
|
+
bomData = await createVscodeExtensionBom(path, options);
|
|
8083
|
+
if (bomData?.bomJson?.components?.length) {
|
|
8084
|
+
if (DEBUG_MODE) {
|
|
8085
|
+
console.log(
|
|
8086
|
+
`Found ${bomData.bomJson.components.length} VS Code extension(s) at ${path}`,
|
|
8087
|
+
);
|
|
8088
|
+
}
|
|
8089
|
+
components = components.concat(bomData.bomJson.components);
|
|
8090
|
+
dependencies = mergeDependencies(
|
|
8091
|
+
dependencies,
|
|
8092
|
+
bomData.bomJson.dependencies,
|
|
8093
|
+
);
|
|
8094
|
+
if (
|
|
8095
|
+
bomData.parentComponent &&
|
|
8096
|
+
Object.keys(bomData.parentComponent).length
|
|
8097
|
+
) {
|
|
8098
|
+
parentSubComponents.push(bomData.parentComponent);
|
|
8099
|
+
}
|
|
8100
|
+
}
|
|
8101
|
+
}
|
|
8171
8102
|
// Collect any crypto keys
|
|
8172
8103
|
if (options.specVersion >= 1.6 && options.includeCrypto) {
|
|
8173
8104
|
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
@@ -8266,7 +8197,16 @@ export async function createMultiXBom(pathList, options) {
|
|
|
8266
8197
|
}
|
|
8267
8198
|
}
|
|
8268
8199
|
}
|
|
8269
|
-
|
|
8200
|
+
const multiResult = dedupeBom(
|
|
8201
|
+
options,
|
|
8202
|
+
components,
|
|
8203
|
+
parentComponent,
|
|
8204
|
+
dependencies,
|
|
8205
|
+
);
|
|
8206
|
+
if (formulationList.length) {
|
|
8207
|
+
multiResult.formulationList = formulationList;
|
|
8208
|
+
}
|
|
8209
|
+
return multiResult;
|
|
8270
8210
|
}
|
|
8271
8211
|
|
|
8272
8212
|
/**
|
|
@@ -8274,6 +8214,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
8274
8214
|
*
|
|
8275
8215
|
* @param {string} path to the project
|
|
8276
8216
|
* @param {Object} options Parse options from the cli
|
|
8217
|
+
* @returns {Promise<Object|undefined>} Promise resolving to BOM object, or undefined if path is not readable
|
|
8277
8218
|
*/
|
|
8278
8219
|
export async function createXBom(path, options) {
|
|
8279
8220
|
try {
|
|
@@ -8512,6 +8453,16 @@ export async function createXBom(path, options) {
|
|
|
8512
8453
|
return await createJenkinsBom(path, options);
|
|
8513
8454
|
}
|
|
8514
8455
|
|
|
8456
|
+
// VS Code extensions (.vsix files)
|
|
8457
|
+
const vsixFiles = getAllFiles(
|
|
8458
|
+
path,
|
|
8459
|
+
`${options.multiProject ? "**/" : ""}*.vsix`,
|
|
8460
|
+
options,
|
|
8461
|
+
);
|
|
8462
|
+
if (vsixFiles.length) {
|
|
8463
|
+
return await createVscodeExtensionBom(path, options);
|
|
8464
|
+
}
|
|
8465
|
+
|
|
8515
8466
|
// Helm charts
|
|
8516
8467
|
const chartFiles = getAllFiles(
|
|
8517
8468
|
path,
|
|
@@ -8619,6 +8570,7 @@ export async function createXBom(path, options) {
|
|
|
8619
8570
|
*
|
|
8620
8571
|
* @param {string} path to the project
|
|
8621
8572
|
* @param {Object} options Parse options from the cli
|
|
8573
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
8622
8574
|
*/
|
|
8623
8575
|
export async function createBom(path, options) {
|
|
8624
8576
|
let { projectType } = options;
|
|
@@ -8863,6 +8815,9 @@ export async function createBom(path, options) {
|
|
|
8863
8815
|
if (PROJECT_TYPE_ALIASES["caxa"].includes(projectType[0])) {
|
|
8864
8816
|
return await createCaxaBom(path, options);
|
|
8865
8817
|
}
|
|
8818
|
+
if (PROJECT_TYPE_ALIASES["vscode-extension"].includes(projectType[0])) {
|
|
8819
|
+
return await createVscodeExtensionBom(path, options);
|
|
8820
|
+
}
|
|
8866
8821
|
switch (projectType[0]) {
|
|
8867
8822
|
case "jar":
|
|
8868
8823
|
return createJarBom(path, options);
|