@cyclonedx/cdxgen 12.3.3 → 12.4.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 +64 -22
- package/bin/audit.js +21 -7
- package/bin/cdxgen.js +238 -116
- package/bin/convert.js +28 -13
- package/bin/hbom.js +490 -0
- package/bin/repl.js +580 -29
- package/bin/validate.js +34 -4
- package/bin/verify.js +40 -5
- package/data/README.md +298 -25
- package/data/component-tags.json +6 -0
- package/data/crypto-oid.json +16 -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 +209 -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 +506 -88
- package/lib/cli/index.poku.js +1352 -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/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 +349 -0
- package/lib/helpers/plugins.poku.js +57 -0
- package/lib/helpers/protobom.js +156 -45
- package/lib/helpers/protobom.poku.js +140 -5
- 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 +2 -27
- 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 +192 -1
- package/lib/stages/postgen/postgen.poku.js +321 -0
- package/lib/stages/postgen/ruleEngine.js +116 -0
- package/lib/stages/pregen/envAudit.js +14 -3
- 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.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/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 +62 -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 +3 -4
- package/types/lib/helpers/protobom.d.ts.map +1 -1
- 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/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,15 @@ 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";
|
|
39
44
|
import { parseCaxaMetadata } from "../helpers/caxa.js";
|
|
45
|
+
import { collectSourceCryptoComponents } from "../helpers/cbomutils.js";
|
|
40
46
|
import {
|
|
41
47
|
CHROME_EXTENSION_PURL_TYPE,
|
|
42
48
|
collectChromeExtensionsFromPath,
|
|
@@ -49,15 +55,19 @@ import {
|
|
|
49
55
|
trimComponents,
|
|
50
56
|
} from "../helpers/depsUtils.js";
|
|
51
57
|
import { GIT_COMMAND } from "../helpers/envcontext.js";
|
|
52
|
-
import { thoughtLog } from "../helpers/logger.js";
|
|
53
58
|
import {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
createHbomDocument,
|
|
60
|
+
ensureHbomRuntimeSupport,
|
|
61
|
+
ensureNoMixedHbomProjectTypes,
|
|
62
|
+
hasHbomProjectType,
|
|
63
|
+
} from "../helpers/hbom.js";
|
|
64
|
+
import { mergeHostInventoryBoms } from "../helpers/hostTopology.js";
|
|
65
|
+
import { thoughtLog } from "../helpers/logger.js";
|
|
66
|
+
import { enrichComponentWithMcpMetadata } from "../helpers/mcp.js";
|
|
57
67
|
import { isPyLockFile } from "../helpers/pylockutils.js";
|
|
58
68
|
import {
|
|
59
69
|
buildDependencyTrackBomPayload,
|
|
60
|
-
|
|
70
|
+
getDependencyTrackBomApiUrl,
|
|
61
71
|
} from "../helpers/remote/dependency-track.js";
|
|
62
72
|
import { table } from "../helpers/table.js";
|
|
63
73
|
import {
|
|
@@ -71,6 +81,7 @@ import {
|
|
|
71
81
|
CARGO_CMD,
|
|
72
82
|
CDXGEN_VERSION,
|
|
73
83
|
CLJ_CMD,
|
|
84
|
+
cdxgenAgent,
|
|
74
85
|
checksumFile,
|
|
75
86
|
cleanupPlugin,
|
|
76
87
|
collectGemModuleNames,
|
|
@@ -111,6 +122,7 @@ import {
|
|
|
111
122
|
getTmpDir,
|
|
112
123
|
hasAnyProjectType,
|
|
113
124
|
includeMavenTestScope,
|
|
125
|
+
isAllowedHttpHost,
|
|
114
126
|
isDryRun,
|
|
115
127
|
isFeatureEnabled,
|
|
116
128
|
isMac,
|
|
@@ -201,6 +213,7 @@ import {
|
|
|
201
213
|
readZipEntry,
|
|
202
214
|
recomputeScope,
|
|
203
215
|
recordActivity,
|
|
216
|
+
recordSensitiveFileRead,
|
|
204
217
|
resetActivityContext,
|
|
205
218
|
SWIFT_CMD,
|
|
206
219
|
safeExistsSync,
|
|
@@ -226,10 +239,12 @@ import {
|
|
|
226
239
|
VSCODE_EXTENSION_PURL_TYPE,
|
|
227
240
|
} from "../helpers/vsixutils.js";
|
|
228
241
|
import {
|
|
242
|
+
enrichOSComponentsWithTrustData,
|
|
229
243
|
executeOsQuery,
|
|
230
244
|
getBinaryBom,
|
|
231
245
|
getDotnetSlices,
|
|
232
246
|
getOSPackages,
|
|
247
|
+
getPluginToolComponents,
|
|
233
248
|
} from "../managers/binary.js";
|
|
234
249
|
import {
|
|
235
250
|
addSkippedSrcFiles,
|
|
@@ -369,6 +384,18 @@ const shouldIncludeNodeModulesDir = (options = {}, baseProjectTypes = []) => {
|
|
|
369
384
|
);
|
|
370
385
|
};
|
|
371
386
|
|
|
387
|
+
const hasExplicitProjectTypeSelection = (options, baseProjectType) => {
|
|
388
|
+
options = options || {};
|
|
389
|
+
const projectTypes = Array.isArray(options.projectType)
|
|
390
|
+
? options.projectType
|
|
391
|
+
: options.projectType
|
|
392
|
+
? [options.projectType]
|
|
393
|
+
: [];
|
|
394
|
+
return projectTypes.some((selectedProjectType) =>
|
|
395
|
+
PROJECT_TYPE_ALIASES[baseProjectType]?.includes(selectedProjectType),
|
|
396
|
+
);
|
|
397
|
+
};
|
|
398
|
+
|
|
372
399
|
const determineParentComponent = (options) => {
|
|
373
400
|
let parentComponent;
|
|
374
401
|
if (options.parentComponent && Object.keys(options.parentComponent).length) {
|
|
@@ -840,6 +867,18 @@ function addMetadata(parentComponent = {}, options = {}, context = {}) {
|
|
|
840
867
|
value: options.allOSComponentTypes.sort().join("\\n"),
|
|
841
868
|
});
|
|
842
869
|
}
|
|
870
|
+
if (Number.isInteger(options?.unpackagedExecutableCount)) {
|
|
871
|
+
mproperties.push({
|
|
872
|
+
name: "cdx:container:unpackagedExecutableCount",
|
|
873
|
+
value: String(options.unpackagedExecutableCount),
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
if (Number.isInteger(options?.unpackagedSharedLibraryCount)) {
|
|
877
|
+
mproperties.push({
|
|
878
|
+
name: "cdx:container:unpackagedSharedLibraryCount",
|
|
879
|
+
value: String(options.unpackagedSharedLibraryCount),
|
|
880
|
+
});
|
|
881
|
+
}
|
|
843
882
|
// Should we move these to formulation?
|
|
844
883
|
if (options?.bundledSdks?.length) {
|
|
845
884
|
for (const sdk of options.bundledSdks) {
|
|
@@ -943,6 +982,9 @@ export function listComponents(options, allImports, pkg, ptype = "npm") {
|
|
|
943
982
|
return Object.keys(compMap).map((k) => compMap[k]);
|
|
944
983
|
}
|
|
945
984
|
|
|
985
|
+
// These component types do not have PURLs
|
|
986
|
+
const NON_PURL_TYPES = ["cryptographic-asset", "file", "data"];
|
|
987
|
+
|
|
946
988
|
/**
|
|
947
989
|
* Given the specified package, create a CycloneDX component and add it to the list.
|
|
948
990
|
*/
|
|
@@ -979,9 +1021,9 @@ function addComponent(
|
|
|
979
1021
|
}
|
|
980
1022
|
const version = pkg.version || "";
|
|
981
1023
|
const licenses = pkg.licenses || getLicenses(pkg);
|
|
982
|
-
let purl =
|
|
983
|
-
|
|
984
|
-
new PackageURL(
|
|
1024
|
+
let purl = pkg.purl;
|
|
1025
|
+
if (!purl && ptype) {
|
|
1026
|
+
purl = new PackageURL(
|
|
985
1027
|
ptype,
|
|
986
1028
|
encodeForPurl(group),
|
|
987
1029
|
encodeForPurl(name),
|
|
@@ -989,9 +1031,9 @@ function addComponent(
|
|
|
989
1031
|
pkg.qualifiers,
|
|
990
1032
|
encodeForPurl(pkg.subpath),
|
|
991
1033
|
);
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
if (ptype
|
|
1034
|
+
}
|
|
1035
|
+
let purlString = purl?.toString();
|
|
1036
|
+
if (NON_PURL_TYPES.includes(ptype) || NON_PURL_TYPES.includes(pkg.type)) {
|
|
995
1037
|
purl = undefined;
|
|
996
1038
|
purlString = undefined;
|
|
997
1039
|
}
|
|
@@ -1102,7 +1144,7 @@ function addComponent(
|
|
|
1102
1144
|
if (pkg.properties?.length) {
|
|
1103
1145
|
component.properties = pkg.properties;
|
|
1104
1146
|
}
|
|
1105
|
-
if (pkg.cryptoProperties
|
|
1147
|
+
if (pkg.cryptoProperties && typeof pkg.cryptoProperties === "object") {
|
|
1106
1148
|
component.cryptoProperties = pkg.cryptoProperties;
|
|
1107
1149
|
}
|
|
1108
1150
|
// Retain nested components
|
|
@@ -2769,11 +2811,10 @@ export async function createJavaBom(path, options) {
|
|
|
2769
2811
|
}
|
|
2770
2812
|
|
|
2771
2813
|
/**
|
|
2772
|
-
*
|
|
2814
|
+
* Identify the requested AI inventory project types.
|
|
2773
2815
|
*
|
|
2774
|
-
* @param {string} path to the project
|
|
2775
2816
|
* @param {Object} options Parse options from the cli
|
|
2776
|
-
* @returns {
|
|
2817
|
+
* @returns {string[]} Requested AI inventory types
|
|
2777
2818
|
*/
|
|
2778
2819
|
function getRequestedAiInventoryTypes(options) {
|
|
2779
2820
|
return AI_INVENTORY_PROJECT_TYPES.filter((type) =>
|
|
@@ -2787,6 +2828,48 @@ function getExcludedAiInventoryTypes(options) {
|
|
|
2787
2828
|
);
|
|
2788
2829
|
}
|
|
2789
2830
|
|
|
2831
|
+
function filterIncludedAiInventoryTypes(
|
|
2832
|
+
includedAiInventoryTypes,
|
|
2833
|
+
excludedAiInventoryTypes,
|
|
2834
|
+
) {
|
|
2835
|
+
return [...new Set(includedAiInventoryTypes)].filter(
|
|
2836
|
+
(type) => !excludedAiInventoryTypes.includes(type),
|
|
2837
|
+
);
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2840
|
+
/**
|
|
2841
|
+
* Determine which AI inventory types should be collected for a scan.
|
|
2842
|
+
*
|
|
2843
|
+
* This combines explicit project-type opt-ins with BOM audit category-driven
|
|
2844
|
+
* opt-ins, then removes any explicitly excluded inventory types.
|
|
2845
|
+
*
|
|
2846
|
+
* @param {Object} options Parse options from the CLI
|
|
2847
|
+
* @returns {string[]} AI inventory types to collect
|
|
2848
|
+
*/
|
|
2849
|
+
function getIncludedAiInventoryTypes(options) {
|
|
2850
|
+
const requestedAiInventoryTypes = getRequestedAiInventoryTypes(options);
|
|
2851
|
+
const excludedAiInventoryTypes = getExcludedAiInventoryTypes(options);
|
|
2852
|
+
const exactAiInventoryType = getExactAiInventoryType(options);
|
|
2853
|
+
if (exactAiInventoryType) {
|
|
2854
|
+
return filterIncludedAiInventoryTypes(
|
|
2855
|
+
requestedAiInventoryTypes,
|
|
2856
|
+
excludedAiInventoryTypes,
|
|
2857
|
+
);
|
|
2858
|
+
}
|
|
2859
|
+
const auditCategories = expandBomAuditCategories(options?.bomAuditCategories);
|
|
2860
|
+
const includedAiInventoryTypes = [...requestedAiInventoryTypes];
|
|
2861
|
+
if (auditCategories.includes("ai-agent")) {
|
|
2862
|
+
includedAiInventoryTypes.push("ai-skill");
|
|
2863
|
+
}
|
|
2864
|
+
if (auditCategories.includes("mcp-server")) {
|
|
2865
|
+
includedAiInventoryTypes.push("mcp");
|
|
2866
|
+
}
|
|
2867
|
+
return filterIncludedAiInventoryTypes(
|
|
2868
|
+
includedAiInventoryTypes,
|
|
2869
|
+
excludedAiInventoryTypes,
|
|
2870
|
+
);
|
|
2871
|
+
}
|
|
2872
|
+
|
|
2790
2873
|
function getExactAiInventoryType(options) {
|
|
2791
2874
|
const requestedAiInventoryTypes = getRequestedAiInventoryTypes(options);
|
|
2792
2875
|
return requestedAiInventoryTypes.length === 1 &&
|
|
@@ -2796,22 +2879,14 @@ function getExactAiInventoryType(options) {
|
|
|
2796
2879
|
: undefined;
|
|
2797
2880
|
}
|
|
2798
2881
|
|
|
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
|
-
});
|
|
2882
|
+
/**
|
|
2883
|
+
* Determine whether MCP source-code analysis should run for the current scan.
|
|
2884
|
+
*
|
|
2885
|
+
* @param {string[]} includedAiInventoryTypes AI inventory types selected for collection
|
|
2886
|
+
* @returns {boolean} True when MCP inventory collection is enabled
|
|
2887
|
+
*/
|
|
2888
|
+
function shouldDetectMcpInventory(includedAiInventoryTypes) {
|
|
2889
|
+
return includedAiInventoryTypes.includes("mcp");
|
|
2815
2890
|
}
|
|
2816
2891
|
|
|
2817
2892
|
function summarizeAiInventoryNames(subjects, discoveryPath, kindSet) {
|
|
@@ -2906,14 +2981,8 @@ export async function createNodejsBom(path, options) {
|
|
|
2906
2981
|
let parentComponent = {};
|
|
2907
2982
|
const parentSubComponents = [];
|
|
2908
2983
|
let ppurl = "";
|
|
2909
|
-
const requestedAiInventoryTypes = getRequestedAiInventoryTypes(options);
|
|
2910
|
-
const excludedAiInventoryTypes = getExcludedAiInventoryTypes(options);
|
|
2911
2984
|
const exactAiInventoryType = getExactAiInventoryType(options);
|
|
2912
|
-
const includedAiInventoryTypes =
|
|
2913
|
-
? requestedAiInventoryTypes
|
|
2914
|
-
: AI_INVENTORY_PROJECT_TYPES.filter(
|
|
2915
|
-
(type) => !excludedAiInventoryTypes.includes(type),
|
|
2916
|
-
);
|
|
2985
|
+
const includedAiInventoryTypes = getIncludedAiInventoryTypes(options);
|
|
2917
2986
|
let aiInventory = { components: [], dependencies: [], services: [] };
|
|
2918
2987
|
// Docker mode requires special handling
|
|
2919
2988
|
if (hasAnyProjectType(["docker", "oci", "container", "os"], options, false)) {
|
|
@@ -2965,16 +3034,13 @@ export async function createNodejsBom(path, options) {
|
|
|
2965
3034
|
const retData = await findJSImportsExports(path, options.deep);
|
|
2966
3035
|
allImports = retData.allImports;
|
|
2967
3036
|
allExports = retData.allExports;
|
|
2968
|
-
if (shouldDetectMcpInventory(
|
|
3037
|
+
if (shouldDetectMcpInventory(includedAiInventoryTypes)) {
|
|
2969
3038
|
mcpInventory = detectMcpInventory(path, options.deep);
|
|
2970
3039
|
}
|
|
2971
3040
|
}
|
|
2972
3041
|
if (includedAiInventoryTypes.length) {
|
|
2973
3042
|
aiInventory = collectAiInventory(path, options, includedAiInventoryTypes);
|
|
2974
3043
|
}
|
|
2975
|
-
if (excludedAiInventoryTypes.includes("mcp")) {
|
|
2976
|
-
mcpInventory = { components: [], dependencies: [], services: [] };
|
|
2977
|
-
}
|
|
2978
3044
|
const aiInventorySummary = summarizeAiInventory(aiInventory);
|
|
2979
3045
|
if (!exactAiInventoryType) {
|
|
2980
3046
|
if (aiInventorySummary.instructionCount || aiInventorySummary.skillCount) {
|
|
@@ -2987,7 +3053,6 @@ export async function createNodejsBom(path, options) {
|
|
|
2987
3053
|
`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
3054
|
);
|
|
2989
3055
|
}
|
|
2990
|
-
emitAiInventorySummary(aiInventory, path);
|
|
2991
3056
|
}
|
|
2992
3057
|
if (exactAiInventoryType === "ai-skill") {
|
|
2993
3058
|
const exactComponents = trimComponents([...(aiInventory.components || [])]);
|
|
@@ -3167,11 +3232,15 @@ export async function createNodejsBom(path, options) {
|
|
|
3167
3232
|
}
|
|
3168
3233
|
const basePath = dirname(apkgJson);
|
|
3169
3234
|
let npmrcData;
|
|
3170
|
-
|
|
3235
|
+
const npmrcFile = join(basePath, ".npmrc");
|
|
3236
|
+
if (safeExistsSync(npmrcFile)) {
|
|
3171
3237
|
thoughtLog(
|
|
3172
3238
|
"Wait, there is a .npmrc file here! I'm going to check if it has anything malicious.",
|
|
3173
3239
|
);
|
|
3174
|
-
npmrcData = readFileSync(
|
|
3240
|
+
npmrcData = readFileSync(npmrcFile, "utf-8");
|
|
3241
|
+
recordSensitiveFileRead(npmrcFile, {
|
|
3242
|
+
label: "npm registry configuration",
|
|
3243
|
+
});
|
|
3175
3244
|
const npmrcObj = parseNpmrc(npmrcData);
|
|
3176
3245
|
for (const [key, value] of Object.entries(npmrcObj)) {
|
|
3177
3246
|
const baseKey = key.replace(/^(?:\/\/[^/]+\/|@[^:]+:)/, "");
|
|
@@ -4102,14 +4171,7 @@ export async function createPythonBom(path, options) {
|
|
|
4102
4171
|
let dependencies = [];
|
|
4103
4172
|
let pkgList = [];
|
|
4104
4173
|
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
|
-
);
|
|
4174
|
+
const includedAiInventoryTypes = getIncludedAiInventoryTypes(options);
|
|
4113
4175
|
let aiInventory = { components: [], dependencies: [], services: [] };
|
|
4114
4176
|
let mcpInventory = { components: [], dependencies: [], services: [] };
|
|
4115
4177
|
const tempDir = safeMkdtempSync(join(getTmpDir(), "cdxgen-venv-"));
|
|
@@ -5955,6 +6017,7 @@ export function createOSBom(_path, options) {
|
|
|
5955
6017
|
let pkgList = [];
|
|
5956
6018
|
let bomData = {};
|
|
5957
6019
|
let parentComponent = {};
|
|
6020
|
+
let externalTools = getPluginToolComponents(["osquery"]);
|
|
5958
6021
|
for (const queryCategory of Object.keys(osQueries)) {
|
|
5959
6022
|
const queryObj = osQueries[queryCategory];
|
|
5960
6023
|
const results = executeOsQuery(queryObj.query);
|
|
@@ -5973,6 +6036,20 @@ export function createOSBom(_path, options) {
|
|
|
5973
6036
|
);
|
|
5974
6037
|
}
|
|
5975
6038
|
} // for
|
|
6039
|
+
const hostTrustInventory = enrichOSComponentsWithTrustData(pkgList);
|
|
6040
|
+
if (hostTrustInventory?.components?.length) {
|
|
6041
|
+
pkgList = hostTrustInventory.components;
|
|
6042
|
+
}
|
|
6043
|
+
if (hostTrustInventory?.tools?.length) {
|
|
6044
|
+
externalTools = externalTools.concat(hostTrustInventory.tools);
|
|
6045
|
+
}
|
|
6046
|
+
if (externalTools.length) {
|
|
6047
|
+
options.tools = Array.from(
|
|
6048
|
+
new Map(
|
|
6049
|
+
externalTools.map((tool) => [tool["bom-ref"] || tool.name, tool]),
|
|
6050
|
+
).values(),
|
|
6051
|
+
);
|
|
6052
|
+
}
|
|
5976
6053
|
if (pkgList.length) {
|
|
5977
6054
|
bomData = buildBomNSData(options, pkgList, "", {
|
|
5978
6055
|
src: "",
|
|
@@ -7776,6 +7853,10 @@ export async function createVscodeExtensionBom(path, options) {
|
|
|
7776
7853
|
let pkgList = [];
|
|
7777
7854
|
let dependencies = [];
|
|
7778
7855
|
const tempDirs = [];
|
|
7856
|
+
const shouldDiscoverInstalledIdeExtensions = hasExplicitProjectTypeSelection(
|
|
7857
|
+
options,
|
|
7858
|
+
"vscode-extension",
|
|
7859
|
+
);
|
|
7779
7860
|
|
|
7780
7861
|
// Mode 1: Scan for .vsix files in the given directory, or treat the input
|
|
7781
7862
|
// path as a single .vsix file.
|
|
@@ -7821,7 +7902,7 @@ export async function createVscodeExtensionBom(path, options) {
|
|
|
7821
7902
|
}
|
|
7822
7903
|
|
|
7823
7904
|
// Mode 2: Auto-discover extensions from known IDE locations
|
|
7824
|
-
if (
|
|
7905
|
+
if (shouldDiscoverInstalledIdeExtensions) {
|
|
7825
7906
|
const ideDirs = discoverIdeExtensionDirs();
|
|
7826
7907
|
if (ideDirs.length) {
|
|
7827
7908
|
if (DEBUG_MODE) {
|
|
@@ -7863,6 +7944,198 @@ export async function createVscodeExtensionBom(path, options) {
|
|
|
7863
7944
|
});
|
|
7864
7945
|
}
|
|
7865
7946
|
|
|
7947
|
+
/**
|
|
7948
|
+
* Function to create BOM for Electron ASAR archives.
|
|
7949
|
+
*
|
|
7950
|
+
* @param {string} path to a single archive or a directory to scan
|
|
7951
|
+
* @param {Object} options Parse options from the cli
|
|
7952
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
7953
|
+
*/
|
|
7954
|
+
export async function createAsarBom(path, options) {
|
|
7955
|
+
let pkgList = [];
|
|
7956
|
+
let dependencies = [];
|
|
7957
|
+
let parentComponent = {};
|
|
7958
|
+
const tempDirs = [];
|
|
7959
|
+
const processedArchives = new Set();
|
|
7960
|
+
const maxNestedAsarDepth = 4;
|
|
7961
|
+
const explicitAsarPath = path.endsWith(".asar") ? resolve(path) : undefined;
|
|
7962
|
+
const asarFiles = explicitAsarPath
|
|
7963
|
+
? [explicitAsarPath]
|
|
7964
|
+
: getAllFiles(path, `${options.multiProject ? "**/" : ""}*.asar`, options);
|
|
7965
|
+
|
|
7966
|
+
const aggregateArchiveResults = (
|
|
7967
|
+
archiveAnalysis,
|
|
7968
|
+
isPrimaryArchive = false,
|
|
7969
|
+
) => {
|
|
7970
|
+
if (
|
|
7971
|
+
archiveAnalysis.parentComponent &&
|
|
7972
|
+
Object.keys(archiveAnalysis.parentComponent).length
|
|
7973
|
+
) {
|
|
7974
|
+
if (isPrimaryArchive && !Object.keys(parentComponent).length) {
|
|
7975
|
+
parentComponent = archiveAnalysis.parentComponent;
|
|
7976
|
+
} else {
|
|
7977
|
+
pkgList.push(archiveAnalysis.parentComponent);
|
|
7978
|
+
}
|
|
7979
|
+
}
|
|
7980
|
+
if (archiveAnalysis.components?.length) {
|
|
7981
|
+
pkgList = pkgList.concat(archiveAnalysis.components);
|
|
7982
|
+
}
|
|
7983
|
+
if (archiveAnalysis.dependencies?.length) {
|
|
7984
|
+
dependencies = mergeDependencies(
|
|
7985
|
+
dependencies,
|
|
7986
|
+
archiveAnalysis.dependencies,
|
|
7987
|
+
parentComponent,
|
|
7988
|
+
);
|
|
7989
|
+
}
|
|
7990
|
+
};
|
|
7991
|
+
|
|
7992
|
+
const analyzeExtractedArchive = async (
|
|
7993
|
+
extractedDir,
|
|
7994
|
+
archiveAnalysis,
|
|
7995
|
+
archiveIdentityPath,
|
|
7996
|
+
) => {
|
|
7997
|
+
if (!archiveAnalysis.packageManifestPaths?.length) {
|
|
7998
|
+
return undefined;
|
|
7999
|
+
}
|
|
8000
|
+
const nodeBomOptions = {
|
|
8001
|
+
...options,
|
|
8002
|
+
installDeps: false,
|
|
8003
|
+
multiProject: true,
|
|
8004
|
+
noBabel: false,
|
|
8005
|
+
path: extractedDir,
|
|
8006
|
+
projectType: ["js"],
|
|
8007
|
+
};
|
|
8008
|
+
const nodeBomData = await createNodejsBom(extractedDir, nodeBomOptions);
|
|
8009
|
+
if (nodeBomData?.bomJson?.components?.length) {
|
|
8010
|
+
rewriteExtractedArchivePaths(
|
|
8011
|
+
nodeBomData.bomJson.components,
|
|
8012
|
+
extractedDir,
|
|
8013
|
+
archiveIdentityPath,
|
|
8014
|
+
);
|
|
8015
|
+
pkgList = pkgList.concat(nodeBomData.bomJson.components);
|
|
8016
|
+
}
|
|
8017
|
+
if (nodeBomData?.bomJson?.dependencies?.length) {
|
|
8018
|
+
dependencies = mergeDependencies(
|
|
8019
|
+
dependencies,
|
|
8020
|
+
nodeBomData.bomJson.dependencies,
|
|
8021
|
+
);
|
|
8022
|
+
}
|
|
8023
|
+
if (
|
|
8024
|
+
archiveAnalysis.parentComponent?.["bom-ref"] &&
|
|
8025
|
+
nodeBomData?.parentComponent?.["bom-ref"] &&
|
|
8026
|
+
archiveAnalysis.parentComponent["bom-ref"] !==
|
|
8027
|
+
nodeBomData.parentComponent["bom-ref"]
|
|
8028
|
+
) {
|
|
8029
|
+
rewriteExtractedArchivePaths(
|
|
8030
|
+
nodeBomData.parentComponent,
|
|
8031
|
+
extractedDir,
|
|
8032
|
+
archiveIdentityPath,
|
|
8033
|
+
);
|
|
8034
|
+
dependencies = mergeDependencies(dependencies, [
|
|
8035
|
+
{
|
|
8036
|
+
ref: archiveAnalysis.parentComponent["bom-ref"],
|
|
8037
|
+
dependsOn: [nodeBomData.parentComponent["bom-ref"]],
|
|
8038
|
+
},
|
|
8039
|
+
]);
|
|
8040
|
+
}
|
|
8041
|
+
return nodeBomData;
|
|
8042
|
+
};
|
|
8043
|
+
|
|
8044
|
+
const processAsarArchive = async (
|
|
8045
|
+
archivePath,
|
|
8046
|
+
archiveIdentityPath,
|
|
8047
|
+
isPrimaryArchive = false,
|
|
8048
|
+
depth = 0,
|
|
8049
|
+
) => {
|
|
8050
|
+
const processedKey = archiveIdentityPath;
|
|
8051
|
+
if (processedArchives.has(processedKey)) {
|
|
8052
|
+
return undefined;
|
|
8053
|
+
}
|
|
8054
|
+
processedArchives.add(processedKey);
|
|
8055
|
+
const archiveAnalysis = await parseAsarArchive(archivePath, {
|
|
8056
|
+
...options,
|
|
8057
|
+
asarVirtualPath: archiveIdentityPath,
|
|
8058
|
+
});
|
|
8059
|
+
aggregateArchiveResults(archiveAnalysis, isPrimaryArchive);
|
|
8060
|
+
const shouldExtract =
|
|
8061
|
+
archiveAnalysis.packageManifestPaths?.length ||
|
|
8062
|
+
(depth < maxNestedAsarDepth &&
|
|
8063
|
+
archiveAnalysis.summary?.nestedArchiveCount > 0);
|
|
8064
|
+
if (!shouldExtract) {
|
|
8065
|
+
return archiveAnalysis.parentComponent?.["bom-ref"];
|
|
8066
|
+
}
|
|
8067
|
+
const extractedDir = await extractAsarToTempDir(archivePath);
|
|
8068
|
+
if (!extractedDir) {
|
|
8069
|
+
return archiveAnalysis.parentComponent?.["bom-ref"];
|
|
8070
|
+
}
|
|
8071
|
+
tempDirs.push(extractedDir);
|
|
8072
|
+
await analyzeExtractedArchive(
|
|
8073
|
+
extractedDir,
|
|
8074
|
+
archiveAnalysis,
|
|
8075
|
+
archiveIdentityPath,
|
|
8076
|
+
);
|
|
8077
|
+
if (depth < maxNestedAsarDepth) {
|
|
8078
|
+
const nestedAsarFiles = getAllFiles(extractedDir, "**/*.asar", options);
|
|
8079
|
+
for (const nestedArchivePath of nestedAsarFiles) {
|
|
8080
|
+
const relativeNestedArchivePath = relative(
|
|
8081
|
+
extractedDir,
|
|
8082
|
+
nestedArchivePath,
|
|
8083
|
+
).replaceAll("\\", "/");
|
|
8084
|
+
if (
|
|
8085
|
+
!relativeNestedArchivePath ||
|
|
8086
|
+
relativeNestedArchivePath.startsWith("..")
|
|
8087
|
+
) {
|
|
8088
|
+
continue;
|
|
8089
|
+
}
|
|
8090
|
+
const nestedArchiveIdentityPath = `${archiveIdentityPath}#/${relativeNestedArchivePath}`;
|
|
8091
|
+
const nestedParentRef = await processAsarArchive(
|
|
8092
|
+
nestedArchivePath,
|
|
8093
|
+
nestedArchiveIdentityPath,
|
|
8094
|
+
false,
|
|
8095
|
+
depth + 1,
|
|
8096
|
+
);
|
|
8097
|
+
if (
|
|
8098
|
+
archiveAnalysis.parentComponent?.["bom-ref"] &&
|
|
8099
|
+
nestedParentRef &&
|
|
8100
|
+
archiveAnalysis.parentComponent["bom-ref"] !== nestedParentRef
|
|
8101
|
+
) {
|
|
8102
|
+
dependencies = mergeDependencies(dependencies, [
|
|
8103
|
+
{
|
|
8104
|
+
ref: archiveAnalysis.parentComponent["bom-ref"],
|
|
8105
|
+
dependsOn: [nestedParentRef],
|
|
8106
|
+
},
|
|
8107
|
+
]);
|
|
8108
|
+
}
|
|
8109
|
+
}
|
|
8110
|
+
}
|
|
8111
|
+
return archiveAnalysis.parentComponent?.["bom-ref"];
|
|
8112
|
+
};
|
|
8113
|
+
try {
|
|
8114
|
+
for (const archivePath of asarFiles) {
|
|
8115
|
+
const isPrimaryArchive =
|
|
8116
|
+
explicitAsarPath && resolve(archivePath) === explicitAsarPath;
|
|
8117
|
+
await processAsarArchive(
|
|
8118
|
+
archivePath,
|
|
8119
|
+
resolve(archivePath),
|
|
8120
|
+
isPrimaryArchive,
|
|
8121
|
+
0,
|
|
8122
|
+
);
|
|
8123
|
+
}
|
|
8124
|
+
} finally {
|
|
8125
|
+
for (const tempDir of tempDirs) {
|
|
8126
|
+
cleanupAsarTempDir(tempDir);
|
|
8127
|
+
}
|
|
8128
|
+
}
|
|
8129
|
+
pkgList = trimComponents(pkgList);
|
|
8130
|
+
return buildBomNSData(options, pkgList, "asar", {
|
|
8131
|
+
src: path,
|
|
8132
|
+
filename: asarFiles.join(", "),
|
|
8133
|
+
nsMapping: {},
|
|
8134
|
+
dependencies,
|
|
8135
|
+
parentComponent,
|
|
8136
|
+
});
|
|
8137
|
+
}
|
|
8138
|
+
|
|
7866
8139
|
/**
|
|
7867
8140
|
* Function to create BOM for installed Chrome and Chromium-based browser extensions.
|
|
7868
8141
|
*
|
|
@@ -7874,7 +8147,8 @@ export async function createChromeExtensionBom(path, options) {
|
|
|
7874
8147
|
let dependencies = [];
|
|
7875
8148
|
let sourcePaths = [];
|
|
7876
8149
|
const directResult = collectChromeExtensionsFromPath(path);
|
|
7877
|
-
const
|
|
8150
|
+
const shouldDiscoverInstalledChromeExtensions =
|
|
8151
|
+
hasExplicitProjectTypeSelection(options, "chrome-extension");
|
|
7878
8152
|
let pkgList = directResult.components || [];
|
|
7879
8153
|
if (directResult.extensionDirs?.length) {
|
|
7880
8154
|
sourcePaths = directResult.extensionDirs.slice();
|
|
@@ -7893,20 +8167,23 @@ export async function createChromeExtensionBom(path, options) {
|
|
|
7893
8167
|
`Found ${pkgList.length} component(s) from direct Chrome extension path scan`,
|
|
7894
8168
|
);
|
|
7895
8169
|
}
|
|
7896
|
-
if (
|
|
7897
|
-
|
|
7898
|
-
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
|
|
7902
|
-
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
8170
|
+
if (shouldDiscoverInstalledChromeExtensions) {
|
|
8171
|
+
const chromeDirs = discoverChromiumExtensionDirs();
|
|
8172
|
+
if (chromeDirs.length) {
|
|
8173
|
+
if (DEBUG_MODE) {
|
|
8174
|
+
thoughtLog(
|
|
8175
|
+
`Discovered Chromium extension directories: ${chromeDirs.map((d) => `${d.browser} (${d.channel}): ${d.dir}`).join(", ")}`,
|
|
8176
|
+
);
|
|
8177
|
+
}
|
|
8178
|
+
if (!pkgList.length) {
|
|
8179
|
+
pkgList = collectInstalledChromeExtensions(chromeDirs);
|
|
8180
|
+
sourcePaths = chromeDirs.map((d) => d.dir);
|
|
8181
|
+
}
|
|
8182
|
+
if (DEBUG_MODE && pkgList.length && !directResult.components?.length) {
|
|
8183
|
+
thoughtLog(
|
|
8184
|
+
`Found ${pkgList.length} Chrome/Chromium extension(s) from ${chromeDirs.length} browser location(s)`,
|
|
8185
|
+
);
|
|
8186
|
+
}
|
|
7910
8187
|
}
|
|
7911
8188
|
}
|
|
7912
8189
|
pkgList = trimComponents(pkgList);
|
|
@@ -8026,6 +8303,26 @@ export async function createCryptoCertsBom(path, options) {
|
|
|
8026
8303
|
for (const f of certFiles) {
|
|
8027
8304
|
const name = basename(f);
|
|
8028
8305
|
const fileHash = await checksumFile("sha256", f);
|
|
8306
|
+
let evidence;
|
|
8307
|
+
if (options.evidence) {
|
|
8308
|
+
const identityEvidence = {
|
|
8309
|
+
field: "name",
|
|
8310
|
+
confidence: 1,
|
|
8311
|
+
concludedValue: name,
|
|
8312
|
+
methods: [
|
|
8313
|
+
{
|
|
8314
|
+
technique: "filename",
|
|
8315
|
+
confidence: 1,
|
|
8316
|
+
value: f,
|
|
8317
|
+
},
|
|
8318
|
+
],
|
|
8319
|
+
};
|
|
8320
|
+
evidence = {
|
|
8321
|
+
identity:
|
|
8322
|
+
options.specVersion >= 1.6 ? [identityEvidence] : identityEvidence,
|
|
8323
|
+
occurrences: [{ location: f }],
|
|
8324
|
+
};
|
|
8325
|
+
}
|
|
8029
8326
|
const apkg = {
|
|
8030
8327
|
name,
|
|
8031
8328
|
type: "cryptographic-asset",
|
|
@@ -8038,10 +8335,18 @@ export async function createCryptoCertsBom(path, options) {
|
|
|
8038
8335
|
implementationPlatform: "unknown",
|
|
8039
8336
|
},
|
|
8040
8337
|
},
|
|
8338
|
+
...(evidence ? { evidence } : {}),
|
|
8041
8339
|
properties: [{ name: "SrcFile", value: f }],
|
|
8042
8340
|
};
|
|
8043
8341
|
pkgList.push(apkg);
|
|
8044
8342
|
}
|
|
8343
|
+
const sourceCryptoComponents = await collectSourceCryptoComponents(
|
|
8344
|
+
path,
|
|
8345
|
+
options,
|
|
8346
|
+
);
|
|
8347
|
+
if (sourceCryptoComponents.length) {
|
|
8348
|
+
pkgList.push(...sourceCryptoComponents);
|
|
8349
|
+
}
|
|
8045
8350
|
return {
|
|
8046
8351
|
bomJson: {
|
|
8047
8352
|
components: pkgList,
|
|
@@ -8132,6 +8437,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
8132
8437
|
) {
|
|
8133
8438
|
const {
|
|
8134
8439
|
osPackages,
|
|
8440
|
+
osPackageFiles,
|
|
8135
8441
|
dependenciesList,
|
|
8136
8442
|
allTypes,
|
|
8137
8443
|
bundledSdks,
|
|
@@ -8139,6 +8445,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
8139
8445
|
binPaths,
|
|
8140
8446
|
executables,
|
|
8141
8447
|
sharedLibs,
|
|
8448
|
+
services,
|
|
8142
8449
|
tools,
|
|
8143
8450
|
} = await getOSPackages(
|
|
8144
8451
|
options.allLayersExplodedDir,
|
|
@@ -8149,14 +8456,16 @@ export async function createMultiXBom(pathList, options) {
|
|
|
8149
8456
|
options.bundledSdks = bundledSdks;
|
|
8150
8457
|
options.bundledRuntimes = bundledRuntimes;
|
|
8151
8458
|
options.binPaths = binPaths;
|
|
8459
|
+
options.unpackagedExecutableCount = executables?.length || 0;
|
|
8460
|
+
options.unpackagedSharedLibraryCount = sharedLibs?.length || 0;
|
|
8152
8461
|
if (DEBUG_MODE) {
|
|
8153
8462
|
console.log(
|
|
8154
|
-
`**OS**: Found ${osPackages.length} OS packages, ${executables?.length} executables,
|
|
8463
|
+
`**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
8464
|
);
|
|
8156
8465
|
}
|
|
8157
8466
|
if (osPackages.length) {
|
|
8158
8467
|
thoughtLog(
|
|
8159
|
-
`I found ${osPackages.length} OS packages and ${
|
|
8468
|
+
`I found ${osPackages.length} OS packages, ${osPackageFiles?.length || 0} package-owned files, and ${services?.length || 0} packaged services at ${options.allLayersExplodedDir}`,
|
|
8160
8469
|
);
|
|
8161
8470
|
} else if (executables?.length || sharedLibs?.length) {
|
|
8162
8471
|
thoughtLog(
|
|
@@ -8178,14 +8487,20 @@ export async function createMultiXBom(pathList, options) {
|
|
|
8178
8487
|
).concat(tools);
|
|
8179
8488
|
}
|
|
8180
8489
|
components = components.concat(osPackages);
|
|
8490
|
+
components = components.concat(osPackageFiles || []);
|
|
8181
8491
|
components = components.concat(executables);
|
|
8182
8492
|
components = components.concat(sharedLibs);
|
|
8183
8493
|
if (dependenciesList?.length) {
|
|
8184
8494
|
dependencies = mergeDependencies(dependencies, dependenciesList);
|
|
8185
8495
|
}
|
|
8496
|
+
if (services?.length) {
|
|
8497
|
+
options.services = mergeServices(options.services || [], services);
|
|
8498
|
+
}
|
|
8186
8499
|
if (parentComponent && Object.keys(parentComponent).length) {
|
|
8187
8500
|
// Make the parent oci image depend on all os components
|
|
8188
|
-
const parentDependsOn = new Set(
|
|
8501
|
+
const parentDependsOn = new Set(
|
|
8502
|
+
osPackages.concat(osPackageFiles || []).map((p) => p["bom-ref"]),
|
|
8503
|
+
);
|
|
8189
8504
|
dependencies.splice(0, 0, {
|
|
8190
8505
|
ref: parentComponent["bom-ref"],
|
|
8191
8506
|
dependsOn: Array.from(parentDependsOn).sort(),
|
|
@@ -8887,6 +9202,28 @@ export async function createMultiXBom(pathList, options) {
|
|
|
8887
9202
|
}
|
|
8888
9203
|
}
|
|
8889
9204
|
}
|
|
9205
|
+
if (hasAnyProjectType(["asar"], options)) {
|
|
9206
|
+
setProjectTypeActivityContext("asar", path);
|
|
9207
|
+
bomData = await createAsarBom(path, options);
|
|
9208
|
+
if (bomData?.bomJson?.components?.length) {
|
|
9209
|
+
if (DEBUG_MODE) {
|
|
9210
|
+
console.log(
|
|
9211
|
+
`Found ${bomData.bomJson.components.length} ASAR component(s) at ${path}`,
|
|
9212
|
+
);
|
|
9213
|
+
}
|
|
9214
|
+
components = components.concat(bomData.bomJson.components);
|
|
9215
|
+
dependencies = mergeDependencies(
|
|
9216
|
+
dependencies,
|
|
9217
|
+
bomData.bomJson.dependencies,
|
|
9218
|
+
);
|
|
9219
|
+
if (
|
|
9220
|
+
bomData.parentComponent &&
|
|
9221
|
+
Object.keys(bomData.parentComponent).length
|
|
9222
|
+
) {
|
|
9223
|
+
parentSubComponents.push(bomData.parentComponent);
|
|
9224
|
+
}
|
|
9225
|
+
}
|
|
9226
|
+
}
|
|
8890
9227
|
if (hasAnyProjectType(["vscode-extension"], options)) {
|
|
8891
9228
|
setProjectTypeActivityContext("vscode-extension", path);
|
|
8892
9229
|
bomData = await createVscodeExtensionBom(path, options);
|
|
@@ -9326,6 +9663,16 @@ export async function createXBom(path, options) {
|
|
|
9326
9663
|
return await createVscodeExtensionBom(path, options);
|
|
9327
9664
|
}
|
|
9328
9665
|
|
|
9666
|
+
// Electron ASAR archives
|
|
9667
|
+
const asarFiles = getAllFiles(
|
|
9668
|
+
path,
|
|
9669
|
+
`${options.multiProject ? "**/" : ""}*.asar`,
|
|
9670
|
+
options,
|
|
9671
|
+
);
|
|
9672
|
+
if (asarFiles.length) {
|
|
9673
|
+
return await createAsarBom(path, options);
|
|
9674
|
+
}
|
|
9675
|
+
|
|
9329
9676
|
// Helm charts
|
|
9330
9677
|
const chartFiles = getAllFiles(
|
|
9331
9678
|
path,
|
|
@@ -9428,6 +9775,35 @@ export async function createXBom(path, options) {
|
|
|
9428
9775
|
}
|
|
9429
9776
|
}
|
|
9430
9777
|
|
|
9778
|
+
/**
|
|
9779
|
+
* Function to create a hardware BOM for the current host.
|
|
9780
|
+
*
|
|
9781
|
+
* @param {string} _path Source path (unused for live host HBOM generation)
|
|
9782
|
+
* @param {Object} options Parse options from the cli
|
|
9783
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
9784
|
+
*/
|
|
9785
|
+
export async function createHBom(_path, options) {
|
|
9786
|
+
ensureHbomRuntimeSupport(options, options.commandName || "hbom");
|
|
9787
|
+
let bomJson = await createHbomDocument(options);
|
|
9788
|
+
if (options.includeRuntime) {
|
|
9789
|
+
const runtimeOptions = {
|
|
9790
|
+
...options,
|
|
9791
|
+
includeRuntime: false,
|
|
9792
|
+
multiProject: false,
|
|
9793
|
+
projectType: ["os"],
|
|
9794
|
+
};
|
|
9795
|
+
const obomData = await createOSBom(_path, runtimeOptions);
|
|
9796
|
+
bomJson = mergeHostInventoryBoms(bomJson, obomData);
|
|
9797
|
+
} else {
|
|
9798
|
+
bomJson = mergeHostInventoryBoms(bomJson);
|
|
9799
|
+
}
|
|
9800
|
+
return {
|
|
9801
|
+
bomJson,
|
|
9802
|
+
dependencies: bomJson.dependencies || [],
|
|
9803
|
+
parentComponent: bomJson.metadata?.component,
|
|
9804
|
+
};
|
|
9805
|
+
}
|
|
9806
|
+
|
|
9431
9807
|
/**
|
|
9432
9808
|
* Function to create bom string for various languages
|
|
9433
9809
|
*
|
|
@@ -9440,6 +9816,21 @@ export async function createBom(path, options) {
|
|
|
9440
9816
|
if (!projectType) {
|
|
9441
9817
|
projectType = [];
|
|
9442
9818
|
}
|
|
9819
|
+
ensureNoMixedHbomProjectTypes(projectType);
|
|
9820
|
+
if (hasHbomProjectType(projectType)) {
|
|
9821
|
+
const selectedHbomProjectType = Array.isArray(projectType)
|
|
9822
|
+
? projectType[0]
|
|
9823
|
+
: `${projectType}`.split(",")[0];
|
|
9824
|
+
options.projectType = [selectedHbomProjectType];
|
|
9825
|
+
setActivityContext({
|
|
9826
|
+
projectType: selectedHbomProjectType,
|
|
9827
|
+
sourcePath: path,
|
|
9828
|
+
});
|
|
9829
|
+
thoughtLog(
|
|
9830
|
+
"The user wants a Hardware Bill-of-Materials (HBOM) for the current host. Let's use the dedicated hardware collector.",
|
|
9831
|
+
);
|
|
9832
|
+
return await createHBom(path, options);
|
|
9833
|
+
}
|
|
9443
9834
|
let exportData;
|
|
9444
9835
|
let isContainerMode = false;
|
|
9445
9836
|
const isLocalDirectoryInput =
|
|
@@ -9574,6 +9965,9 @@ export async function createBom(path, options) {
|
|
|
9574
9965
|
if (path.endsWith(".war")) {
|
|
9575
9966
|
projectType = ["java"];
|
|
9576
9967
|
}
|
|
9968
|
+
if (!projectType.length && path.endsWith(".asar")) {
|
|
9969
|
+
projectType = ["asar"];
|
|
9970
|
+
}
|
|
9577
9971
|
if (projectType.length > 1) {
|
|
9578
9972
|
setActivityContext({
|
|
9579
9973
|
projectType: projectType.join(","),
|
|
@@ -9697,6 +10091,9 @@ export async function createBom(path, options) {
|
|
|
9697
10091
|
if (PROJECT_TYPE_ALIASES["caxa"].includes(projectType[0])) {
|
|
9698
10092
|
return await createCaxaBom(path, options);
|
|
9699
10093
|
}
|
|
10094
|
+
if (PROJECT_TYPE_ALIASES["asar"].includes(projectType[0])) {
|
|
10095
|
+
return await createAsarBom(path, options);
|
|
10096
|
+
}
|
|
9700
10097
|
if (PROJECT_TYPE_ALIASES["vscode-extension"].includes(projectType[0])) {
|
|
9701
10098
|
return await createVscodeExtensionBom(path, options);
|
|
9702
10099
|
}
|
|
@@ -9741,17 +10138,33 @@ export async function createBom(path, options) {
|
|
|
9741
10138
|
* @throws {Error} if the request fails
|
|
9742
10139
|
*/
|
|
9743
10140
|
export async function submitBom(args, bomContents) {
|
|
10141
|
+
const dependencyTrackApiUrl = getDependencyTrackBomApiUrl(args.serverUrl);
|
|
10142
|
+
const serverUrl = dependencyTrackApiUrl?.toString();
|
|
10143
|
+
if (!dependencyTrackApiUrl || !serverUrl) {
|
|
10144
|
+
console.log(
|
|
10145
|
+
"Invalid Dependency-Track server URL. Provide an absolute http(s) URL without dangerous characters.",
|
|
10146
|
+
);
|
|
10147
|
+
args.failOnError && process.exit(1);
|
|
10148
|
+
return undefined;
|
|
10149
|
+
}
|
|
10150
|
+
const serverHost = dependencyTrackApiUrl.hostname;
|
|
10151
|
+
if (!isAllowedHttpHost(serverHost)) {
|
|
10152
|
+
console.log(
|
|
10153
|
+
`Dependency-Track server host '${serverHost}' is not allowed by CDXGEN_ALLOWED_HOSTS.`,
|
|
10154
|
+
);
|
|
10155
|
+
args.failOnError && process.exit(1);
|
|
10156
|
+
return undefined;
|
|
10157
|
+
}
|
|
9744
10158
|
if (isDryRun) {
|
|
9745
10159
|
recordActivity({
|
|
9746
10160
|
kind: "network",
|
|
9747
10161
|
reason:
|
|
9748
10162
|
"Dry run mode blocks Dependency-Track submission and reports the request instead.",
|
|
9749
10163
|
status: "blocked",
|
|
9750
|
-
target:
|
|
10164
|
+
target: serverUrl,
|
|
9751
10165
|
});
|
|
9752
10166
|
return undefined;
|
|
9753
10167
|
}
|
|
9754
|
-
const serverUrl = getDependencyTrackBomUrl(args.serverUrl);
|
|
9755
10168
|
const bomPayload = buildDependencyTrackBomPayload(args, bomContents);
|
|
9756
10169
|
if (!bomPayload) {
|
|
9757
10170
|
console.log(
|
|
@@ -9770,15 +10183,7 @@ export async function submitBom(args, bomContents) {
|
|
|
9770
10183
|
);
|
|
9771
10184
|
}
|
|
9772
10185
|
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, {
|
|
10186
|
+
const requestOptions = {
|
|
9782
10187
|
method: "PUT",
|
|
9783
10188
|
followRedirect: false,
|
|
9784
10189
|
https: {
|
|
@@ -9787,11 +10192,22 @@ export async function submitBom(args, bomContents) {
|
|
|
9787
10192
|
headers: {
|
|
9788
10193
|
"X-Api-Key": (args.apiKey || "").replace(/[\r\n]/g, ""),
|
|
9789
10194
|
"Content-Type": "application/json",
|
|
9790
|
-
"user-agent": `@CycloneDX/cdxgen ${CDXGEN_VERSION}`,
|
|
9791
10195
|
},
|
|
9792
10196
|
json: bomPayload,
|
|
9793
10197
|
responseType: "json",
|
|
9794
|
-
|
|
10198
|
+
context: {
|
|
10199
|
+
activityIntent: "bom-submit",
|
|
10200
|
+
},
|
|
10201
|
+
};
|
|
10202
|
+
if (DEBUG_MODE && args.skipDtTlsCheck) {
|
|
10203
|
+
console.log(
|
|
10204
|
+
"Calling ",
|
|
10205
|
+
serverUrl,
|
|
10206
|
+
"with --skip-dt-tls-check argument: Skip DT TLS check.",
|
|
10207
|
+
);
|
|
10208
|
+
}
|
|
10209
|
+
// See issue #1963 regarding CRLF hardening
|
|
10210
|
+
return await cdxgenAgent(dependencyTrackApiUrl, requestOptions).json();
|
|
9795
10211
|
} catch (error) {
|
|
9796
10212
|
if (error.response && error.response.statusCode === 401) {
|
|
9797
10213
|
// Unauthorized
|
|
@@ -9804,7 +10220,7 @@ export async function submitBom(args, bomContents) {
|
|
|
9804
10220
|
);
|
|
9805
10221
|
// Method not allowed errors
|
|
9806
10222
|
try {
|
|
9807
|
-
return await
|
|
10223
|
+
return await cdxgenAgent(dependencyTrackApiUrl, {
|
|
9808
10224
|
method: "POST",
|
|
9809
10225
|
followRedirect: false,
|
|
9810
10226
|
https: {
|
|
@@ -9813,10 +10229,12 @@ export async function submitBom(args, bomContents) {
|
|
|
9813
10229
|
headers: {
|
|
9814
10230
|
"X-Api-Key": (args.apiKey || "").replace(/[\r\n]/g, ""),
|
|
9815
10231
|
"Content-Type": "application/json",
|
|
9816
|
-
"user-agent": `@CycloneDX/cdxgen ${CDXGEN_VERSION}`,
|
|
9817
10232
|
},
|
|
9818
10233
|
json: bomPayload,
|
|
9819
10234
|
responseType: "json",
|
|
10235
|
+
context: {
|
|
10236
|
+
activityIntent: "bom-submit",
|
|
10237
|
+
},
|
|
9820
10238
|
}).json();
|
|
9821
10239
|
} catch (error) {
|
|
9822
10240
|
if (DEBUG_MODE) {
|