@cyclonedx/cdxgen 12.3.0 → 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.
Files changed (121) hide show
  1. package/README.md +15 -5
  2. package/bin/audit.js +7 -0
  3. package/bin/cdxgen.js +241 -81
  4. package/bin/repl.js +138 -0
  5. package/data/rules/ai-agent-governance.yaml +249 -0
  6. package/data/rules/dependency-sources.yaml +41 -0
  7. package/data/rules/mcp-servers.yaml +304 -0
  8. package/data/rules/package-integrity.yaml +123 -0
  9. package/lib/audit/index.js +353 -29
  10. package/lib/audit/index.poku.js +247 -7
  11. package/lib/audit/reporters.js +26 -0
  12. package/lib/audit/scoring.js +262 -13
  13. package/lib/audit/scoring.poku.js +179 -0
  14. package/lib/audit/targets.js +391 -2
  15. package/lib/audit/targets.poku.js +416 -3
  16. package/lib/cli/index.js +588 -45
  17. package/lib/cli/index.poku.js +735 -1
  18. package/lib/evinser/evinser.js +8 -5
  19. package/lib/helpers/agentFormulationParser.js +318 -0
  20. package/lib/helpers/aiInventory.js +262 -0
  21. package/lib/helpers/aiInventory.poku.js +111 -0
  22. package/lib/helpers/analyzer.js +1769 -0
  23. package/lib/helpers/analyzer.poku.js +284 -3
  24. package/lib/helpers/auditCategories.js +76 -0
  25. package/lib/helpers/ciParsers/githubActions.js +140 -16
  26. package/lib/helpers/ciParsers/githubActions.poku.js +110 -0
  27. package/lib/helpers/communityAiConfigParser.js +672 -0
  28. package/lib/helpers/communityAiConfigParser.poku.js +63 -0
  29. package/lib/helpers/depsUtils.js +108 -0
  30. package/lib/helpers/depsUtils.poku.js +72 -1
  31. package/lib/helpers/display.js +325 -3
  32. package/lib/helpers/display.poku.js +301 -0
  33. package/lib/helpers/formulationParsers.js +28 -0
  34. package/lib/helpers/formulationParsers.poku.js +504 -1
  35. package/lib/helpers/jsonLike.js +102 -0
  36. package/lib/helpers/jsonLike.poku.js +34 -0
  37. package/lib/helpers/mcp.js +248 -0
  38. package/lib/helpers/mcp.poku.js +101 -0
  39. package/lib/helpers/mcpConfigParser.js +656 -0
  40. package/lib/helpers/mcpConfigParser.poku.js +126 -0
  41. package/lib/helpers/mcpDiscovery.js +84 -0
  42. package/lib/helpers/mcpDiscovery.poku.js +21 -0
  43. package/lib/helpers/protobom.js +3 -3
  44. package/lib/helpers/provenanceUtils.js +29 -4
  45. package/lib/helpers/provenanceUtils.poku.js +29 -3
  46. package/lib/helpers/registryProvenance.js +210 -0
  47. package/lib/helpers/registryProvenance.poku.js +144 -0
  48. package/lib/helpers/rustFormulationParser.js +330 -0
  49. package/lib/helpers/source.js +21 -2
  50. package/lib/helpers/source.poku.js +38 -0
  51. package/lib/helpers/utils.js +1331 -83
  52. package/lib/helpers/utils.poku.js +599 -188
  53. package/lib/helpers/vsixutils.js +12 -4
  54. package/lib/helpers/vsixutils.poku.js +34 -0
  55. package/lib/managers/binary.js +36 -12
  56. package/lib/managers/binary.poku.js +68 -0
  57. package/lib/managers/docker.js +59 -9
  58. package/lib/managers/docker.poku.js +61 -0
  59. package/lib/managers/piptree.js +12 -7
  60. package/lib/managers/piptree.poku.js +44 -0
  61. package/lib/stages/postgen/annotator.js +2 -1
  62. package/lib/stages/postgen/annotator.poku.js +15 -0
  63. package/lib/stages/postgen/auditBom.js +20 -6
  64. package/lib/stages/postgen/auditBom.poku.js +694 -1
  65. package/lib/stages/postgen/postgen.js +262 -11
  66. package/lib/stages/postgen/postgen.poku.js +306 -2
  67. package/lib/stages/postgen/ruleEngine.js +49 -1
  68. package/lib/stages/postgen/spdxConverter.poku.js +70 -0
  69. package/lib/stages/pregen/pregen.js +6 -4
  70. package/package.json +1 -1
  71. package/types/bin/repl.d.ts.map +1 -1
  72. package/types/lib/audit/index.d.ts.map +1 -1
  73. package/types/lib/audit/reporters.d.ts.map +1 -1
  74. package/types/lib/audit/scoring.d.ts.map +1 -1
  75. package/types/lib/audit/targets.d.ts +12 -0
  76. package/types/lib/audit/targets.d.ts.map +1 -1
  77. package/types/lib/cli/index.d.ts +2 -8
  78. package/types/lib/cli/index.d.ts.map +1 -1
  79. package/types/lib/evinser/evinser.d.ts.map +1 -1
  80. package/types/lib/helpers/agentFormulationParser.d.ts +19 -0
  81. package/types/lib/helpers/agentFormulationParser.d.ts.map +1 -0
  82. package/types/lib/helpers/aiInventory.d.ts +23 -0
  83. package/types/lib/helpers/aiInventory.d.ts.map +1 -0
  84. package/types/lib/helpers/analyzer.d.ts +10 -0
  85. package/types/lib/helpers/analyzer.d.ts.map +1 -1
  86. package/types/lib/helpers/auditCategories.d.ts +12 -0
  87. package/types/lib/helpers/auditCategories.d.ts.map +1 -0
  88. package/types/lib/helpers/ciParsers/githubActions.d.ts.map +1 -1
  89. package/types/lib/helpers/communityAiConfigParser.d.ts +29 -0
  90. package/types/lib/helpers/communityAiConfigParser.d.ts.map +1 -0
  91. package/types/lib/helpers/depsUtils.d.ts +8 -0
  92. package/types/lib/helpers/depsUtils.d.ts.map +1 -1
  93. package/types/lib/helpers/display.d.ts +17 -1
  94. package/types/lib/helpers/display.d.ts.map +1 -1
  95. package/types/lib/helpers/formulationParsers.d.ts.map +1 -1
  96. package/types/lib/helpers/jsonLike.d.ts +4 -0
  97. package/types/lib/helpers/jsonLike.d.ts.map +1 -0
  98. package/types/lib/helpers/mcp.d.ts +29 -0
  99. package/types/lib/helpers/mcp.d.ts.map +1 -0
  100. package/types/lib/helpers/mcpConfigParser.d.ts +30 -0
  101. package/types/lib/helpers/mcpConfigParser.d.ts.map +1 -0
  102. package/types/lib/helpers/mcpDiscovery.d.ts +5 -0
  103. package/types/lib/helpers/mcpDiscovery.d.ts.map +1 -0
  104. package/types/lib/helpers/provenanceUtils.d.ts +5 -3
  105. package/types/lib/helpers/provenanceUtils.d.ts.map +1 -1
  106. package/types/lib/helpers/registryProvenance.d.ts +9 -0
  107. package/types/lib/helpers/registryProvenance.d.ts.map +1 -1
  108. package/types/lib/helpers/rustFormulationParser.d.ts +17 -0
  109. package/types/lib/helpers/rustFormulationParser.d.ts.map +1 -0
  110. package/types/lib/helpers/source.d.ts.map +1 -1
  111. package/types/lib/helpers/utils.d.ts +31 -1
  112. package/types/lib/helpers/utils.d.ts.map +1 -1
  113. package/types/lib/helpers/vsixutils.d.ts.map +1 -1
  114. package/types/lib/managers/binary.d.ts.map +1 -1
  115. package/types/lib/managers/docker.d.ts.map +1 -1
  116. package/types/lib/managers/piptree.d.ts.map +1 -1
  117. package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
  118. package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
  119. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  120. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  121. package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
