@cyclonedx/cdxgen 12.3.1 → 12.3.2
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/bin/cdxgen.js +1 -2
- package/data/rules/ai-agent-governance.yaml +43 -0
- package/data/rules/mcp-servers.yaml +36 -2
- package/lib/cli/index.js +295 -17
- package/lib/cli/index.poku.js +296 -1
- package/lib/helpers/agentFormulationParser.js +4 -1
- package/lib/helpers/aiInventory.js +262 -0
- package/lib/helpers/aiInventory.poku.js +111 -0
- package/lib/helpers/analyzer.js +375 -45
- package/lib/helpers/analyzer.poku.js +50 -0
- package/lib/helpers/auditCategories.js +76 -0
- package/lib/helpers/display.js +5 -2
- package/lib/helpers/display.poku.js +25 -0
- package/lib/helpers/formulationParsers.js +26 -6
- package/lib/helpers/jsonLike.js +21 -20
- package/lib/helpers/jsonLike.poku.js +34 -0
- package/lib/helpers/mcpConfigParser.js +11 -11
- package/lib/helpers/mcpConfigParser.poku.js +67 -0
- package/lib/helpers/mcpDiscovery.js +13 -23
- package/lib/helpers/mcpDiscovery.poku.js +21 -0
- package/lib/helpers/utils.js +2 -1
- package/lib/helpers/utils.poku.js +19 -1
- package/lib/stages/postgen/annotator.js +2 -1
- package/lib/stages/postgen/annotator.poku.js +15 -0
- package/lib/stages/postgen/auditBom.js +12 -6
- package/lib/stages/postgen/auditBom.poku.js +111 -4
- package/lib/stages/postgen/postgen.js +229 -6
- package/lib/stages/postgen/postgen.poku.js +180 -0
- package/package.json +1 -1
- package/types/lib/cli/index.d.ts +1 -0
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/helpers/agentFormulationParser.d.ts +19 -0
- package/types/lib/helpers/agentFormulationParser.d.ts.map +1 -0
- package/types/lib/helpers/aiInventory.d.ts +23 -0
- package/types/lib/helpers/aiInventory.d.ts.map +1 -0
- package/types/lib/helpers/analyzer.d.ts +5 -0
- package/types/lib/helpers/analyzer.d.ts.map +1 -1
- package/types/lib/helpers/auditCategories.d.ts +12 -0
- package/types/lib/helpers/auditCategories.d.ts.map +1 -0
- package/types/lib/helpers/communityAiConfigParser.d.ts +29 -0
- package/types/lib/helpers/communityAiConfigParser.d.ts.map +1 -0
- package/types/lib/helpers/display.d.ts.map +1 -1
- package/types/lib/helpers/formulationParsers.d.ts.map +1 -1
- package/types/lib/helpers/jsonLike.d.ts +4 -0
- package/types/lib/helpers/jsonLike.d.ts.map +1 -0
- package/types/lib/helpers/mcp.d.ts +29 -0
- package/types/lib/helpers/mcp.d.ts.map +1 -0
- package/types/lib/helpers/mcpConfigParser.d.ts +30 -0
- package/types/lib/helpers/mcpConfigParser.d.ts.map +1 -0
- package/types/lib/helpers/mcpDiscovery.d.ts +5 -0
- package/types/lib/helpers/mcpDiscovery.d.ts.map +1 -0
- package/types/lib/helpers/utils.d.ts +2 -0
- package/types/lib/helpers/utils.d.ts.map +1 -1
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
- package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
package/bin/cdxgen.js
CHANGED
|
@@ -462,9 +462,8 @@ const args = _yargs
|
|
|
462
462
|
})
|
|
463
463
|
.option("tlp-classification", {
|
|
464
464
|
description:
|
|
465
|
-
|
|
465
|
+
"Traffic Light Protocol (TLP) is a classification system for identifying the potential risk associated with an artefact, including whether it is subject to certain types of legal, financial, or technical threats. Refer to [https://www.first.org/tlp/](https://www.first.org/tlp/) for further information.",
|
|
466
466
|
choices: ["CLEAR", "GREEN", "AMBER", "AMBER_AND_STRICT", "RED"],
|
|
467
|
-
default: "CLEAR",
|
|
468
467
|
hidden: true,
|
|
469
468
|
})
|
|
470
469
|
.option("env-audit", {
|
|
@@ -204,3 +204,46 @@
|
|
|
204
204
|
"hiddenMcpUrls": $prop($, 'cdx:agent:hiddenMcpUrls'),
|
|
205
205
|
"providerNames": $prop($, 'cdx:agent:providerNames')
|
|
206
206
|
}
|
|
207
|
+
|
|
208
|
+
- id: AGT-007
|
|
209
|
+
name: "AI agent or skill file is included in a build or post-build SBOM"
|
|
210
|
+
description: "Shipped AI instruction and skill files deserve explicit review because they can alter developer tooling, release-time automation, and downstream runtime behavior."
|
|
211
|
+
severity: medium
|
|
212
|
+
category: ai-agent
|
|
213
|
+
standards:
|
|
214
|
+
owasp-ai-top-10:
|
|
215
|
+
- "LLM05: Supply Chain Vulnerabilities"
|
|
216
|
+
- "LLM08: Excessive Agency"
|
|
217
|
+
nist-ai-rmf:
|
|
218
|
+
- "Govern"
|
|
219
|
+
- "Map"
|
|
220
|
+
nist-ssdf:
|
|
221
|
+
- "Review build and release instructions before distribution"
|
|
222
|
+
condition: |
|
|
223
|
+
components[
|
|
224
|
+
(
|
|
225
|
+
$prop($, 'cdx:agent:inventorySource') = 'agent-file'
|
|
226
|
+
or $prop($, 'cdx:agent:inventorySource') = 'community-config'
|
|
227
|
+
)
|
|
228
|
+
and (
|
|
229
|
+
$prop($, 'cdx:file:kind') = 'skill-file'
|
|
230
|
+
or $prop($, 'cdx:file:kind') = 'agent-instructions'
|
|
231
|
+
or $prop($, 'cdx:file:kind') = 'copilot-instructions'
|
|
232
|
+
or $prop($, 'cdx:file:kind') = 'copilot-setup-workflow'
|
|
233
|
+
or $prop($, 'cdx:file:kind') = 'ai-agent-file'
|
|
234
|
+
)
|
|
235
|
+
and $count($$.metadata.lifecycles[phase = 'build' or phase = 'post-build']) > 0
|
|
236
|
+
]
|
|
237
|
+
location: |
|
|
238
|
+
{
|
|
239
|
+
"bomRef": $."bom-ref",
|
|
240
|
+
"file": $prop($, 'SrcFile')
|
|
241
|
+
}
|
|
242
|
+
message: "AI instruction or skill file '{{ name }}' is included in a build/post-build SBOM"
|
|
243
|
+
mitigation: "If the file must ship, keep the BOM review-friendly with '--bom-audit --bom-audit-categories ai-agent' and consider '--tlp-classification AMBER'. If you want a package-only BOM, rerun with '--exclude-type ai-skill'."
|
|
244
|
+
evidence: |
|
|
245
|
+
{
|
|
246
|
+
"inventorySource": $prop($, 'cdx:agent:inventorySource'),
|
|
247
|
+
"fileKind": $prop($, 'cdx:file:kind'),
|
|
248
|
+
"providerNames": $prop($, 'cdx:agent:providerNames')
|
|
249
|
+
}
|
|
@@ -191,8 +191,9 @@
|
|
|
191
191
|
{
|
|
192
192
|
"configFormat": $prop($, 'cdx:mcp:configFormat'),
|
|
193
193
|
"configKey": $prop($, 'cdx:mcp:configKey'),
|
|
194
|
-
"
|
|
195
|
-
"
|
|
194
|
+
"credentialExposureFieldCount": $prop($, 'cdx:mcp:credentialExposureFieldCount'),
|
|
195
|
+
"credentialIndicatorCount": $prop($, 'cdx:mcp:credentialIndicatorCount'),
|
|
196
|
+
"credentialReferenceCount": $prop($, 'cdx:mcp:credentialReferenceCount')
|
|
196
197
|
}
|
|
197
198
|
|
|
198
199
|
- id: MCP-006
|
|
@@ -268,3 +269,36 @@
|
|
|
268
269
|
"trustProfile": $prop($, 'cdx:mcp:trustProfile'),
|
|
269
270
|
"authPosture": $prop($, 'cdx:mcp:authPosture')
|
|
270
271
|
}
|
|
272
|
+
|
|
273
|
+
- id: MCP-008
|
|
274
|
+
name: "MCP configuration file is included in a build or post-build SBOM"
|
|
275
|
+
description: "Committed MCP client configuration files can carry trust, auth, and distribution sensitivity even when they are not actively used during the current scan."
|
|
276
|
+
severity: medium
|
|
277
|
+
category: mcp-server
|
|
278
|
+
standards:
|
|
279
|
+
owasp-ai-top-10:
|
|
280
|
+
- "LLM07: Insecure Plugin Design"
|
|
281
|
+
- "LLM08: Excessive Agency"
|
|
282
|
+
nist-ai-rmf:
|
|
283
|
+
- "Govern"
|
|
284
|
+
- "Map"
|
|
285
|
+
nist-ssdf:
|
|
286
|
+
- "Review configured AI control surfaces before release"
|
|
287
|
+
condition: |
|
|
288
|
+
components[
|
|
289
|
+
$prop($, 'cdx:file:kind') = 'mcp-config'
|
|
290
|
+
and $count($$.metadata.lifecycles[phase = 'build' or phase = 'post-build']) > 0
|
|
291
|
+
]
|
|
292
|
+
location: |
|
|
293
|
+
{
|
|
294
|
+
"bomRef": $."bom-ref",
|
|
295
|
+
"file": $prop($, 'SrcFile')
|
|
296
|
+
}
|
|
297
|
+
message: "MCP config file '{{ name }}' is included in a build/post-build SBOM"
|
|
298
|
+
mitigation: "Review the config with '--bom-audit --bom-audit-categories mcp-server', consider '--tlp-classification AMBER' before sharing the BOM broadly, or rerun with '--exclude-type mcp' if config artifacts should be omitted."
|
|
299
|
+
evidence: |
|
|
300
|
+
{
|
|
301
|
+
"configFormat": $prop($, 'cdx:mcp:configFormat'),
|
|
302
|
+
"configuredServiceCount": $prop($, 'cdx:mcp:configuredServiceCount'),
|
|
303
|
+
"configuredServiceNames": $prop($, 'cdx:mcp:configuredServiceNames')
|
|
304
|
+
}
|
package/lib/cli/index.js
CHANGED
|
@@ -18,10 +18,24 @@ import { parse } from "ssri";
|
|
|
18
18
|
import { v4 as uuidv4 } from "uuid";
|
|
19
19
|
import { parse as loadYaml } from "yaml";
|
|
20
20
|
|
|
21
|
+
import {
|
|
22
|
+
AI_INSTRUCTION_FILE_KINDS,
|
|
23
|
+
AI_INVENTORY_PROJECT_TYPES,
|
|
24
|
+
AI_SKILL_FILE_KIND,
|
|
25
|
+
collectAiInventory,
|
|
26
|
+
filterInventoryDependencies,
|
|
27
|
+
filterInventorySubjectsByTypes,
|
|
28
|
+
inventoryPropertyValue,
|
|
29
|
+
MCP_CONFIG_FILE_KIND,
|
|
30
|
+
optionIncludesAiInventoryProjectType,
|
|
31
|
+
summarizeAiInventory,
|
|
32
|
+
} from "../helpers/aiInventory.js";
|
|
21
33
|
import {
|
|
22
34
|
detectMcpInventory,
|
|
35
|
+
detectPythonMcpInventory,
|
|
23
36
|
findJSImportsExports,
|
|
24
37
|
} from "../helpers/analyzer.js";
|
|
38
|
+
import { expandBomAuditCategories } from "../helpers/auditCategories.js";
|
|
25
39
|
import { parseCaxaMetadata } from "../helpers/caxa.js";
|
|
26
40
|
import {
|
|
27
41
|
CHROME_EXTENSION_PURL_TYPE,
|
|
@@ -197,6 +211,9 @@ import {
|
|
|
197
211
|
shouldFetchLicense,
|
|
198
212
|
splitOutputByGradleProjects,
|
|
199
213
|
} from "../helpers/utils.js";
|
|
214
|
+
|
|
215
|
+
export { summarizeAiInventory } from "../helpers/aiInventory.js";
|
|
216
|
+
|
|
200
217
|
import {
|
|
201
218
|
cleanupTempDir,
|
|
202
219
|
collectInstalledExtensions,
|
|
@@ -2717,16 +2734,32 @@ export async function createJavaBom(path, options) {
|
|
|
2717
2734
|
* @param {Object} options Parse options from the cli
|
|
2718
2735
|
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
2719
2736
|
*/
|
|
2737
|
+
function getRequestedAiInventoryTypes(options) {
|
|
2738
|
+
return AI_INVENTORY_PROJECT_TYPES.filter((type) =>
|
|
2739
|
+
optionIncludesAiInventoryProjectType(options?.projectType, type),
|
|
2740
|
+
);
|
|
2741
|
+
}
|
|
2742
|
+
|
|
2743
|
+
function getExcludedAiInventoryTypes(options) {
|
|
2744
|
+
return AI_INVENTORY_PROJECT_TYPES.filter((type) =>
|
|
2745
|
+
optionIncludesAiInventoryProjectType(options?.excludeType, type),
|
|
2746
|
+
);
|
|
2747
|
+
}
|
|
2748
|
+
|
|
2749
|
+
function getExactAiInventoryType(options) {
|
|
2750
|
+
const requestedAiInventoryTypes = getRequestedAiInventoryTypes(options);
|
|
2751
|
+
return requestedAiInventoryTypes.length === 1 &&
|
|
2752
|
+
Array.isArray(options?.projectType) &&
|
|
2753
|
+
options.projectType.length === 1
|
|
2754
|
+
? requestedAiInventoryTypes[0]
|
|
2755
|
+
: undefined;
|
|
2756
|
+
}
|
|
2757
|
+
|
|
2720
2758
|
function shouldDetectMcpInventory(options, allImports = {}) {
|
|
2721
2759
|
if (hasAnyProjectType(["mcp"], options, false)) {
|
|
2722
2760
|
return true;
|
|
2723
2761
|
}
|
|
2724
|
-
const auditCategories =
|
|
2725
|
-
? options.bomAuditCategories
|
|
2726
|
-
: String(options?.bomAuditCategories || "")
|
|
2727
|
-
.split(",")
|
|
2728
|
-
.map((category) => category.trim())
|
|
2729
|
-
.filter(Boolean);
|
|
2762
|
+
const auditCategories = expandBomAuditCategories(options?.bomAuditCategories);
|
|
2730
2763
|
if (
|
|
2731
2764
|
auditCategories.some((category) =>
|
|
2732
2765
|
["mcp-server", "ai-agent"].includes(category),
|
|
@@ -2740,6 +2773,91 @@ function shouldDetectMcpInventory(options, allImports = {}) {
|
|
|
2740
2773
|
});
|
|
2741
2774
|
}
|
|
2742
2775
|
|
|
2776
|
+
function summarizeAiInventoryNames(subjects, discoveryPath, kindSet) {
|
|
2777
|
+
return [
|
|
2778
|
+
...new Set(
|
|
2779
|
+
(subjects || [])
|
|
2780
|
+
.filter((subject) =>
|
|
2781
|
+
kindSet.has(inventoryPropertyValue(subject, "cdx:file:kind")),
|
|
2782
|
+
)
|
|
2783
|
+
.map((subject) => inventoryPropertyValue(subject, "SrcFile"))
|
|
2784
|
+
.filter(Boolean)
|
|
2785
|
+
.map(
|
|
2786
|
+
(filePath) => relative(discoveryPath, filePath) || basename(filePath),
|
|
2787
|
+
),
|
|
2788
|
+
),
|
|
2789
|
+
].sort();
|
|
2790
|
+
}
|
|
2791
|
+
|
|
2792
|
+
function summarizeAiInventoryServiceNames(services) {
|
|
2793
|
+
return [
|
|
2794
|
+
...new Set(
|
|
2795
|
+
(services || []).map((service) => service?.name).filter(Boolean),
|
|
2796
|
+
),
|
|
2797
|
+
].sort();
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
function formatAiInventorySummaryLine(label, count, nameList) {
|
|
2801
|
+
return ` ${label.padEnd(20)} ${count}${nameList.length ? ` (${nameList.join(", ")})` : ""}`;
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2804
|
+
function emitAiInventorySummary(aiInventory, discoveryPath) {
|
|
2805
|
+
const summary = summarizeAiInventory(aiInventory);
|
|
2806
|
+
const totalInventory =
|
|
2807
|
+
summary.instructionCount +
|
|
2808
|
+
summary.skillCount +
|
|
2809
|
+
summary.mcpConfigCount +
|
|
2810
|
+
summary.mcpServiceCount;
|
|
2811
|
+
if (!totalInventory) {
|
|
2812
|
+
return;
|
|
2813
|
+
}
|
|
2814
|
+
const instructionNames = summarizeAiInventoryNames(
|
|
2815
|
+
aiInventory.components,
|
|
2816
|
+
discoveryPath,
|
|
2817
|
+
AI_INSTRUCTION_FILE_KINDS,
|
|
2818
|
+
);
|
|
2819
|
+
const skillNames = summarizeAiInventoryNames(
|
|
2820
|
+
aiInventory.components,
|
|
2821
|
+
discoveryPath,
|
|
2822
|
+
new Set([AI_SKILL_FILE_KIND]),
|
|
2823
|
+
);
|
|
2824
|
+
const mcpConfigNames = summarizeAiInventoryNames(
|
|
2825
|
+
aiInventory.components,
|
|
2826
|
+
discoveryPath,
|
|
2827
|
+
new Set([MCP_CONFIG_FILE_KIND]),
|
|
2828
|
+
);
|
|
2829
|
+
const mcpServiceNames = summarizeAiInventoryServiceNames(
|
|
2830
|
+
aiInventory.services,
|
|
2831
|
+
);
|
|
2832
|
+
console.warn(
|
|
2833
|
+
[
|
|
2834
|
+
"AI Inventory Summary:",
|
|
2835
|
+
formatAiInventorySummaryLine(
|
|
2836
|
+
"AI instruction files:",
|
|
2837
|
+
summary.instructionCount,
|
|
2838
|
+
instructionNames,
|
|
2839
|
+
),
|
|
2840
|
+
formatAiInventorySummaryLine(
|
|
2841
|
+
"Skill files:",
|
|
2842
|
+
summary.skillCount,
|
|
2843
|
+
skillNames,
|
|
2844
|
+
),
|
|
2845
|
+
formatAiInventorySummaryLine(
|
|
2846
|
+
"MCP configs:",
|
|
2847
|
+
summary.mcpConfigCount,
|
|
2848
|
+
mcpConfigNames,
|
|
2849
|
+
),
|
|
2850
|
+
formatAiInventorySummaryLine(
|
|
2851
|
+
"MCP services:",
|
|
2852
|
+
summary.mcpServiceCount,
|
|
2853
|
+
mcpServiceNames,
|
|
2854
|
+
),
|
|
2855
|
+
"",
|
|
2856
|
+
"Run --bom-audit --bom-audit-categories ai-inventory to audit these surfaces.",
|
|
2857
|
+
].join("\n"),
|
|
2858
|
+
);
|
|
2859
|
+
}
|
|
2860
|
+
|
|
2743
2861
|
export async function createNodejsBom(path, options) {
|
|
2744
2862
|
let pkgList = [];
|
|
2745
2863
|
let manifestFiles = [];
|
|
@@ -2747,6 +2865,15 @@ export async function createNodejsBom(path, options) {
|
|
|
2747
2865
|
let parentComponent = {};
|
|
2748
2866
|
const parentSubComponents = [];
|
|
2749
2867
|
let ppurl = "";
|
|
2868
|
+
const requestedAiInventoryTypes = getRequestedAiInventoryTypes(options);
|
|
2869
|
+
const excludedAiInventoryTypes = getExcludedAiInventoryTypes(options);
|
|
2870
|
+
const exactAiInventoryType = getExactAiInventoryType(options);
|
|
2871
|
+
const includedAiInventoryTypes = exactAiInventoryType
|
|
2872
|
+
? requestedAiInventoryTypes
|
|
2873
|
+
: AI_INVENTORY_PROJECT_TYPES.filter(
|
|
2874
|
+
(type) => !excludedAiInventoryTypes.includes(type),
|
|
2875
|
+
);
|
|
2876
|
+
let aiInventory = { components: [], dependencies: [], services: [] };
|
|
2750
2877
|
// Docker mode requires special handling
|
|
2751
2878
|
if (hasAnyProjectType(["docker", "oci", "container", "os"], options, false)) {
|
|
2752
2879
|
const pkgJsonFiles = getAllFiles(path, "**/package.json", options);
|
|
@@ -2758,11 +2885,27 @@ export async function createNodejsBom(path, options) {
|
|
|
2758
2885
|
pkgList = pkgList.concat(dlist);
|
|
2759
2886
|
}
|
|
2760
2887
|
}
|
|
2888
|
+
if (includedAiInventoryTypes.length) {
|
|
2889
|
+
aiInventory = collectAiInventory(
|
|
2890
|
+
path,
|
|
2891
|
+
options,
|
|
2892
|
+
includedAiInventoryTypes,
|
|
2893
|
+
);
|
|
2894
|
+
}
|
|
2895
|
+
if (aiInventory.components?.length) {
|
|
2896
|
+
pkgList = trimComponents(pkgList.concat(aiInventory.components));
|
|
2897
|
+
}
|
|
2761
2898
|
return buildBomNSData(options, pkgList, "npm", {
|
|
2762
2899
|
allImports: {},
|
|
2763
2900
|
src: path,
|
|
2764
2901
|
filename: "package.json",
|
|
2902
|
+
dependencies: mergeDependencies(
|
|
2903
|
+
[],
|
|
2904
|
+
aiInventory.dependencies,
|
|
2905
|
+
parentComponent,
|
|
2906
|
+
),
|
|
2765
2907
|
parentComponent,
|
|
2908
|
+
services: aiInventory.services,
|
|
2766
2909
|
});
|
|
2767
2910
|
}
|
|
2768
2911
|
}
|
|
@@ -2785,6 +2928,40 @@ export async function createNodejsBom(path, options) {
|
|
|
2785
2928
|
mcpInventory = detectMcpInventory(path, options.deep);
|
|
2786
2929
|
}
|
|
2787
2930
|
}
|
|
2931
|
+
if (includedAiInventoryTypes.length) {
|
|
2932
|
+
aiInventory = collectAiInventory(path, options, includedAiInventoryTypes);
|
|
2933
|
+
}
|
|
2934
|
+
if (excludedAiInventoryTypes.includes("mcp")) {
|
|
2935
|
+
mcpInventory = { components: [], dependencies: [], services: [] };
|
|
2936
|
+
}
|
|
2937
|
+
const aiInventorySummary = summarizeAiInventory(aiInventory);
|
|
2938
|
+
if (!exactAiInventoryType) {
|
|
2939
|
+
if (aiInventorySummary.instructionCount || aiInventorySummary.skillCount) {
|
|
2940
|
+
thoughtLog(
|
|
2941
|
+
`I found ${aiInventorySummary.instructionCount + aiInventorySummary.skillCount} AI skill/instruction file component(s). Use '--exclude-type ai-skill' for a package-only BOM, or '--bom-audit --bom-audit-categories ai-inventory' for review-friendly reporting.`,
|
|
2942
|
+
);
|
|
2943
|
+
}
|
|
2944
|
+
if (aiInventorySummary.mcpConfigCount) {
|
|
2945
|
+
thoughtLog(
|
|
2946
|
+
`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.`,
|
|
2947
|
+
);
|
|
2948
|
+
}
|
|
2949
|
+
emitAiInventorySummary(aiInventory, path);
|
|
2950
|
+
}
|
|
2951
|
+
if (exactAiInventoryType === "ai-skill") {
|
|
2952
|
+
const exactComponents = trimComponents([...(aiInventory.components || [])]);
|
|
2953
|
+
const exactDependencies = mergeDependencies([], aiInventory.dependencies);
|
|
2954
|
+
const exactServices = mergeServices([], aiInventory.services);
|
|
2955
|
+
parentComponent = createDefaultParentComponent(path, "generic", options);
|
|
2956
|
+
return buildBomNSData(options, exactComponents, "generic", {
|
|
2957
|
+
dependencies: exactDependencies,
|
|
2958
|
+
filename: path,
|
|
2959
|
+
parentComponent,
|
|
2960
|
+
projectType: exactAiInventoryType,
|
|
2961
|
+
services: exactServices,
|
|
2962
|
+
src: path,
|
|
2963
|
+
});
|
|
2964
|
+
}
|
|
2788
2965
|
let yarnLockFiles = getAllFiles(
|
|
2789
2966
|
path,
|
|
2790
2967
|
`${options.multiProject ? "**/" : ""}yarn.lock`,
|
|
@@ -3675,12 +3852,37 @@ export async function createNodejsBom(path, options) {
|
|
|
3675
3852
|
parentComponent,
|
|
3676
3853
|
);
|
|
3677
3854
|
}
|
|
3855
|
+
if (aiInventory.components?.length) {
|
|
3856
|
+
pkgList = trimComponents(pkgList.concat(aiInventory.components));
|
|
3857
|
+
}
|
|
3858
|
+
if (aiInventory.dependencies?.length) {
|
|
3859
|
+
dependencies = mergeDependencies(
|
|
3860
|
+
dependencies,
|
|
3861
|
+
aiInventory.dependencies,
|
|
3862
|
+
parentComponent,
|
|
3863
|
+
);
|
|
3864
|
+
}
|
|
3865
|
+
const inventoryServices = mergeServices(
|
|
3866
|
+
mergeServices([], mcpInventory.services || []),
|
|
3867
|
+
aiInventory.services || [],
|
|
3868
|
+
);
|
|
3869
|
+
if (exactAiInventoryType === "mcp") {
|
|
3870
|
+
pkgList = trimComponents(filterInventorySubjectsByTypes(pkgList, ["mcp"]));
|
|
3871
|
+
dependencies = filterInventoryDependencies(
|
|
3872
|
+
dependencies,
|
|
3873
|
+
pkgList,
|
|
3874
|
+
inventoryServices,
|
|
3875
|
+
);
|
|
3876
|
+
if (!parentComponent || !Object.keys(parentComponent).length) {
|
|
3877
|
+
parentComponent = createDefaultParentComponent(path, "generic", options);
|
|
3878
|
+
}
|
|
3879
|
+
}
|
|
3678
3880
|
return buildBomNSData(options, pkgList, "npm", {
|
|
3679
3881
|
src: path,
|
|
3680
3882
|
filename: manifestFiles.join(", "),
|
|
3681
3883
|
dependencies,
|
|
3682
3884
|
parentComponent,
|
|
3683
|
-
services:
|
|
3885
|
+
services: inventoryServices,
|
|
3684
3886
|
});
|
|
3685
3887
|
}
|
|
3686
3888
|
|
|
@@ -3859,6 +4061,16 @@ export async function createPythonBom(path, options) {
|
|
|
3859
4061
|
let dependencies = [];
|
|
3860
4062
|
let pkgList = [];
|
|
3861
4063
|
let formulationList = [];
|
|
4064
|
+
const requestedAiInventoryTypes = getRequestedAiInventoryTypes(options);
|
|
4065
|
+
const excludedAiInventoryTypes = getExcludedAiInventoryTypes(options);
|
|
4066
|
+
const includedAiInventoryTypes = AI_INVENTORY_PROJECT_TYPES.filter(
|
|
4067
|
+
(type) =>
|
|
4068
|
+
(!requestedAiInventoryTypes.length ||
|
|
4069
|
+
requestedAiInventoryTypes.includes(type)) &&
|
|
4070
|
+
!excludedAiInventoryTypes.includes(type),
|
|
4071
|
+
);
|
|
4072
|
+
let aiInventory = { components: [], dependencies: [], services: [] };
|
|
4073
|
+
let mcpInventory = { components: [], dependencies: [], services: [] };
|
|
3862
4074
|
const tempDir = safeMkdtempSync(join(getTmpDir(), "cdxgen-venv-"));
|
|
3863
4075
|
let parentComponent = createDefaultParentComponent(path, "pypi", options);
|
|
3864
4076
|
// We are checking only the root here for pipenv
|
|
@@ -4350,6 +4562,24 @@ export async function createPythonBom(path, options) {
|
|
|
4350
4562
|
pkgList = pkgList.concat(dlist);
|
|
4351
4563
|
}
|
|
4352
4564
|
}
|
|
4565
|
+
if (includedAiInventoryTypes.length) {
|
|
4566
|
+
aiInventory = collectAiInventory(path, options, includedAiInventoryTypes);
|
|
4567
|
+
if (includedAiInventoryTypes.includes("mcp")) {
|
|
4568
|
+
mcpInventory = detectPythonMcpInventory(path, options.deep);
|
|
4569
|
+
}
|
|
4570
|
+
}
|
|
4571
|
+
const aiInventorySummary = summarizeAiInventory(aiInventory);
|
|
4572
|
+
if (aiInventorySummary.instructionCount || aiInventorySummary.skillCount) {
|
|
4573
|
+
thoughtLog(
|
|
4574
|
+
`I found ${aiInventorySummary.instructionCount + aiInventorySummary.skillCount} AI skill/instruction file component(s). Use '--exclude-type ai-skill' for a package-only BOM, or '--bom-audit --bom-audit-categories ai-inventory' for review-friendly reporting.`,
|
|
4575
|
+
);
|
|
4576
|
+
}
|
|
4577
|
+
if (aiInventorySummary.mcpConfigCount) {
|
|
4578
|
+
thoughtLog(
|
|
4579
|
+
`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.`,
|
|
4580
|
+
);
|
|
4581
|
+
}
|
|
4582
|
+
emitAiInventorySummary(aiInventory, path);
|
|
4353
4583
|
// Check and complete the dependency tree
|
|
4354
4584
|
if (
|
|
4355
4585
|
isFeatureEnabled(options, "safe-pip-install") &&
|
|
@@ -4398,6 +4628,30 @@ export async function createPythonBom(path, options) {
|
|
|
4398
4628
|
if (shouldFetchLicense()) {
|
|
4399
4629
|
pkgList = await getPyMetadata(pkgList, false);
|
|
4400
4630
|
}
|
|
4631
|
+
if (mcpInventory.components?.length) {
|
|
4632
|
+
pkgList = trimComponents(pkgList.concat(mcpInventory.components));
|
|
4633
|
+
}
|
|
4634
|
+
if (mcpInventory.dependencies?.length) {
|
|
4635
|
+
dependencies = mergeDependencies(
|
|
4636
|
+
dependencies,
|
|
4637
|
+
mcpInventory.dependencies,
|
|
4638
|
+
parentComponent,
|
|
4639
|
+
);
|
|
4640
|
+
}
|
|
4641
|
+
if (aiInventory.components?.length) {
|
|
4642
|
+
pkgList = trimComponents(pkgList.concat(aiInventory.components));
|
|
4643
|
+
}
|
|
4644
|
+
if (aiInventory.dependencies?.length) {
|
|
4645
|
+
dependencies = mergeDependencies(
|
|
4646
|
+
dependencies,
|
|
4647
|
+
aiInventory.dependencies,
|
|
4648
|
+
parentComponent,
|
|
4649
|
+
);
|
|
4650
|
+
}
|
|
4651
|
+
const inventoryServices = mergeServices(
|
|
4652
|
+
mergeServices([], mcpInventory.services || []),
|
|
4653
|
+
aiInventory.services || [],
|
|
4654
|
+
);
|
|
4401
4655
|
return buildBomNSData(options, pkgList, "pypi", {
|
|
4402
4656
|
allImports,
|
|
4403
4657
|
src: path,
|
|
@@ -4405,6 +4659,7 @@ export async function createPythonBom(path, options) {
|
|
|
4405
4659
|
dependencies,
|
|
4406
4660
|
parentComponent,
|
|
4407
4661
|
formulationList,
|
|
4662
|
+
services: inventoryServices,
|
|
4408
4663
|
});
|
|
4409
4664
|
}
|
|
4410
4665
|
|
|
@@ -7882,22 +8137,39 @@ export async function createMultiXBom(pathList, options) {
|
|
|
7882
8137
|
if (pathList.length > 2) {
|
|
7883
8138
|
thoughtLog(`Let's thoroughly check the path ${path}.`);
|
|
7884
8139
|
}
|
|
7885
|
-
// Node.js
|
|
7886
|
-
if (hasAnyProjectType(["oci", "js"], options)) {
|
|
8140
|
+
// Node.js and AI inventory
|
|
8141
|
+
if (hasAnyProjectType(["oci", "js", "mcp", "ai-skill"], options)) {
|
|
8142
|
+
const exactAiInventoryType = getExactAiInventoryType(options);
|
|
7887
8143
|
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
7888
|
-
|
|
7889
|
-
|
|
7890
|
-
|
|
8144
|
+
if (exactAiInventoryType === "mcp") {
|
|
8145
|
+
thoughtLog(
|
|
8146
|
+
"**MCP**: Looking for MCP services, MCP configs, and related AI control-plane artifacts.",
|
|
8147
|
+
);
|
|
8148
|
+
} else if (exactAiInventoryType === "ai-skill") {
|
|
8149
|
+
thoughtLog(
|
|
8150
|
+
"**AI-SKILL**: Looking for AI instruction, skill, and agent-definition files that can influence build or release flows.",
|
|
8151
|
+
);
|
|
8152
|
+
} else {
|
|
8153
|
+
thoughtLog(
|
|
8154
|
+
"**JS**: Now looking for JavaScript projects (npm, yarn, pnpm) and files.",
|
|
8155
|
+
);
|
|
8156
|
+
}
|
|
7891
8157
|
}
|
|
7892
|
-
setProjectTypeActivityContext("js", path);
|
|
8158
|
+
setProjectTypeActivityContext(exactAiInventoryType || "js", path);
|
|
7893
8159
|
bomData = await createNodejsBom(path, options);
|
|
7894
8160
|
if (bomData?.bomJson?.components?.length) {
|
|
7895
|
-
|
|
7896
|
-
|
|
7897
|
-
|
|
8161
|
+
if (exactAiInventoryType) {
|
|
8162
|
+
thoughtLog(
|
|
8163
|
+
`I found ${bomData.bomJson.components.length} ${exactAiInventoryType} component(s). Let's keep looking.`,
|
|
8164
|
+
);
|
|
8165
|
+
} else {
|
|
8166
|
+
thoughtLog(
|
|
8167
|
+
`I found ${bomData.bomJson.components.length} npm packages. Let's keep looking.`,
|
|
8168
|
+
);
|
|
8169
|
+
}
|
|
7898
8170
|
if (DEBUG_MODE) {
|
|
7899
8171
|
console.log(
|
|
7900
|
-
`Found ${bomData.bomJson.components.length} npm
|
|
8172
|
+
`Found ${bomData.bomJson.components.length} ${exactAiInventoryType || "npm"} components at ${path}`,
|
|
7901
8173
|
);
|
|
7902
8174
|
}
|
|
7903
8175
|
components = components.concat(bomData.bomJson.components);
|
|
@@ -9238,6 +9510,12 @@ export async function createBom(path, options) {
|
|
|
9238
9510
|
) {
|
|
9239
9511
|
return await createNodejsBom(path, options);
|
|
9240
9512
|
}
|
|
9513
|
+
if (PROJECT_TYPE_ALIASES["mcp"].includes(projectType[0])) {
|
|
9514
|
+
return await createNodejsBom(path, options);
|
|
9515
|
+
}
|
|
9516
|
+
if (PROJECT_TYPE_ALIASES["ai-skill"].includes(projectType[0])) {
|
|
9517
|
+
return await createNodejsBom(path, options);
|
|
9518
|
+
}
|
|
9241
9519
|
if (
|
|
9242
9520
|
PROJECT_TYPE_ALIASES["py"].includes(projectType[0]) ||
|
|
9243
9521
|
projectType?.[0]?.startsWith("python")
|