@cyclonedx/cdxgen 12.3.3 → 12.4.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 +69 -25
- package/bin/audit.js +21 -7
- package/bin/cdxgen.js +270 -127
- package/bin/convert.js +34 -15
- package/bin/hbom.js +495 -0
- package/bin/repl.js +592 -37
- package/bin/validate.js +31 -4
- package/bin/verify.js +18 -5
- package/data/README.md +298 -25
- package/data/component-tags.json +6 -0
- package/data/crypto-oid.json +16 -0
- package/data/cyclonedx-2.0-bundled.schema.json +7182 -0
- package/data/predictive-audit-allowlist.json +11 -0
- package/data/queries-darwin.json +12 -1
- package/data/queries-win.json +7 -1
- package/data/queries.json +39 -2
- package/data/rules/ai-agent-governance.yaml +16 -0
- package/data/rules/asar-archives.yaml +150 -0
- package/data/rules/chrome-extensions.yaml +8 -0
- package/data/rules/ci-permissions.yaml +42 -18
- package/data/rules/container-risk.yaml +14 -7
- package/data/rules/dependency-sources.yaml +11 -0
- package/data/rules/hbom-compliance.yaml +325 -0
- package/data/rules/hbom-performance.yaml +307 -0
- package/data/rules/hbom-security.yaml +248 -0
- package/data/rules/host-topology.yaml +165 -0
- package/data/rules/mcp-servers.yaml +18 -3
- package/data/rules/obom-runtime.yaml +907 -22
- package/data/rules/package-integrity.yaml +14 -0
- package/data/rules/rootfs-hardening.yaml +179 -0
- package/data/rules/vscode-extensions.yaml +9 -0
- package/lib/audit/index.js +210 -8
- package/lib/audit/index.poku.js +332 -0
- package/lib/audit/reporters.js +222 -0
- package/lib/audit/targets.js +146 -1
- package/lib/audit/targets.poku.js +186 -0
- package/lib/cli/asar.poku.js +328 -0
- package/lib/cli/index.js +527 -99
- package/lib/cli/index.poku.js +1469 -212
- package/lib/evinser/evinser.js +14 -9
- package/lib/helpers/analyzer.js +1406 -29
- package/lib/helpers/analyzer.poku.js +342 -0
- package/lib/helpers/analyzerScope.js +712 -0
- package/lib/helpers/asarutils.js +1556 -0
- package/lib/helpers/asarutils.poku.js +443 -0
- package/lib/helpers/auditCategories.js +12 -0
- package/lib/helpers/auditCategories.poku.js +32 -0
- package/lib/helpers/bomUtils.js +155 -1
- package/lib/helpers/bomUtils.poku.js +79 -1
- package/lib/helpers/cbomutils.js +271 -1
- package/lib/helpers/cbomutils.poku.js +248 -5
- package/lib/helpers/display.js +291 -1
- package/lib/helpers/display.poku.js +149 -0
- package/lib/helpers/evidenceUtils.js +58 -0
- package/lib/helpers/evidenceUtils.poku.js +54 -0
- package/lib/helpers/exportUtils.js +9 -0
- package/lib/helpers/gtfobins.js +142 -8
- package/lib/helpers/gtfobins.poku.js +24 -1
- package/lib/helpers/hbom.js +710 -0
- package/lib/helpers/hbom.poku.js +496 -0
- package/lib/helpers/hbomAnalysis.js +268 -0
- package/lib/helpers/hbomAnalysis.poku.js +249 -0
- package/lib/helpers/hbomLoader.js +35 -0
- package/lib/helpers/hostTopology.js +803 -0
- package/lib/helpers/hostTopology.poku.js +363 -0
- package/lib/helpers/inventoryStats.js +69 -0
- package/lib/helpers/inventoryStats.poku.js +86 -0
- package/lib/helpers/lolbas.js +19 -1
- package/lib/helpers/lolbas.poku.js +23 -0
- package/lib/helpers/osqueryTransform.js +47 -0
- package/lib/helpers/osqueryTransform.poku.js +47 -0
- package/lib/helpers/plugins.js +350 -0
- package/lib/helpers/plugins.poku.js +57 -0
- package/lib/helpers/protobom.js +209 -45
- package/lib/helpers/protobom.poku.js +183 -5
- package/lib/helpers/protobomLoader.js +43 -0
- package/lib/helpers/protobomLoader.poku.js +31 -0
- package/lib/helpers/remote/dependency-track.js +36 -3
- package/lib/helpers/remote/dependency-track.poku.js +44 -0
- package/lib/helpers/source.js +24 -0
- package/lib/helpers/source.poku.js +32 -0
- package/lib/helpers/utils.js +1438 -93
- package/lib/helpers/utils.poku.js +846 -4
- package/lib/managers/binary.e2e.poku.js +367 -0
- package/lib/managers/binary.js +2293 -353
- package/lib/managers/binary.poku.js +1699 -1
- package/lib/managers/docker.js +201 -79
- package/lib/managers/docker.poku.js +337 -12
- package/lib/server/server.js +4 -28
- package/lib/stages/postgen/annotator.js +38 -0
- package/lib/stages/postgen/annotator.poku.js +107 -1
- package/lib/stages/postgen/auditBom.js +121 -18
- package/lib/stages/postgen/auditBom.poku.js +1366 -31
- package/lib/stages/postgen/hostTopologyAudit.poku.js +186 -0
- package/lib/stages/postgen/postgen.js +406 -8
- package/lib/stages/postgen/postgen.poku.js +484 -0
- package/lib/stages/postgen/ruleEngine.js +116 -0
- package/lib/stages/pregen/envAudit.js +14 -3
- package/lib/validator/bomValidator.js +90 -38
- package/lib/validator/bomValidator.poku.js +90 -0
- package/lib/validator/complianceRules.js +4 -2
- package/lib/validator/index.poku.js +14 -0
- package/package.json +23 -21
- package/types/bin/hbom.d.ts +3 -0
- package/types/bin/hbom.d.ts.map +1 -0
- package/types/bin/repl.d.ts +1 -1
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts +44 -0
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/audit/reporters.d.ts +16 -0
- package/types/lib/audit/reporters.d.ts.map +1 -1
- package/types/lib/audit/targets.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts +16 -0
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +4 -0
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/helpers/analyzer.d.ts +33 -0
- package/types/lib/helpers/analyzer.d.ts.map +1 -1
- package/types/lib/helpers/analyzerScope.d.ts +11 -0
- package/types/lib/helpers/analyzerScope.d.ts.map +1 -0
- package/types/lib/helpers/asarutils.d.ts +34 -0
- package/types/lib/helpers/asarutils.d.ts.map +1 -0
- package/types/lib/helpers/auditCategories.d.ts +5 -0
- package/types/lib/helpers/auditCategories.d.ts.map +1 -1
- package/types/lib/helpers/bomUtils.d.ts +10 -0
- package/types/lib/helpers/bomUtils.d.ts.map +1 -1
- package/types/lib/helpers/cbomutils.d.ts +3 -2
- package/types/lib/helpers/cbomutils.d.ts.map +1 -1
- package/types/lib/helpers/display.d.ts.map +1 -1
- package/types/lib/helpers/evidenceUtils.d.ts +8 -0
- package/types/lib/helpers/evidenceUtils.d.ts.map +1 -0
- package/types/lib/helpers/exportUtils.d.ts.map +1 -1
- package/types/lib/helpers/gtfobins.d.ts +8 -0
- package/types/lib/helpers/gtfobins.d.ts.map +1 -1
- package/types/lib/helpers/hbom.d.ts +49 -0
- package/types/lib/helpers/hbom.d.ts.map +1 -0
- package/types/lib/helpers/hbomAnalysis.d.ts +76 -0
- package/types/lib/helpers/hbomAnalysis.d.ts.map +1 -0
- package/types/lib/helpers/hbomLoader.d.ts +7 -0
- package/types/lib/helpers/hbomLoader.d.ts.map +1 -0
- package/types/lib/helpers/hostTopology.d.ts +12 -0
- package/types/lib/helpers/hostTopology.d.ts.map +1 -0
- package/types/lib/helpers/inventoryStats.d.ts +11 -0
- package/types/lib/helpers/inventoryStats.d.ts.map +1 -0
- package/types/lib/helpers/lolbas.d.ts.map +1 -1
- package/types/lib/helpers/osqueryTransform.d.ts +3 -0
- package/types/lib/helpers/osqueryTransform.d.ts.map +1 -1
- package/types/lib/helpers/plugins.d.ts +58 -0
- package/types/lib/helpers/plugins.d.ts.map +1 -0
- package/types/lib/helpers/protobom.d.ts +5 -4
- package/types/lib/helpers/protobom.d.ts.map +1 -1
- package/types/lib/helpers/protobomLoader.d.ts +17 -0
- package/types/lib/helpers/protobomLoader.d.ts.map +1 -0
- package/types/lib/helpers/remote/dependency-track.d.ts +10 -3
- package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -1
- package/types/lib/helpers/source.d.ts.map +1 -1
- package/types/lib/helpers/utils.d.ts +45 -8
- package/types/lib/helpers/utils.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts +5 -0
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/server/server.d.ts +2 -1
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
- package/types/lib/stages/postgen/auditBom.d.ts +26 -1
- package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts +2 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
- package/types/lib/third-party/arborist/lib/node.d.ts +23 -0
- package/types/lib/third-party/arborist/lib/node.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/complianceRules.d.ts.map +1 -1
- package/data/spdx-model-v3.0.1.jsonld +0 -15999
package/lib/cli/index.js
CHANGED
|
@@ -11,7 +11,6 @@ import { platform as _platform, arch, homedir } from "node:os";
|
|
|
11
11
|
import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
12
12
|
import process from "node:process";
|
|
13
13
|
|
|
14
|
-
import got from "got";
|
|
15
14
|
import { PackageURL } from "packageurl-js";
|
|
16
15
|
import { gte, lte } from "semver";
|
|
17
16
|
import { parse } from "ssri";
|
|
@@ -35,8 +34,19 @@ import {
|
|
|
35
34
|
detectPythonMcpInventory,
|
|
36
35
|
findJSImportsExports,
|
|
37
36
|
} from "../helpers/analyzer.js";
|
|
37
|
+
import {
|
|
38
|
+
cleanupAsarTempDir,
|
|
39
|
+
extractAsarToTempDir,
|
|
40
|
+
parseAsarArchive,
|
|
41
|
+
rewriteExtractedArchivePaths,
|
|
42
|
+
} from "../helpers/asarutils.js";
|
|
38
43
|
import { expandBomAuditCategories } from "../helpers/auditCategories.js";
|
|
44
|
+
import {
|
|
45
|
+
setCycloneDxFormat,
|
|
46
|
+
toCycloneDxSpecVersionString,
|
|
47
|
+
} from "../helpers/bomUtils.js";
|
|
39
48
|
import { parseCaxaMetadata } from "../helpers/caxa.js";
|
|
49
|
+
import { collectSourceCryptoComponents } from "../helpers/cbomutils.js";
|
|
40
50
|
import {
|
|
41
51
|
CHROME_EXTENSION_PURL_TYPE,
|
|
42
52
|
collectChromeExtensionsFromPath,
|
|
@@ -49,15 +59,19 @@ import {
|
|
|
49
59
|
trimComponents,
|
|
50
60
|
} from "../helpers/depsUtils.js";
|
|
51
61
|
import { GIT_COMMAND } from "../helpers/envcontext.js";
|
|
52
|
-
import { thoughtLog } from "../helpers/logger.js";
|
|
53
62
|
import {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
63
|
+
createHbomDocument,
|
|
64
|
+
ensureHbomRuntimeSupport,
|
|
65
|
+
ensureNoMixedHbomProjectTypes,
|
|
66
|
+
hasHbomProjectType,
|
|
67
|
+
} from "../helpers/hbom.js";
|
|
68
|
+
import { mergeHostInventoryBoms } from "../helpers/hostTopology.js";
|
|
69
|
+
import { thoughtLog } from "../helpers/logger.js";
|
|
70
|
+
import { enrichComponentWithMcpMetadata } from "../helpers/mcp.js";
|
|
57
71
|
import { isPyLockFile } from "../helpers/pylockutils.js";
|
|
58
72
|
import {
|
|
59
73
|
buildDependencyTrackBomPayload,
|
|
60
|
-
|
|
74
|
+
getDependencyTrackBomApiUrl,
|
|
61
75
|
} from "../helpers/remote/dependency-track.js";
|
|
62
76
|
import { table } from "../helpers/table.js";
|
|
63
77
|
import {
|
|
@@ -71,6 +85,7 @@ import {
|
|
|
71
85
|
CARGO_CMD,
|
|
72
86
|
CDXGEN_VERSION,
|
|
73
87
|
CLJ_CMD,
|
|
88
|
+
cdxgenAgent,
|
|
74
89
|
checksumFile,
|
|
75
90
|
cleanupPlugin,
|
|
76
91
|
collectGemModuleNames,
|
|
@@ -111,6 +126,7 @@ import {
|
|
|
111
126
|
getTmpDir,
|
|
112
127
|
hasAnyProjectType,
|
|
113
128
|
includeMavenTestScope,
|
|
129
|
+
isAllowedHttpHost,
|
|
114
130
|
isDryRun,
|
|
115
131
|
isFeatureEnabled,
|
|
116
132
|
isMac,
|
|
@@ -201,6 +217,7 @@ import {
|
|
|
201
217
|
readZipEntry,
|
|
202
218
|
recomputeScope,
|
|
203
219
|
recordActivity,
|
|
220
|
+
recordSensitiveFileRead,
|
|
204
221
|
resetActivityContext,
|
|
205
222
|
SWIFT_CMD,
|
|
206
223
|
safeExistsSync,
|
|
@@ -226,10 +243,12 @@ import {
|
|
|
226
243
|
VSCODE_EXTENSION_PURL_TYPE,
|
|
227
244
|
} from "../helpers/vsixutils.js";
|
|
228
245
|
import {
|
|
246
|
+
enrichOSComponentsWithTrustData,
|
|
229
247
|
executeOsQuery,
|
|
230
248
|
getBinaryBom,
|
|
231
249
|
getDotnetSlices,
|
|
232
250
|
getOSPackages,
|
|
251
|
+
getPluginToolComponents,
|
|
233
252
|
} from "../managers/binary.js";
|
|
234
253
|
import {
|
|
235
254
|
addSkippedSrcFiles,
|
|
@@ -369,6 +388,18 @@ const shouldIncludeNodeModulesDir = (options = {}, baseProjectTypes = []) => {
|
|
|
369
388
|
);
|
|
370
389
|
};
|
|
371
390
|
|
|
391
|
+
const hasExplicitProjectTypeSelection = (options, baseProjectType) => {
|
|
392
|
+
options = options || {};
|
|
393
|
+
const projectTypes = Array.isArray(options.projectType)
|
|
394
|
+
? options.projectType
|
|
395
|
+
: options.projectType
|
|
396
|
+
? [options.projectType]
|
|
397
|
+
: [];
|
|
398
|
+
return projectTypes.some((selectedProjectType) =>
|
|
399
|
+
PROJECT_TYPE_ALIASES[baseProjectType]?.includes(selectedProjectType),
|
|
400
|
+
);
|
|
401
|
+
};
|
|
402
|
+
|
|
372
403
|
const determineParentComponent = (options) => {
|
|
373
404
|
let parentComponent;
|
|
374
405
|
if (options.parentComponent && Object.keys(options.parentComponent).length) {
|
|
@@ -840,6 +871,18 @@ function addMetadata(parentComponent = {}, options = {}, context = {}) {
|
|
|
840
871
|
value: options.allOSComponentTypes.sort().join("\\n"),
|
|
841
872
|
});
|
|
842
873
|
}
|
|
874
|
+
if (Number.isInteger(options?.unpackagedExecutableCount)) {
|
|
875
|
+
mproperties.push({
|
|
876
|
+
name: "cdx:container:unpackagedExecutableCount",
|
|
877
|
+
value: String(options.unpackagedExecutableCount),
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
if (Number.isInteger(options?.unpackagedSharedLibraryCount)) {
|
|
881
|
+
mproperties.push({
|
|
882
|
+
name: "cdx:container:unpackagedSharedLibraryCount",
|
|
883
|
+
value: String(options.unpackagedSharedLibraryCount),
|
|
884
|
+
});
|
|
885
|
+
}
|
|
843
886
|
// Should we move these to formulation?
|
|
844
887
|
if (options?.bundledSdks?.length) {
|
|
845
888
|
for (const sdk of options.bundledSdks) {
|
|
@@ -943,6 +986,9 @@ export function listComponents(options, allImports, pkg, ptype = "npm") {
|
|
|
943
986
|
return Object.keys(compMap).map((k) => compMap[k]);
|
|
944
987
|
}
|
|
945
988
|
|
|
989
|
+
// These component types do not have PURLs
|
|
990
|
+
const NON_PURL_TYPES = ["cryptographic-asset", "file", "data"];
|
|
991
|
+
|
|
946
992
|
/**
|
|
947
993
|
* Given the specified package, create a CycloneDX component and add it to the list.
|
|
948
994
|
*/
|
|
@@ -979,9 +1025,9 @@ function addComponent(
|
|
|
979
1025
|
}
|
|
980
1026
|
const version = pkg.version || "";
|
|
981
1027
|
const licenses = pkg.licenses || getLicenses(pkg);
|
|
982
|
-
let purl =
|
|
983
|
-
|
|
984
|
-
new PackageURL(
|
|
1028
|
+
let purl = pkg.purl;
|
|
1029
|
+
if (!purl && ptype) {
|
|
1030
|
+
purl = new PackageURL(
|
|
985
1031
|
ptype,
|
|
986
1032
|
encodeForPurl(group),
|
|
987
1033
|
encodeForPurl(name),
|
|
@@ -989,9 +1035,9 @@ function addComponent(
|
|
|
989
1035
|
pkg.qualifiers,
|
|
990
1036
|
encodeForPurl(pkg.subpath),
|
|
991
1037
|
);
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
if (ptype
|
|
1038
|
+
}
|
|
1039
|
+
let purlString = purl?.toString();
|
|
1040
|
+
if (NON_PURL_TYPES.includes(ptype) || NON_PURL_TYPES.includes(pkg.type)) {
|
|
995
1041
|
purl = undefined;
|
|
996
1042
|
purlString = undefined;
|
|
997
1043
|
}
|
|
@@ -1102,7 +1148,7 @@ function addComponent(
|
|
|
1102
1148
|
if (pkg.properties?.length) {
|
|
1103
1149
|
component.properties = pkg.properties;
|
|
1104
1150
|
}
|
|
1105
|
-
if (pkg.cryptoProperties
|
|
1151
|
+
if (pkg.cryptoProperties && typeof pkg.cryptoProperties === "object") {
|
|
1106
1152
|
component.cryptoProperties = pkg.cryptoProperties;
|
|
1107
1153
|
}
|
|
1108
1154
|
// Retain nested components
|
|
@@ -1351,13 +1397,16 @@ const buildBomNSData = (options, pkgInfo, ptype, context) => {
|
|
|
1351
1397
|
// CycloneDX Json Template
|
|
1352
1398
|
const jsonTpl = {
|
|
1353
1399
|
bomFormat: "CycloneDX",
|
|
1354
|
-
specVersion:
|
|
1400
|
+
specVersion: toCycloneDxSpecVersionString(options.specVersion || "1.7"),
|
|
1355
1401
|
serialNumber: serialNum,
|
|
1356
1402
|
version: 1,
|
|
1357
1403
|
metadata: metadata,
|
|
1358
1404
|
components,
|
|
1359
1405
|
dependencies,
|
|
1360
1406
|
};
|
|
1407
|
+
setCycloneDxFormat(jsonTpl, jsonTpl.specVersion, {
|
|
1408
|
+
preserveLegacyBomFormat: true,
|
|
1409
|
+
});
|
|
1361
1410
|
if (services.length) {
|
|
1362
1411
|
jsonTpl.services = mergeServices([], services);
|
|
1363
1412
|
}
|
|
@@ -2769,11 +2818,10 @@ export async function createJavaBom(path, options) {
|
|
|
2769
2818
|
}
|
|
2770
2819
|
|
|
2771
2820
|
/**
|
|
2772
|
-
*
|
|
2821
|
+
* Identify the requested AI inventory project types.
|
|
2773
2822
|
*
|
|
2774
|
-
* @param {string} path to the project
|
|
2775
2823
|
* @param {Object} options Parse options from the cli
|
|
2776
|
-
* @returns {
|
|
2824
|
+
* @returns {string[]} Requested AI inventory types
|
|
2777
2825
|
*/
|
|
2778
2826
|
function getRequestedAiInventoryTypes(options) {
|
|
2779
2827
|
return AI_INVENTORY_PROJECT_TYPES.filter((type) =>
|
|
@@ -2787,6 +2835,48 @@ function getExcludedAiInventoryTypes(options) {
|
|
|
2787
2835
|
);
|
|
2788
2836
|
}
|
|
2789
2837
|
|
|
2838
|
+
function filterIncludedAiInventoryTypes(
|
|
2839
|
+
includedAiInventoryTypes,
|
|
2840
|
+
excludedAiInventoryTypes,
|
|
2841
|
+
) {
|
|
2842
|
+
return [...new Set(includedAiInventoryTypes)].filter(
|
|
2843
|
+
(type) => !excludedAiInventoryTypes.includes(type),
|
|
2844
|
+
);
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
/**
|
|
2848
|
+
* Determine which AI inventory types should be collected for a scan.
|
|
2849
|
+
*
|
|
2850
|
+
* This combines explicit project-type opt-ins with BOM audit category-driven
|
|
2851
|
+
* opt-ins, then removes any explicitly excluded inventory types.
|
|
2852
|
+
*
|
|
2853
|
+
* @param {Object} options Parse options from the CLI
|
|
2854
|
+
* @returns {string[]} AI inventory types to collect
|
|
2855
|
+
*/
|
|
2856
|
+
function getIncludedAiInventoryTypes(options) {
|
|
2857
|
+
const requestedAiInventoryTypes = getRequestedAiInventoryTypes(options);
|
|
2858
|
+
const excludedAiInventoryTypes = getExcludedAiInventoryTypes(options);
|
|
2859
|
+
const exactAiInventoryType = getExactAiInventoryType(options);
|
|
2860
|
+
if (exactAiInventoryType) {
|
|
2861
|
+
return filterIncludedAiInventoryTypes(
|
|
2862
|
+
requestedAiInventoryTypes,
|
|
2863
|
+
excludedAiInventoryTypes,
|
|
2864
|
+
);
|
|
2865
|
+
}
|
|
2866
|
+
const auditCategories = expandBomAuditCategories(options?.bomAuditCategories);
|
|
2867
|
+
const includedAiInventoryTypes = [...requestedAiInventoryTypes];
|
|
2868
|
+
if (auditCategories.includes("ai-agent")) {
|
|
2869
|
+
includedAiInventoryTypes.push("ai-skill");
|
|
2870
|
+
}
|
|
2871
|
+
if (auditCategories.includes("mcp-server")) {
|
|
2872
|
+
includedAiInventoryTypes.push("mcp");
|
|
2873
|
+
}
|
|
2874
|
+
return filterIncludedAiInventoryTypes(
|
|
2875
|
+
includedAiInventoryTypes,
|
|
2876
|
+
excludedAiInventoryTypes,
|
|
2877
|
+
);
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2790
2880
|
function getExactAiInventoryType(options) {
|
|
2791
2881
|
const requestedAiInventoryTypes = getRequestedAiInventoryTypes(options);
|
|
2792
2882
|
return requestedAiInventoryTypes.length === 1 &&
|
|
@@ -2796,22 +2886,14 @@ function getExactAiInventoryType(options) {
|
|
|
2796
2886
|
: undefined;
|
|
2797
2887
|
}
|
|
2798
2888
|
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
)
|
|
2808
|
-
) {
|
|
2809
|
-
return true;
|
|
2810
|
-
}
|
|
2811
|
-
return Object.keys(allImports).some((importName) => {
|
|
2812
|
-
const classification = classifyMcpReference(importName);
|
|
2813
|
-
return classification.isMcp;
|
|
2814
|
-
});
|
|
2889
|
+
/**
|
|
2890
|
+
* Determine whether MCP source-code analysis should run for the current scan.
|
|
2891
|
+
*
|
|
2892
|
+
* @param {string[]} includedAiInventoryTypes AI inventory types selected for collection
|
|
2893
|
+
* @returns {boolean} True when MCP inventory collection is enabled
|
|
2894
|
+
*/
|
|
2895
|
+
function shouldDetectMcpInventory(includedAiInventoryTypes) {
|
|
2896
|
+
return includedAiInventoryTypes.includes("mcp");
|
|
2815
2897
|
}
|
|
2816
2898
|
|
|
2817
2899
|
function summarizeAiInventoryNames(subjects, discoveryPath, kindSet) {
|
|
@@ -2906,14 +2988,8 @@ export async function createNodejsBom(path, options) {
|
|
|
2906
2988
|
let parentComponent = {};
|
|
2907
2989
|
const parentSubComponents = [];
|
|
2908
2990
|
let ppurl = "";
|
|
2909
|
-
const requestedAiInventoryTypes = getRequestedAiInventoryTypes(options);
|
|
2910
|
-
const excludedAiInventoryTypes = getExcludedAiInventoryTypes(options);
|
|
2911
2991
|
const exactAiInventoryType = getExactAiInventoryType(options);
|
|
2912
|
-
const includedAiInventoryTypes =
|
|
2913
|
-
? requestedAiInventoryTypes
|
|
2914
|
-
: AI_INVENTORY_PROJECT_TYPES.filter(
|
|
2915
|
-
(type) => !excludedAiInventoryTypes.includes(type),
|
|
2916
|
-
);
|
|
2992
|
+
const includedAiInventoryTypes = getIncludedAiInventoryTypes(options);
|
|
2917
2993
|
let aiInventory = { components: [], dependencies: [], services: [] };
|
|
2918
2994
|
// Docker mode requires special handling
|
|
2919
2995
|
if (hasAnyProjectType(["docker", "oci", "container", "os"], options, false)) {
|
|
@@ -2965,16 +3041,13 @@ export async function createNodejsBom(path, options) {
|
|
|
2965
3041
|
const retData = await findJSImportsExports(path, options.deep);
|
|
2966
3042
|
allImports = retData.allImports;
|
|
2967
3043
|
allExports = retData.allExports;
|
|
2968
|
-
if (shouldDetectMcpInventory(
|
|
3044
|
+
if (shouldDetectMcpInventory(includedAiInventoryTypes)) {
|
|
2969
3045
|
mcpInventory = detectMcpInventory(path, options.deep);
|
|
2970
3046
|
}
|
|
2971
3047
|
}
|
|
2972
3048
|
if (includedAiInventoryTypes.length) {
|
|
2973
3049
|
aiInventory = collectAiInventory(path, options, includedAiInventoryTypes);
|
|
2974
3050
|
}
|
|
2975
|
-
if (excludedAiInventoryTypes.includes("mcp")) {
|
|
2976
|
-
mcpInventory = { components: [], dependencies: [], services: [] };
|
|
2977
|
-
}
|
|
2978
3051
|
const aiInventorySummary = summarizeAiInventory(aiInventory);
|
|
2979
3052
|
if (!exactAiInventoryType) {
|
|
2980
3053
|
if (aiInventorySummary.instructionCount || aiInventorySummary.skillCount) {
|
|
@@ -2987,7 +3060,6 @@ export async function createNodejsBom(path, options) {
|
|
|
2987
3060
|
`I found ${aiInventorySummary.mcpConfigCount} MCP config component(s). Use '--exclude-type mcp' to drop them, or '--bom-audit --bom-audit-categories ai-inventory --tlp-classification AMBER' to keep and flag them.`,
|
|
2988
3061
|
);
|
|
2989
3062
|
}
|
|
2990
|
-
emitAiInventorySummary(aiInventory, path);
|
|
2991
3063
|
}
|
|
2992
3064
|
if (exactAiInventoryType === "ai-skill") {
|
|
2993
3065
|
const exactComponents = trimComponents([...(aiInventory.components || [])]);
|
|
@@ -3167,11 +3239,15 @@ export async function createNodejsBom(path, options) {
|
|
|
3167
3239
|
}
|
|
3168
3240
|
const basePath = dirname(apkgJson);
|
|
3169
3241
|
let npmrcData;
|
|
3170
|
-
|
|
3242
|
+
const npmrcFile = join(basePath, ".npmrc");
|
|
3243
|
+
if (safeExistsSync(npmrcFile)) {
|
|
3171
3244
|
thoughtLog(
|
|
3172
3245
|
"Wait, there is a .npmrc file here! I'm going to check if it has anything malicious.",
|
|
3173
3246
|
);
|
|
3174
|
-
npmrcData = readFileSync(
|
|
3247
|
+
npmrcData = readFileSync(npmrcFile, "utf-8");
|
|
3248
|
+
recordSensitiveFileRead(npmrcFile, {
|
|
3249
|
+
label: "npm registry configuration",
|
|
3250
|
+
});
|
|
3175
3251
|
const npmrcObj = parseNpmrc(npmrcData);
|
|
3176
3252
|
for (const [key, value] of Object.entries(npmrcObj)) {
|
|
3177
3253
|
const baseKey = key.replace(/^(?:\/\/[^/]+\/|@[^:]+:)/, "");
|
|
@@ -4102,14 +4178,7 @@ export async function createPythonBom(path, options) {
|
|
|
4102
4178
|
let dependencies = [];
|
|
4103
4179
|
let pkgList = [];
|
|
4104
4180
|
let formulationList = [];
|
|
4105
|
-
const
|
|
4106
|
-
const excludedAiInventoryTypes = getExcludedAiInventoryTypes(options);
|
|
4107
|
-
const includedAiInventoryTypes = AI_INVENTORY_PROJECT_TYPES.filter(
|
|
4108
|
-
(type) =>
|
|
4109
|
-
(!requestedAiInventoryTypes.length ||
|
|
4110
|
-
requestedAiInventoryTypes.includes(type)) &&
|
|
4111
|
-
!excludedAiInventoryTypes.includes(type),
|
|
4112
|
-
);
|
|
4181
|
+
const includedAiInventoryTypes = getIncludedAiInventoryTypes(options);
|
|
4113
4182
|
let aiInventory = { components: [], dependencies: [], services: [] };
|
|
4114
4183
|
let mcpInventory = { components: [], dependencies: [], services: [] };
|
|
4115
4184
|
const tempDir = safeMkdtempSync(join(getTmpDir(), "cdxgen-venv-"));
|
|
@@ -5955,6 +6024,7 @@ export function createOSBom(_path, options) {
|
|
|
5955
6024
|
let pkgList = [];
|
|
5956
6025
|
let bomData = {};
|
|
5957
6026
|
let parentComponent = {};
|
|
6027
|
+
let externalTools = getPluginToolComponents(["osquery"]);
|
|
5958
6028
|
for (const queryCategory of Object.keys(osQueries)) {
|
|
5959
6029
|
const queryObj = osQueries[queryCategory];
|
|
5960
6030
|
const results = executeOsQuery(queryObj.query);
|
|
@@ -5973,6 +6043,20 @@ export function createOSBom(_path, options) {
|
|
|
5973
6043
|
);
|
|
5974
6044
|
}
|
|
5975
6045
|
} // for
|
|
6046
|
+
const hostTrustInventory = enrichOSComponentsWithTrustData(pkgList);
|
|
6047
|
+
if (hostTrustInventory?.components?.length) {
|
|
6048
|
+
pkgList = hostTrustInventory.components;
|
|
6049
|
+
}
|
|
6050
|
+
if (hostTrustInventory?.tools?.length) {
|
|
6051
|
+
externalTools = externalTools.concat(hostTrustInventory.tools);
|
|
6052
|
+
}
|
|
6053
|
+
if (externalTools.length) {
|
|
6054
|
+
options.tools = Array.from(
|
|
6055
|
+
new Map(
|
|
6056
|
+
externalTools.map((tool) => [tool["bom-ref"] || tool.name, tool]),
|
|
6057
|
+
).values(),
|
|
6058
|
+
);
|
|
6059
|
+
}
|
|
5976
6060
|
if (pkgList.length) {
|
|
5977
6061
|
bomData = buildBomNSData(options, pkgList, "", {
|
|
5978
6062
|
src: "",
|
|
@@ -7776,6 +7860,10 @@ export async function createVscodeExtensionBom(path, options) {
|
|
|
7776
7860
|
let pkgList = [];
|
|
7777
7861
|
let dependencies = [];
|
|
7778
7862
|
const tempDirs = [];
|
|
7863
|
+
const shouldDiscoverInstalledIdeExtensions = hasExplicitProjectTypeSelection(
|
|
7864
|
+
options,
|
|
7865
|
+
"vscode-extension",
|
|
7866
|
+
);
|
|
7779
7867
|
|
|
7780
7868
|
// Mode 1: Scan for .vsix files in the given directory, or treat the input
|
|
7781
7869
|
// path as a single .vsix file.
|
|
@@ -7821,7 +7909,7 @@ export async function createVscodeExtensionBom(path, options) {
|
|
|
7821
7909
|
}
|
|
7822
7910
|
|
|
7823
7911
|
// Mode 2: Auto-discover extensions from known IDE locations
|
|
7824
|
-
if (
|
|
7912
|
+
if (shouldDiscoverInstalledIdeExtensions) {
|
|
7825
7913
|
const ideDirs = discoverIdeExtensionDirs();
|
|
7826
7914
|
if (ideDirs.length) {
|
|
7827
7915
|
if (DEBUG_MODE) {
|
|
@@ -7863,6 +7951,198 @@ export async function createVscodeExtensionBom(path, options) {
|
|
|
7863
7951
|
});
|
|
7864
7952
|
}
|
|
7865
7953
|
|
|
7954
|
+
/**
|
|
7955
|
+
* Function to create BOM for Electron ASAR archives.
|
|
7956
|
+
*
|
|
7957
|
+
* @param {string} path to a single archive or a directory to scan
|
|
7958
|
+
* @param {Object} options Parse options from the cli
|
|
7959
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
7960
|
+
*/
|
|
7961
|
+
export async function createAsarBom(path, options) {
|
|
7962
|
+
let pkgList = [];
|
|
7963
|
+
let dependencies = [];
|
|
7964
|
+
let parentComponent = {};
|
|
7965
|
+
const tempDirs = [];
|
|
7966
|
+
const processedArchives = new Set();
|
|
7967
|
+
const maxNestedAsarDepth = 4;
|
|
7968
|
+
const explicitAsarPath = path.endsWith(".asar") ? resolve(path) : undefined;
|
|
7969
|
+
const asarFiles = explicitAsarPath
|
|
7970
|
+
? [explicitAsarPath]
|
|
7971
|
+
: getAllFiles(path, `${options.multiProject ? "**/" : ""}*.asar`, options);
|
|
7972
|
+
|
|
7973
|
+
const aggregateArchiveResults = (
|
|
7974
|
+
archiveAnalysis,
|
|
7975
|
+
isPrimaryArchive = false,
|
|
7976
|
+
) => {
|
|
7977
|
+
if (
|
|
7978
|
+
archiveAnalysis.parentComponent &&
|
|
7979
|
+
Object.keys(archiveAnalysis.parentComponent).length
|
|
7980
|
+
) {
|
|
7981
|
+
if (isPrimaryArchive && !Object.keys(parentComponent).length) {
|
|
7982
|
+
parentComponent = archiveAnalysis.parentComponent;
|
|
7983
|
+
} else {
|
|
7984
|
+
pkgList.push(archiveAnalysis.parentComponent);
|
|
7985
|
+
}
|
|
7986
|
+
}
|
|
7987
|
+
if (archiveAnalysis.components?.length) {
|
|
7988
|
+
pkgList = pkgList.concat(archiveAnalysis.components);
|
|
7989
|
+
}
|
|
7990
|
+
if (archiveAnalysis.dependencies?.length) {
|
|
7991
|
+
dependencies = mergeDependencies(
|
|
7992
|
+
dependencies,
|
|
7993
|
+
archiveAnalysis.dependencies,
|
|
7994
|
+
parentComponent,
|
|
7995
|
+
);
|
|
7996
|
+
}
|
|
7997
|
+
};
|
|
7998
|
+
|
|
7999
|
+
const analyzeExtractedArchive = async (
|
|
8000
|
+
extractedDir,
|
|
8001
|
+
archiveAnalysis,
|
|
8002
|
+
archiveIdentityPath,
|
|
8003
|
+
) => {
|
|
8004
|
+
if (!archiveAnalysis.packageManifestPaths?.length) {
|
|
8005
|
+
return undefined;
|
|
8006
|
+
}
|
|
8007
|
+
const nodeBomOptions = {
|
|
8008
|
+
...options,
|
|
8009
|
+
installDeps: false,
|
|
8010
|
+
multiProject: true,
|
|
8011
|
+
noBabel: false,
|
|
8012
|
+
path: extractedDir,
|
|
8013
|
+
projectType: ["js"],
|
|
8014
|
+
};
|
|
8015
|
+
const nodeBomData = await createNodejsBom(extractedDir, nodeBomOptions);
|
|
8016
|
+
if (nodeBomData?.bomJson?.components?.length) {
|
|
8017
|
+
rewriteExtractedArchivePaths(
|
|
8018
|
+
nodeBomData.bomJson.components,
|
|
8019
|
+
extractedDir,
|
|
8020
|
+
archiveIdentityPath,
|
|
8021
|
+
);
|
|
8022
|
+
pkgList = pkgList.concat(nodeBomData.bomJson.components);
|
|
8023
|
+
}
|
|
8024
|
+
if (nodeBomData?.bomJson?.dependencies?.length) {
|
|
8025
|
+
dependencies = mergeDependencies(
|
|
8026
|
+
dependencies,
|
|
8027
|
+
nodeBomData.bomJson.dependencies,
|
|
8028
|
+
);
|
|
8029
|
+
}
|
|
8030
|
+
if (
|
|
8031
|
+
archiveAnalysis.parentComponent?.["bom-ref"] &&
|
|
8032
|
+
nodeBomData?.parentComponent?.["bom-ref"] &&
|
|
8033
|
+
archiveAnalysis.parentComponent["bom-ref"] !==
|
|
8034
|
+
nodeBomData.parentComponent["bom-ref"]
|
|
8035
|
+
) {
|
|
8036
|
+
rewriteExtractedArchivePaths(
|
|
8037
|
+
nodeBomData.parentComponent,
|
|
8038
|
+
extractedDir,
|
|
8039
|
+
archiveIdentityPath,
|
|
8040
|
+
);
|
|
8041
|
+
dependencies = mergeDependencies(dependencies, [
|
|
8042
|
+
{
|
|
8043
|
+
ref: archiveAnalysis.parentComponent["bom-ref"],
|
|
8044
|
+
dependsOn: [nodeBomData.parentComponent["bom-ref"]],
|
|
8045
|
+
},
|
|
8046
|
+
]);
|
|
8047
|
+
}
|
|
8048
|
+
return nodeBomData;
|
|
8049
|
+
};
|
|
8050
|
+
|
|
8051
|
+
const processAsarArchive = async (
|
|
8052
|
+
archivePath,
|
|
8053
|
+
archiveIdentityPath,
|
|
8054
|
+
isPrimaryArchive = false,
|
|
8055
|
+
depth = 0,
|
|
8056
|
+
) => {
|
|
8057
|
+
const processedKey = archiveIdentityPath;
|
|
8058
|
+
if (processedArchives.has(processedKey)) {
|
|
8059
|
+
return undefined;
|
|
8060
|
+
}
|
|
8061
|
+
processedArchives.add(processedKey);
|
|
8062
|
+
const archiveAnalysis = await parseAsarArchive(archivePath, {
|
|
8063
|
+
...options,
|
|
8064
|
+
asarVirtualPath: archiveIdentityPath,
|
|
8065
|
+
});
|
|
8066
|
+
aggregateArchiveResults(archiveAnalysis, isPrimaryArchive);
|
|
8067
|
+
const shouldExtract =
|
|
8068
|
+
archiveAnalysis.packageManifestPaths?.length ||
|
|
8069
|
+
(depth < maxNestedAsarDepth &&
|
|
8070
|
+
archiveAnalysis.summary?.nestedArchiveCount > 0);
|
|
8071
|
+
if (!shouldExtract) {
|
|
8072
|
+
return archiveAnalysis.parentComponent?.["bom-ref"];
|
|
8073
|
+
}
|
|
8074
|
+
const extractedDir = await extractAsarToTempDir(archivePath);
|
|
8075
|
+
if (!extractedDir) {
|
|
8076
|
+
return archiveAnalysis.parentComponent?.["bom-ref"];
|
|
8077
|
+
}
|
|
8078
|
+
tempDirs.push(extractedDir);
|
|
8079
|
+
await analyzeExtractedArchive(
|
|
8080
|
+
extractedDir,
|
|
8081
|
+
archiveAnalysis,
|
|
8082
|
+
archiveIdentityPath,
|
|
8083
|
+
);
|
|
8084
|
+
if (depth < maxNestedAsarDepth) {
|
|
8085
|
+
const nestedAsarFiles = getAllFiles(extractedDir, "**/*.asar", options);
|
|
8086
|
+
for (const nestedArchivePath of nestedAsarFiles) {
|
|
8087
|
+
const relativeNestedArchivePath = relative(
|
|
8088
|
+
extractedDir,
|
|
8089
|
+
nestedArchivePath,
|
|
8090
|
+
).replaceAll("\\", "/");
|
|
8091
|
+
if (
|
|
8092
|
+
!relativeNestedArchivePath ||
|
|
8093
|
+
relativeNestedArchivePath.startsWith("..")
|
|
8094
|
+
) {
|
|
8095
|
+
continue;
|
|
8096
|
+
}
|
|
8097
|
+
const nestedArchiveIdentityPath = `${archiveIdentityPath}#/${relativeNestedArchivePath}`;
|
|
8098
|
+
const nestedParentRef = await processAsarArchive(
|
|
8099
|
+
nestedArchivePath,
|
|
8100
|
+
nestedArchiveIdentityPath,
|
|
8101
|
+
false,
|
|
8102
|
+
depth + 1,
|
|
8103
|
+
);
|
|
8104
|
+
if (
|
|
8105
|
+
archiveAnalysis.parentComponent?.["bom-ref"] &&
|
|
8106
|
+
nestedParentRef &&
|
|
8107
|
+
archiveAnalysis.parentComponent["bom-ref"] !== nestedParentRef
|
|
8108
|
+
) {
|
|
8109
|
+
dependencies = mergeDependencies(dependencies, [
|
|
8110
|
+
{
|
|
8111
|
+
ref: archiveAnalysis.parentComponent["bom-ref"],
|
|
8112
|
+
dependsOn: [nestedParentRef],
|
|
8113
|
+
},
|
|
8114
|
+
]);
|
|
8115
|
+
}
|
|
8116
|
+
}
|
|
8117
|
+
}
|
|
8118
|
+
return archiveAnalysis.parentComponent?.["bom-ref"];
|
|
8119
|
+
};
|
|
8120
|
+
try {
|
|
8121
|
+
for (const archivePath of asarFiles) {
|
|
8122
|
+
const isPrimaryArchive =
|
|
8123
|
+
explicitAsarPath && resolve(archivePath) === explicitAsarPath;
|
|
8124
|
+
await processAsarArchive(
|
|
8125
|
+
archivePath,
|
|
8126
|
+
resolve(archivePath),
|
|
8127
|
+
isPrimaryArchive,
|
|
8128
|
+
0,
|
|
8129
|
+
);
|
|
8130
|
+
}
|
|
8131
|
+
} finally {
|
|
8132
|
+
for (const tempDir of tempDirs) {
|
|
8133
|
+
cleanupAsarTempDir(tempDir);
|
|
8134
|
+
}
|
|
8135
|
+
}
|
|
8136
|
+
pkgList = trimComponents(pkgList);
|
|
8137
|
+
return buildBomNSData(options, pkgList, "asar", {
|
|
8138
|
+
src: path,
|
|
8139
|
+
filename: asarFiles.join(", "),
|
|
8140
|
+
nsMapping: {},
|
|
8141
|
+
dependencies,
|
|
8142
|
+
parentComponent,
|
|
8143
|
+
});
|
|
8144
|
+
}
|
|
8145
|
+
|
|
7866
8146
|
/**
|
|
7867
8147
|
* Function to create BOM for installed Chrome and Chromium-based browser extensions.
|
|
7868
8148
|
*
|
|
@@ -7874,7 +8154,8 @@ export async function createChromeExtensionBom(path, options) {
|
|
|
7874
8154
|
let dependencies = [];
|
|
7875
8155
|
let sourcePaths = [];
|
|
7876
8156
|
const directResult = collectChromeExtensionsFromPath(path);
|
|
7877
|
-
const
|
|
8157
|
+
const shouldDiscoverInstalledChromeExtensions =
|
|
8158
|
+
hasExplicitProjectTypeSelection(options, "chrome-extension");
|
|
7878
8159
|
let pkgList = directResult.components || [];
|
|
7879
8160
|
if (directResult.extensionDirs?.length) {
|
|
7880
8161
|
sourcePaths = directResult.extensionDirs.slice();
|
|
@@ -7893,20 +8174,23 @@ export async function createChromeExtensionBom(path, options) {
|
|
|
7893
8174
|
`Found ${pkgList.length} component(s) from direct Chrome extension path scan`,
|
|
7894
8175
|
);
|
|
7895
8176
|
}
|
|
7896
|
-
if (
|
|
7897
|
-
|
|
7898
|
-
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
|
|
7902
|
-
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
8177
|
+
if (shouldDiscoverInstalledChromeExtensions) {
|
|
8178
|
+
const chromeDirs = discoverChromiumExtensionDirs();
|
|
8179
|
+
if (chromeDirs.length) {
|
|
8180
|
+
if (DEBUG_MODE) {
|
|
8181
|
+
thoughtLog(
|
|
8182
|
+
`Discovered Chromium extension directories: ${chromeDirs.map((d) => `${d.browser} (${d.channel}): ${d.dir}`).join(", ")}`,
|
|
8183
|
+
);
|
|
8184
|
+
}
|
|
8185
|
+
if (!pkgList.length) {
|
|
8186
|
+
pkgList = collectInstalledChromeExtensions(chromeDirs);
|
|
8187
|
+
sourcePaths = chromeDirs.map((d) => d.dir);
|
|
8188
|
+
}
|
|
8189
|
+
if (DEBUG_MODE && pkgList.length && !directResult.components?.length) {
|
|
8190
|
+
thoughtLog(
|
|
8191
|
+
`Found ${pkgList.length} Chrome/Chromium extension(s) from ${chromeDirs.length} browser location(s)`,
|
|
8192
|
+
);
|
|
8193
|
+
}
|
|
7910
8194
|
}
|
|
7911
8195
|
}
|
|
7912
8196
|
pkgList = trimComponents(pkgList);
|
|
@@ -8026,6 +8310,26 @@ export async function createCryptoCertsBom(path, options) {
|
|
|
8026
8310
|
for (const f of certFiles) {
|
|
8027
8311
|
const name = basename(f);
|
|
8028
8312
|
const fileHash = await checksumFile("sha256", f);
|
|
8313
|
+
let evidence;
|
|
8314
|
+
if (options.evidence) {
|
|
8315
|
+
const identityEvidence = {
|
|
8316
|
+
field: "name",
|
|
8317
|
+
confidence: 1,
|
|
8318
|
+
concludedValue: name,
|
|
8319
|
+
methods: [
|
|
8320
|
+
{
|
|
8321
|
+
technique: "filename",
|
|
8322
|
+
confidence: 1,
|
|
8323
|
+
value: f,
|
|
8324
|
+
},
|
|
8325
|
+
],
|
|
8326
|
+
};
|
|
8327
|
+
evidence = {
|
|
8328
|
+
identity:
|
|
8329
|
+
options.specVersion >= 1.6 ? [identityEvidence] : identityEvidence,
|
|
8330
|
+
occurrences: [{ location: f }],
|
|
8331
|
+
};
|
|
8332
|
+
}
|
|
8029
8333
|
const apkg = {
|
|
8030
8334
|
name,
|
|
8031
8335
|
type: "cryptographic-asset",
|
|
@@ -8038,10 +8342,18 @@ export async function createCryptoCertsBom(path, options) {
|
|
|
8038
8342
|
implementationPlatform: "unknown",
|
|
8039
8343
|
},
|
|
8040
8344
|
},
|
|
8345
|
+
...(evidence ? { evidence } : {}),
|
|
8041
8346
|
properties: [{ name: "SrcFile", value: f }],
|
|
8042
8347
|
};
|
|
8043
8348
|
pkgList.push(apkg);
|
|
8044
8349
|
}
|
|
8350
|
+
const sourceCryptoComponents = await collectSourceCryptoComponents(
|
|
8351
|
+
path,
|
|
8352
|
+
options,
|
|
8353
|
+
);
|
|
8354
|
+
if (sourceCryptoComponents.length) {
|
|
8355
|
+
pkgList.push(...sourceCryptoComponents);
|
|
8356
|
+
}
|
|
8045
8357
|
return {
|
|
8046
8358
|
bomJson: {
|
|
8047
8359
|
components: pkgList,
|
|
@@ -8087,16 +8399,19 @@ export function dedupeBom(options, components, parentComponent, dependencies) {
|
|
|
8087
8399
|
options,
|
|
8088
8400
|
parentComponent,
|
|
8089
8401
|
components,
|
|
8090
|
-
bomJson:
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
|
|
8098
|
-
|
|
8099
|
-
|
|
8402
|
+
bomJson: setCycloneDxFormat(
|
|
8403
|
+
{
|
|
8404
|
+
specVersion: toCycloneDxSpecVersionString(options.specVersion || 1.7),
|
|
8405
|
+
serialNumber: serialNum,
|
|
8406
|
+
version: 1,
|
|
8407
|
+
metadata: addMetadata(parentComponent, options, {}),
|
|
8408
|
+
components,
|
|
8409
|
+
services: options.services || [],
|
|
8410
|
+
dependencies,
|
|
8411
|
+
},
|
|
8412
|
+
options.specVersion || 1.7,
|
|
8413
|
+
{ preserveLegacyBomFormat: true },
|
|
8414
|
+
),
|
|
8100
8415
|
};
|
|
8101
8416
|
}
|
|
8102
8417
|
|
|
@@ -8132,6 +8447,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
8132
8447
|
) {
|
|
8133
8448
|
const {
|
|
8134
8449
|
osPackages,
|
|
8450
|
+
osPackageFiles,
|
|
8135
8451
|
dependenciesList,
|
|
8136
8452
|
allTypes,
|
|
8137
8453
|
bundledSdks,
|
|
@@ -8139,6 +8455,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
8139
8455
|
binPaths,
|
|
8140
8456
|
executables,
|
|
8141
8457
|
sharedLibs,
|
|
8458
|
+
services,
|
|
8142
8459
|
tools,
|
|
8143
8460
|
} = await getOSPackages(
|
|
8144
8461
|
options.allLayersExplodedDir,
|
|
@@ -8149,14 +8466,16 @@ export async function createMultiXBom(pathList, options) {
|
|
|
8149
8466
|
options.bundledSdks = bundledSdks;
|
|
8150
8467
|
options.bundledRuntimes = bundledRuntimes;
|
|
8151
8468
|
options.binPaths = binPaths;
|
|
8469
|
+
options.unpackagedExecutableCount = executables?.length || 0;
|
|
8470
|
+
options.unpackagedSharedLibraryCount = sharedLibs?.length || 0;
|
|
8152
8471
|
if (DEBUG_MODE) {
|
|
8153
8472
|
console.log(
|
|
8154
|
-
`**OS**: Found ${osPackages.length} OS packages, ${executables?.length} executables,
|
|
8473
|
+
`**OS**: Found ${osPackages.length} OS packages, ${osPackageFiles?.length} package-owned files, ${executables?.length} unpackaged executables, ${sharedLibs.length} unpackaged shared libraries, and ${services?.length || 0} packaged services at ${options.allLayersExplodedDir}`,
|
|
8155
8474
|
);
|
|
8156
8475
|
}
|
|
8157
8476
|
if (osPackages.length) {
|
|
8158
8477
|
thoughtLog(
|
|
8159
|
-
`I found ${osPackages.length} OS packages and ${
|
|
8478
|
+
`I found ${osPackages.length} OS packages, ${osPackageFiles?.length || 0} package-owned files, and ${services?.length || 0} packaged services at ${options.allLayersExplodedDir}`,
|
|
8160
8479
|
);
|
|
8161
8480
|
} else if (executables?.length || sharedLibs?.length) {
|
|
8162
8481
|
thoughtLog(
|
|
@@ -8178,14 +8497,20 @@ export async function createMultiXBom(pathList, options) {
|
|
|
8178
8497
|
).concat(tools);
|
|
8179
8498
|
}
|
|
8180
8499
|
components = components.concat(osPackages);
|
|
8500
|
+
components = components.concat(osPackageFiles || []);
|
|
8181
8501
|
components = components.concat(executables);
|
|
8182
8502
|
components = components.concat(sharedLibs);
|
|
8183
8503
|
if (dependenciesList?.length) {
|
|
8184
8504
|
dependencies = mergeDependencies(dependencies, dependenciesList);
|
|
8185
8505
|
}
|
|
8506
|
+
if (services?.length) {
|
|
8507
|
+
options.services = mergeServices(options.services || [], services);
|
|
8508
|
+
}
|
|
8186
8509
|
if (parentComponent && Object.keys(parentComponent).length) {
|
|
8187
8510
|
// Make the parent oci image depend on all os components
|
|
8188
|
-
const parentDependsOn = new Set(
|
|
8511
|
+
const parentDependsOn = new Set(
|
|
8512
|
+
osPackages.concat(osPackageFiles || []).map((p) => p["bom-ref"]),
|
|
8513
|
+
);
|
|
8189
8514
|
dependencies.splice(0, 0, {
|
|
8190
8515
|
ref: parentComponent["bom-ref"],
|
|
8191
8516
|
dependsOn: Array.from(parentDependsOn).sort(),
|
|
@@ -8887,6 +9212,28 @@ export async function createMultiXBom(pathList, options) {
|
|
|
8887
9212
|
}
|
|
8888
9213
|
}
|
|
8889
9214
|
}
|
|
9215
|
+
if (hasAnyProjectType(["asar"], options)) {
|
|
9216
|
+
setProjectTypeActivityContext("asar", path);
|
|
9217
|
+
bomData = await createAsarBom(path, options);
|
|
9218
|
+
if (bomData?.bomJson?.components?.length) {
|
|
9219
|
+
if (DEBUG_MODE) {
|
|
9220
|
+
console.log(
|
|
9221
|
+
`Found ${bomData.bomJson.components.length} ASAR component(s) at ${path}`,
|
|
9222
|
+
);
|
|
9223
|
+
}
|
|
9224
|
+
components = components.concat(bomData.bomJson.components);
|
|
9225
|
+
dependencies = mergeDependencies(
|
|
9226
|
+
dependencies,
|
|
9227
|
+
bomData.bomJson.dependencies,
|
|
9228
|
+
);
|
|
9229
|
+
if (
|
|
9230
|
+
bomData.parentComponent &&
|
|
9231
|
+
Object.keys(bomData.parentComponent).length
|
|
9232
|
+
) {
|
|
9233
|
+
parentSubComponents.push(bomData.parentComponent);
|
|
9234
|
+
}
|
|
9235
|
+
}
|
|
9236
|
+
}
|
|
8890
9237
|
if (hasAnyProjectType(["vscode-extension"], options)) {
|
|
8891
9238
|
setProjectTypeActivityContext("vscode-extension", path);
|
|
8892
9239
|
bomData = await createVscodeExtensionBom(path, options);
|
|
@@ -9326,6 +9673,16 @@ export async function createXBom(path, options) {
|
|
|
9326
9673
|
return await createVscodeExtensionBom(path, options);
|
|
9327
9674
|
}
|
|
9328
9675
|
|
|
9676
|
+
// Electron ASAR archives
|
|
9677
|
+
const asarFiles = getAllFiles(
|
|
9678
|
+
path,
|
|
9679
|
+
`${options.multiProject ? "**/" : ""}*.asar`,
|
|
9680
|
+
options,
|
|
9681
|
+
);
|
|
9682
|
+
if (asarFiles.length) {
|
|
9683
|
+
return await createAsarBom(path, options);
|
|
9684
|
+
}
|
|
9685
|
+
|
|
9329
9686
|
// Helm charts
|
|
9330
9687
|
const chartFiles = getAllFiles(
|
|
9331
9688
|
path,
|
|
@@ -9428,6 +9785,35 @@ export async function createXBom(path, options) {
|
|
|
9428
9785
|
}
|
|
9429
9786
|
}
|
|
9430
9787
|
|
|
9788
|
+
/**
|
|
9789
|
+
* Function to create a hardware BOM for the current host.
|
|
9790
|
+
*
|
|
9791
|
+
* @param {string} _path Source path (unused for live host HBOM generation)
|
|
9792
|
+
* @param {Object} options Parse options from the cli
|
|
9793
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
9794
|
+
*/
|
|
9795
|
+
export async function createHBom(_path, options) {
|
|
9796
|
+
ensureHbomRuntimeSupport(options, options.commandName || "hbom");
|
|
9797
|
+
let bomJson = await createHbomDocument(options);
|
|
9798
|
+
if (options.includeRuntime) {
|
|
9799
|
+
const runtimeOptions = {
|
|
9800
|
+
...options,
|
|
9801
|
+
includeRuntime: false,
|
|
9802
|
+
multiProject: false,
|
|
9803
|
+
projectType: ["os"],
|
|
9804
|
+
};
|
|
9805
|
+
const obomData = await createOSBom(_path, runtimeOptions);
|
|
9806
|
+
bomJson = mergeHostInventoryBoms(bomJson, obomData);
|
|
9807
|
+
} else {
|
|
9808
|
+
bomJson = mergeHostInventoryBoms(bomJson);
|
|
9809
|
+
}
|
|
9810
|
+
return {
|
|
9811
|
+
bomJson,
|
|
9812
|
+
dependencies: bomJson.dependencies || [],
|
|
9813
|
+
parentComponent: bomJson.metadata?.component,
|
|
9814
|
+
};
|
|
9815
|
+
}
|
|
9816
|
+
|
|
9431
9817
|
/**
|
|
9432
9818
|
* Function to create bom string for various languages
|
|
9433
9819
|
*
|
|
@@ -9440,6 +9826,21 @@ export async function createBom(path, options) {
|
|
|
9440
9826
|
if (!projectType) {
|
|
9441
9827
|
projectType = [];
|
|
9442
9828
|
}
|
|
9829
|
+
ensureNoMixedHbomProjectTypes(projectType);
|
|
9830
|
+
if (hasHbomProjectType(projectType)) {
|
|
9831
|
+
const selectedHbomProjectType = Array.isArray(projectType)
|
|
9832
|
+
? projectType[0]
|
|
9833
|
+
: `${projectType}`.split(",")[0];
|
|
9834
|
+
options.projectType = [selectedHbomProjectType];
|
|
9835
|
+
setActivityContext({
|
|
9836
|
+
projectType: selectedHbomProjectType,
|
|
9837
|
+
sourcePath: path,
|
|
9838
|
+
});
|
|
9839
|
+
thoughtLog(
|
|
9840
|
+
"The user wants a Hardware Bill-of-Materials (HBOM) for the current host. Let's use the dedicated hardware collector.",
|
|
9841
|
+
);
|
|
9842
|
+
return await createHBom(path, options);
|
|
9843
|
+
}
|
|
9443
9844
|
let exportData;
|
|
9444
9845
|
let isContainerMode = false;
|
|
9445
9846
|
const isLocalDirectoryInput =
|
|
@@ -9574,6 +9975,9 @@ export async function createBom(path, options) {
|
|
|
9574
9975
|
if (path.endsWith(".war")) {
|
|
9575
9976
|
projectType = ["java"];
|
|
9576
9977
|
}
|
|
9978
|
+
if (!projectType.length && path.endsWith(".asar")) {
|
|
9979
|
+
projectType = ["asar"];
|
|
9980
|
+
}
|
|
9577
9981
|
if (projectType.length > 1) {
|
|
9578
9982
|
setActivityContext({
|
|
9579
9983
|
projectType: projectType.join(","),
|
|
@@ -9697,6 +10101,9 @@ export async function createBom(path, options) {
|
|
|
9697
10101
|
if (PROJECT_TYPE_ALIASES["caxa"].includes(projectType[0])) {
|
|
9698
10102
|
return await createCaxaBom(path, options);
|
|
9699
10103
|
}
|
|
10104
|
+
if (PROJECT_TYPE_ALIASES["asar"].includes(projectType[0])) {
|
|
10105
|
+
return await createAsarBom(path, options);
|
|
10106
|
+
}
|
|
9700
10107
|
if (PROJECT_TYPE_ALIASES["vscode-extension"].includes(projectType[0])) {
|
|
9701
10108
|
return await createVscodeExtensionBom(path, options);
|
|
9702
10109
|
}
|
|
@@ -9741,17 +10148,33 @@ export async function createBom(path, options) {
|
|
|
9741
10148
|
* @throws {Error} if the request fails
|
|
9742
10149
|
*/
|
|
9743
10150
|
export async function submitBom(args, bomContents) {
|
|
10151
|
+
const dependencyTrackApiUrl = getDependencyTrackBomApiUrl(args.serverUrl);
|
|
10152
|
+
const serverUrl = dependencyTrackApiUrl?.toString();
|
|
10153
|
+
if (!dependencyTrackApiUrl || !serverUrl) {
|
|
10154
|
+
console.log(
|
|
10155
|
+
"Invalid Dependency-Track server URL. Provide an absolute http(s) URL without dangerous characters.",
|
|
10156
|
+
);
|
|
10157
|
+
args.failOnError && process.exit(1);
|
|
10158
|
+
return undefined;
|
|
10159
|
+
}
|
|
10160
|
+
const serverHost = dependencyTrackApiUrl.hostname;
|
|
10161
|
+
if (!isAllowedHttpHost(serverHost)) {
|
|
10162
|
+
console.log(
|
|
10163
|
+
`Dependency-Track server host '${serverHost}' is not allowed by CDXGEN_ALLOWED_HOSTS.`,
|
|
10164
|
+
);
|
|
10165
|
+
args.failOnError && process.exit(1);
|
|
10166
|
+
return undefined;
|
|
10167
|
+
}
|
|
9744
10168
|
if (isDryRun) {
|
|
9745
10169
|
recordActivity({
|
|
9746
10170
|
kind: "network",
|
|
9747
10171
|
reason:
|
|
9748
10172
|
"Dry run mode blocks Dependency-Track submission and reports the request instead.",
|
|
9749
10173
|
status: "blocked",
|
|
9750
|
-
target:
|
|
10174
|
+
target: serverUrl,
|
|
9751
10175
|
});
|
|
9752
10176
|
return undefined;
|
|
9753
10177
|
}
|
|
9754
|
-
const serverUrl = getDependencyTrackBomUrl(args.serverUrl);
|
|
9755
10178
|
const bomPayload = buildDependencyTrackBomPayload(args, bomContents);
|
|
9756
10179
|
if (!bomPayload) {
|
|
9757
10180
|
console.log(
|
|
@@ -9770,15 +10193,7 @@ export async function submitBom(args, bomContents) {
|
|
|
9770
10193
|
);
|
|
9771
10194
|
}
|
|
9772
10195
|
try {
|
|
9773
|
-
|
|
9774
|
-
console.log(
|
|
9775
|
-
"Calling ",
|
|
9776
|
-
serverUrl,
|
|
9777
|
-
"with --skip-dt-tls-check argument: Skip DT TLS check.",
|
|
9778
|
-
);
|
|
9779
|
-
}
|
|
9780
|
-
// See issue #1963 regarding CRLF hardening
|
|
9781
|
-
return await got(serverUrl, {
|
|
10196
|
+
const requestOptions = {
|
|
9782
10197
|
method: "PUT",
|
|
9783
10198
|
followRedirect: false,
|
|
9784
10199
|
https: {
|
|
@@ -9787,11 +10202,22 @@ export async function submitBom(args, bomContents) {
|
|
|
9787
10202
|
headers: {
|
|
9788
10203
|
"X-Api-Key": (args.apiKey || "").replace(/[\r\n]/g, ""),
|
|
9789
10204
|
"Content-Type": "application/json",
|
|
9790
|
-
"user-agent": `@CycloneDX/cdxgen ${CDXGEN_VERSION}`,
|
|
9791
10205
|
},
|
|
9792
10206
|
json: bomPayload,
|
|
9793
10207
|
responseType: "json",
|
|
9794
|
-
|
|
10208
|
+
context: {
|
|
10209
|
+
activityIntent: "bom-submit",
|
|
10210
|
+
},
|
|
10211
|
+
};
|
|
10212
|
+
if (DEBUG_MODE && args.skipDtTlsCheck) {
|
|
10213
|
+
console.log(
|
|
10214
|
+
"Calling ",
|
|
10215
|
+
serverUrl,
|
|
10216
|
+
"with --skip-dt-tls-check argument: Skip DT TLS check.",
|
|
10217
|
+
);
|
|
10218
|
+
}
|
|
10219
|
+
// See issue #1963 regarding CRLF hardening
|
|
10220
|
+
return await cdxgenAgent(dependencyTrackApiUrl, requestOptions).json();
|
|
9795
10221
|
} catch (error) {
|
|
9796
10222
|
if (error.response && error.response.statusCode === 401) {
|
|
9797
10223
|
// Unauthorized
|
|
@@ -9804,7 +10230,7 @@ export async function submitBom(args, bomContents) {
|
|
|
9804
10230
|
);
|
|
9805
10231
|
// Method not allowed errors
|
|
9806
10232
|
try {
|
|
9807
|
-
return await
|
|
10233
|
+
return await cdxgenAgent(dependencyTrackApiUrl, {
|
|
9808
10234
|
method: "POST",
|
|
9809
10235
|
followRedirect: false,
|
|
9810
10236
|
https: {
|
|
@@ -9813,10 +10239,12 @@ export async function submitBom(args, bomContents) {
|
|
|
9813
10239
|
headers: {
|
|
9814
10240
|
"X-Api-Key": (args.apiKey || "").replace(/[\r\n]/g, ""),
|
|
9815
10241
|
"Content-Type": "application/json",
|
|
9816
|
-
"user-agent": `@CycloneDX/cdxgen ${CDXGEN_VERSION}`,
|
|
9817
10242
|
},
|
|
9818
10243
|
json: bomPayload,
|
|
9819
10244
|
responseType: "json",
|
|
10245
|
+
context: {
|
|
10246
|
+
activityIntent: "bom-submit",
|
|
10247
|
+
},
|
|
9820
10248
|
}).json();
|
|
9821
10249
|
} catch (error) {
|
|
9822
10250
|
if (DEBUG_MODE) {
|