@@ -0,0 +1,248 @@
1
+ import { PackageURL } from "packageurl-js";
2
+
3
+ const OFFICIAL_NPM_NAMESPACE = "@modelcontextprotocol";
4
+ const OFFICIAL_PYPI_PACKAGES = new Set(["mcp"]);
5
+ const OFFICIAL_MAVEN_GROUPS = new Set(["io.modelcontextprotocol.sdk"]);
6
+ const OFFICIAL_NUGET_PREFIXES = ["modelcontextprotocol"];
7
+ const OFFICIAL_GOLANG_PREFIX = "github.com/modelcontextprotocol/";
8
+ const OFFICIAL_CARGO_PACKAGES = new Set(["rmcp", "rmcp-macros"]);
9
+ const KNOWN_MCP_INTEGRATION_GROUPS = new Set(["org.springframework.ai"]);
10
+
11
+ const MCP_ROLE_HINTS = [
12
+ ["server-sdk", /(?:^|[-/.])server(?:$|[-/.])/i],
13
+ ["client-sdk", /(?:^|[-/.])client(?:$|[-/.])/i],
14
+ [
15
+ "transport-sdk",
16
+ /(?:^|[-/.])(express|node|webflux|webmvc|aspnetcore)(?:$|[-/.])/i,
17
+ ],
18
+ ];
19
+
20
+ function lower(value) {
21
+ return typeof value === "string" ? value.toLowerCase() : "";
22
+ }
23
+
24
+ function rootImportName(source) {
25
+ if (typeof source !== "string" || !source.length || source.startsWith(".")) {
26
+ return "";
27
+ }
28
+ if (source.startsWith("@")) {
29
+ return source.split("/").slice(0, 2).join("/");
30
+ }
31
+ return source.split("/")[0];
32
+ }
33
+
34
+ function normalizeReference(ref) {
35
+ if (typeof ref === "string") {
36
+ const importName = rootImportName(ref);
37
+ const importParts = importName.startsWith("@")
38
+ ? importName.split("/")
39
+ : ["", importName];
40
+ return {
41
+ ecosystem: "npm",
42
+ group: lower(importParts[0]),
43
+ name: lower(importParts[1]),
44
+ source: ref,
45
+ };
46
+ }
47
+ if (!ref || typeof ref !== "object") {
48
+ return { ecosystem: "", group: "", name: "", source: "" };
49
+ }
50
+ if (typeof ref.purl === "string" && ref.purl.length) {
51
+ try {
52
+ const purl = PackageURL.fromString(ref.purl);
53
+ return {
54
+ ecosystem: lower(purl.type),
55
+ group: lower(purl.namespace),
56
+ name: lower(purl.name),
57
+ source: ref.purl,
58
+ };
59
+ } catch {
60
+ // continue with object fallback
61
+ }
62
+ }
63
+ return {
64
+ ecosystem: lower(ref.ecosystem || ref.type),
65
+ group: lower(ref.group),
66
+ name: lower(ref.name),
67
+ source: lower(ref.source),
68
+ };
69
+ }
70
+
71
+ function inferRole(name, group) {
72
+ const combined = `${group}/${name}`;
73
+ for (const [role, pattern] of MCP_ROLE_HINTS) {
74
+ if (pattern.test(combined)) {
75
+ return role;
76
+ }
77
+ }
78
+ if (name.includes("sdk") || group.includes("modelcontextprotocol")) {
79
+ return "sdk";
80
+ }
81
+ if (name.includes("integration") || group.includes("springframework")) {
82
+ return "integration";
83
+ }
84
+ return "sdk";
85
+ }
86
+
87
+ /**
88
+ * Classify a package/component/import reference as MCP-related.
89
+ *
90
+ * @param {Object|string} ref Package/component reference or import source
91
+ * @returns {{
92
+ * isMcp: boolean,
93
+ * isOfficial: boolean,
94
+ * isKnownIntegration: boolean,
95
+ * role: string | undefined,
96
+ * catalogSource: string | undefined,
97
+ * packageName: string
98
+ * }}
99
+ */
100
+ export function classifyMcpReference(ref) {
101
+ const normalized = normalizeReference(ref);
102
+ const ecosystem = normalized.ecosystem;
103
+ const group = normalized.group;
104
+ const name = normalized.name;
105
+ const packageName = [group, name].filter(Boolean).join("/") || name;
106
+ if (!name && !group) {
107
+ return {
108
+ isMcp: false,
109
+ isOfficial: false,
110
+ isKnownIntegration: false,
111
+ role: undefined,
112
+ catalogSource: undefined,
113
+ packageName,
114
+ };
115
+ }
116
+
117
+ let isMcp = false;
118
+ let isOfficial = false;
119
+ let isKnownIntegration = false;
120
+ let catalogSource;
121
+
122
+ if (ecosystem === "npm" && group === OFFICIAL_NPM_NAMESPACE) {
123
+ isMcp = true;
124
+ isOfficial = true;
125
+ catalogSource = "official-sdk";
126
+ } else if (ecosystem === "pypi" && OFFICIAL_PYPI_PACKAGES.has(name)) {
127
+ isMcp = true;
128
+ isOfficial = true;
129
+ catalogSource = "official-sdk";
130
+ } else if (ecosystem === "maven" && OFFICIAL_MAVEN_GROUPS.has(group)) {
131
+ isMcp = true;
132
+ isOfficial = true;
133
+ catalogSource = "official-sdk";
134
+ } else if (
135
+ ecosystem === "nuget" &&
136
+ OFFICIAL_NUGET_PREFIXES.some((prefix) => name.startsWith(prefix))
137
+ ) {
138
+ isMcp = true;
139
+ isOfficial = true;
140
+ catalogSource = "official-sdk";
141
+ } else if (
142
+ ecosystem === "golang" &&
143
+ `${group}/${name}`.startsWith(OFFICIAL_GOLANG_PREFIX)
144
+ ) {
145
+ isMcp = true;
146
+ isOfficial = true;
147
+ catalogSource = "official-sdk";
148
+ } else if (ecosystem === "cargo" && OFFICIAL_CARGO_PACKAGES.has(name)) {
149
+ isMcp = true;
150
+ isOfficial = true;
151
+ catalogSource = "official-sdk";
152
+ } else if (
153
+ ecosystem === "maven" &&
154
+ KNOWN_MCP_INTEGRATION_GROUPS.has(group) &&
155
+ name.includes("mcp")
156
+ ) {
157
+ isMcp = true;
158
+ isKnownIntegration = true;
159
+ catalogSource = "known-integration";
160
+ } else if (
161
+ packageName.includes("modelcontextprotocol") ||
162
+ packageName.includes("mcp")
163
+ ) {
164
+ isMcp = true;
165
+ catalogSource = "heuristic";
166
+ }
167
+
168
+ return {
169
+ isMcp,
170
+ isOfficial,
171
+ isKnownIntegration,
172
+ role: isMcp ? inferRole(name, group) : undefined,
173
+ catalogSource,
174
+ packageName,
175
+ };
176
+ }
177
+
178
+ function pushUniqueProperty(properties, name, value) {
179
+ if (value === undefined || value === null || value === "") {
180
+ return;
181
+ }
182
+ if (properties.some((prop) => prop.name === name && prop.value === value)) {
183
+ return;
184
+ }
185
+ properties.push({ name, value });
186
+ }
187
+
188
+ /**
189
+ * Add MCP catalog metadata to a CycloneDX component.
190
+ *
191
+ * @param {Object} component CycloneDX component
192
+ * @returns {Object} Same component reference
193
+ */
194
+ export function enrichComponentWithMcpMetadata(component) {
195
+ if (!component || typeof component !== "object") {
196
+ return component;
197
+ }
198
+ const classification = classifyMcpReference(component);
199
+ if (!classification.isMcp) {
200
+ return component;
201
+ }
202
+ const tags = new Set(component.tags || []);
203
+ tags.add("mcp");
204
+ tags.add("mcp-sdk");
205
+ if (classification.role === "server-sdk") {
206
+ tags.add("mcp-server");
207
+ }
208
+ if (classification.role === "client-sdk") {
209
+ tags.add("mcp-client");
210
+ }
211
+ if (classification.isOfficial) {
212
+ tags.add("official-mcp-sdk");
213
+ tags.add("trusted-source");
214
+ }
215
+ if (classification.isKnownIntegration) {
216
+ tags.add("known-mcp-integration");
217
+ }
218
+ component.tags = Array.from(tags).sort();
219
+ component.properties = component.properties || [];
220
+ pushUniqueProperty(component.properties, "cdx:mcp:package", "true");
221
+ pushUniqueProperty(
222
+ component.properties,
223
+ "cdx:mcp:official",
224
+ classification.isOfficial ? "true" : "false",
225
+ );
226
+ if (classification.role) {
227
+ pushUniqueProperty(
228
+ component.properties,
229
+ "cdx:mcp:role",
230
+ classification.role,
231
+ );
232
+ }
233
+ if (classification.catalogSource) {
234
+ pushUniqueProperty(
235
+ component.properties,
236
+ "cdx:mcp:catalogSource",
237
+ classification.catalogSource,
238
+ );
239
+ }
240
+ if (classification.packageName) {
241
+ pushUniqueProperty(
242
+ component.properties,
243
+ "cdx:mcp:packageName",
244
+ classification.packageName,
245
+ );
246
+ }
247
+ return component;
248
+ }
@@ -0,0 +1,101 @@
1
+ import { assert, describe, it } from "poku";
2
+
3
+ import { classifyMcpReference, enrichComponentWithMcpMetadata } from "./mcp.js";
4
+
5
+ describe("classifyMcpReference()", () => {
6
+ it("detects official MCP SDK packages", () => {
7
+ const classification = classifyMcpReference({
8
+ purl: "pkg:npm/%40modelcontextprotocol/server@2.0.0-alpha.0",
9
+ });
10
+ assert.strictEqual(classification.isMcp, true);
11
+ assert.strictEqual(classification.isOfficial, true);
12
+ assert.strictEqual(classification.role, "server-sdk");
13
+ });
14
+
15
+ it("detects non-official MCP-like packages heuristically", () => {
16
+ const classification = classifyMcpReference({
17
+ purl: "pkg:npm/%40acme/mcp-server@1.0.0",
18
+ });
19
+ assert.strictEqual(classification.isMcp, true);
20
+ assert.strictEqual(classification.isOfficial, false);
21
+ assert.strictEqual(classification.catalogSource, "heuristic");
22
+ });
23
+
24
+ it("classifies official npm import strings using the package root", () => {
25
+ const classification = classifyMcpReference(
26
+ "@modelcontextprotocol/client/stdio",
27
+ );
28
+ assert.strictEqual(classification.isMcp, true);
29
+ assert.strictEqual(classification.isOfficial, true);
30
+ assert.strictEqual(classification.role, "client-sdk");
31
+ assert.strictEqual(
32
+ classification.packageName,
33
+ "@modelcontextprotocol/client",
34
+ );
35
+ });
36
+
37
+ it("detects known maven integrations separately from official SDKs", () => {
38
+ const classification = classifyMcpReference({
39
+ purl: "pkg:maven/org.springframework.ai/spring-ai-mcp@1.0.0",
40
+ });
41
+ assert.strictEqual(classification.isMcp, true);
42
+ assert.strictEqual(classification.isOfficial, false);
43
+ assert.strictEqual(classification.isKnownIntegration, true);
44
+ assert.strictEqual(classification.catalogSource, "known-integration");
45
+ });
46
+ });
47
+
48
+ describe("enrichComponentWithMcpMetadata()", () => {
49
+ it("adds MCP properties and tags to official SDK components", () => {
50
+ const component = enrichComponentWithMcpMetadata({
51
+ type: "library",
52
+ name: "server",
53
+ group: "@modelcontextprotocol",
54
+ purl: "pkg:npm/%40modelcontextprotocol/server@2.0.0-alpha.0",
55
+ version: "2.0.0-alpha.0",
56
+ });
57
+ assert.ok(component.tags.includes("mcp"));
58
+ assert.ok(component.tags.includes("official-mcp-sdk"));
59
+ assert.ok(
60
+ component.properties.some(
61
+ (prop) => prop.name === "cdx:mcp:official" && prop.value === "true",
62
+ ),
63
+ );
64
+ });
65
+
66
+ it("is idempotent when metadata enrichment is applied multiple times", () => {
67
+ const component = {
68
+ type: "library",
69
+ name: "client",
70
+ group: "@modelcontextprotocol",
71
+ purl: "pkg:npm/%40modelcontextprotocol/client@1.0.0",
72
+ properties: [],
73
+ tags: [],
74
+ version: "1.0.0",
75
+ };
76
+ enrichComponentWithMcpMetadata(component);
77
+ enrichComponentWithMcpMetadata(component);
78
+ assert.strictEqual(component.tags.filter((tag) => tag === "mcp").length, 1);
79
+ assert.strictEqual(
80
+ component.properties.filter((prop) => prop.name === "cdx:mcp:package")
81
+ .length,
82
+ 1,
83
+ );
84
+ });
85
+
86
+ it("leaves non-MCP components unchanged", () => {
87
+ const component = {
88
+ name: "lodash",
89
+ purl: "pkg:npm/lodash@4.17.21",
90
+ properties: [{ name: "existing", value: "true" }],
91
+ tags: ["utility"],
92
+ type: "library",
93
+ version: "4.17.21",
94
+ };
95
+ const enriched = enrichComponentWithMcpMetadata(component);
96
+ assert.deepStrictEqual(enriched.properties, [
97
+ { name: "existing", value: "true" },
98
+ ]);
99
+ assert.deepStrictEqual(enriched.tags, ["utility"]);
100
+ });
101
+ });