@cdxgen/cdxgen 13.1.0 → 13.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -7
- package/bin/cdxgen.js +16 -3
- package/bin/evinse.js +2 -0
- package/data/crypto-oid.json +88 -12
- package/data/remediations.json +23 -0
- package/data/rules/ai-bom.yaml +78 -0
- package/data/rules/cargo-rust.yaml +132 -0
- package/data/rules/mcp-servers.yaml +122 -0
- package/lib/audit/targets.js +6 -1
- package/lib/cli/bomAssembly.js +12 -0
- package/lib/cli/index.js +192 -1
- package/lib/cli/jsBom.js +59 -2
- package/lib/cli/jvmBom.js +22 -0
- package/lib/cli/managedBom.js +152 -0
- package/lib/cli/nativeBom.js +798 -1
- package/lib/core/activity.js +120 -2
- package/lib/core/env.js +39 -1
- package/lib/core/userAgent.js +151 -0
- package/lib/ecosystems/cargoCache.js +364 -0
- package/lib/ecosystems/caxa.js +13 -5
- package/lib/ecosystems/cppEvidence.js +40 -2
- package/lib/ecosystems/ecosystems.js +241 -91
- package/lib/ecosystems/golem.js +11 -1
- package/lib/ecosystems/gradleutils.js +143 -2
- package/lib/ecosystems/kosi.js +1256 -0
- package/lib/ecosystems/parsers-crystal.js +179 -0
- package/lib/ecosystems/parsers-dotnet.js +69 -39
- package/lib/ecosystems/parsers-elm.js +816 -0
- package/lib/ecosystems/parsers-gleam.js +1 -1
- package/lib/ecosystems/parsers-julia.js +268 -0
- package/lib/ecosystems/parsers-lua.js +218 -0
- package/lib/ecosystems/parsers-misc.js +172 -0
- package/lib/ecosystems/parsers-nim.js +221 -0
- package/lib/ecosystems/parsers-opam.js +180 -0
- package/lib/ecosystems/parsers-perl.js +277 -0
- package/lib/ecosystems/parsers-python.js +1 -1
- package/lib/ecosystems/parsers-r.js +148 -0
- package/lib/ecosystems/parsers-rebar.js +411 -0
- package/lib/ecosystems/parsers-rust.js +932 -17
- package/lib/ecosystems/parsers-spack.js +204 -0
- package/lib/ecosystems/parsers-stack.js +264 -0
- package/lib/ecosystems/parsers-terraform.js +194 -0
- package/lib/ecosystems/rusi.js +126 -6
- package/lib/evinser/evinser.js +111 -1
- package/lib/helpers/agenticToolutils.js +925 -0
- package/lib/inventory/agentFormulationParser.js +57 -4
- package/lib/inventory/aiCollector.js +504 -10
- package/lib/inventory/aiInventory.js +12 -0
- package/lib/inventory/analyzer.js +488 -30
- package/lib/inventory/astWorker.js +6 -0
- package/lib/inventory/auditCategories.js +8 -0
- package/lib/inventory/cbomutils.js +30 -0
- package/lib/inventory/cdxrs.js +7 -2
- package/lib/inventory/communityAiConfigParser.js +215 -2
- package/lib/inventory/dosai.js +77 -0
- package/lib/inventory/envcontext.js +9 -6
- package/lib/inventory/evidenceUtils.js +13 -0
- package/lib/inventory/fetchBatch.js +74 -2
- package/lib/inventory/fetchRate.js +74 -11
- package/lib/inventory/forgeEnricher.js +3 -2
- package/lib/inventory/jvmToolEnv.js +6 -5
- package/lib/inventory/mcpConfigParser.js +166 -2
- package/lib/inventory/mcpDiscovery.js +11 -0
- package/lib/inventory/mcpServerManifest.js +389 -0
- package/lib/inventory/mcpbBundle.js +419 -0
- package/lib/inventory/plugins.js +3 -0
- package/lib/inventory/protobom.js +54 -88
- package/lib/inventory/protobomLoader.js +4 -0
- package/lib/inventory/rustFormulationParser.js +1 -1
- package/lib/inventory/source.js +10 -4
- package/lib/inventory/stylePackageRefs.js +41 -0
- package/lib/inventory/svelteUtils.js +524 -0
- package/lib/parsers/huggingfaceUtils.js +15 -7
- package/lib/parsers/onnx.js +294 -0
- package/lib/parsers/safetensors.js +114 -0
- package/lib/stages/postgen/introspection/markers.js +29 -6
- package/lib/stages/postgen/postgen.js +169 -1
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +462 -0
- package/package.json +25 -25
- package/types/lib/audit/targets.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts +2 -2
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/cli/jsBom.d.ts +23 -0
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts +33 -0
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts +88 -8
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/activity.d.ts.map +1 -1
- package/types/lib/core/env.d.ts +21 -0
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/userAgent.d.ts +56 -0
- package/types/lib/core/userAgent.d.ts.map +1 -0
- package/types/lib/ecosystems/cargoCache.d.ts +75 -0
- package/types/lib/ecosystems/cargoCache.d.ts.map +1 -0
- package/types/lib/ecosystems/caxa.d.ts.map +1 -1
- package/types/lib/ecosystems/cppEvidence.d.ts +1 -1
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts +15 -1
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +20 -0
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts +73 -0
- package/types/lib/ecosystems/kosi.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-crystal.d.ts +27 -0
- package/types/lib/ecosystems/parsers-crystal.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-dotnet.d.ts +19 -0
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-elm.d.ts +79 -0
- package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-julia.d.ts +57 -0
- package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-lua.d.ts +50 -0
- package/types/lib/ecosystems/parsers-lua.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-misc.d.ts +23 -0
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-nim.d.ts +43 -0
- package/types/lib/ecosystems/parsers-nim.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-opam.d.ts +31 -0
- package/types/lib/ecosystems/parsers-opam.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-perl.d.ts +36 -0
- package/types/lib/ecosystems/parsers-perl.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-r.d.ts +35 -0
- package/types/lib/ecosystems/parsers-r.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-rebar.d.ts +42 -0
- package/types/lib/ecosystems/parsers-rebar.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-rust.d.ts +199 -0
- package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-spack.d.ts +33 -0
- package/types/lib/ecosystems/parsers-spack.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-stack.d.ts +35 -0
- package/types/lib/ecosystems/parsers-stack.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-terraform.d.ts +34 -0
- package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -0
- package/types/lib/ecosystems/pythonutils.d.ts +1 -1
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +29 -0
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/helpers/agenticToolutils.d.ts +87 -0
- package/types/lib/helpers/agenticToolutils.d.ts.map +1 -0
- package/types/lib/inventory/agentFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/aiInventory.d.ts.map +1 -1
- package/types/lib/inventory/analyzer.d.ts.map +1 -1
- package/types/lib/inventory/auditCategories.d.ts +4 -0
- package/types/lib/inventory/auditCategories.d.ts.map +1 -1
- package/types/lib/inventory/cbomutils.d.ts.map +1 -1
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +23 -0
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/fetchBatch.d.ts +34 -0
- package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
- package/types/lib/inventory/fetchRate.d.ts +9 -6
- package/types/lib/inventory/fetchRate.d.ts.map +1 -1
- package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
- package/types/lib/inventory/jvmToolEnv.d.ts +5 -4
- package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/mcpDiscovery.d.ts.map +1 -1
- package/types/lib/inventory/mcpServerManifest.d.ts +19 -0
- package/types/lib/inventory/mcpServerManifest.d.ts.map +1 -0
- package/types/lib/inventory/mcpbBundle.d.ts +19 -0
- package/types/lib/inventory/mcpbBundle.d.ts.map +1 -0
- package/types/lib/inventory/plugins.d.ts.map +1 -1
- package/types/lib/inventory/protobom.d.ts +18 -13
- package/types/lib/inventory/protobom.d.ts.map +1 -1
- package/types/lib/inventory/protobomLoader.d.ts.map +1 -1
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/stylePackageRefs.d.ts +17 -0
- package/types/lib/inventory/stylePackageRefs.d.ts.map +1 -0
- package/types/lib/inventory/svelteUtils.d.ts +124 -0
- package/types/lib/inventory/svelteUtils.d.ts.map +1 -0
- package/types/lib/parsers/huggingfaceUtils.d.ts +8 -1
- package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
- package/types/lib/parsers/onnx.d.ts +28 -0
- package/types/lib/parsers/onnx.d.ts.map +1 -0
- package/types/lib/parsers/safetensors.d.ts +22 -0
- package/types/lib/parsers/safetensors.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/markers.d.ts +3 -2
- package/types/lib/stages/postgen/introspection/markers.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts +33 -0
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
|
@@ -2,18 +2,21 @@
|
|
|
2
2
|
* Per-host rate policy for batched registry fetches.
|
|
3
3
|
*
|
|
4
4
|
* This is the single source of truth for the JS transport's concurrency and
|
|
5
|
-
* minimum-interval decisions.
|
|
5
|
+
* minimum-interval decisions. Every number is shared with the Rust transport
|
|
6
6
|
* (`cdxgen-plugins-bin/thirdparty/cdxrs/src/fetch/rate.rs`); a change to one
|
|
7
7
|
* side must be reflected in the other, and `fetchRate.poku.js` pins them so a
|
|
8
|
-
* drift is visible.
|
|
8
|
+
* drift is visible. crates.io and package.elm-lang.org are routed so that their
|
|
9
|
+
* requests never reach the Rust transport, which makes this table the one that
|
|
10
|
+
* governs them in practice — their rows are mirrored there regardless, because
|
|
11
|
+
* two tables that disagree are a bug waiting for the routing to change.
|
|
9
12
|
*
|
|
10
13
|
* Why a per-host table rather than a single global interval: a single-registry
|
|
11
14
|
* project — which is to say, almost every npm project — has exactly one host,
|
|
12
15
|
* so a conservative per-host cap *is* the global cap. Applying crates.io's
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
16
|
+
* one-per-second policy to every host would cap an npm run at one request a
|
|
17
|
+
* second, orders of magnitude slower than that registry asks for. Hosts that
|
|
18
|
+
* publish a limit get an interval; hosts that do not are bounded by the
|
|
19
|
+
* per-host concurrency cap alone, and by whatever `Retry-After` they send.
|
|
17
20
|
*/
|
|
18
21
|
|
|
19
22
|
import { readEnvironmentVariable } from "../core/activity.js";
|
|
@@ -48,13 +51,18 @@ import { readEnvironmentVariable } from "../core/activity.js";
|
|
|
48
51
|
* Suffix match is used so `index.crates.io` inherits `crates.io`.
|
|
49
52
|
*/
|
|
50
53
|
const HOST_POLICIES = [
|
|
51
|
-
// crates.io's
|
|
54
|
+
// crates.io's crawler policy asks for at most one request per second, made
|
|
55
|
+
// serially. It is a condition of use rather than a throughput hint, so the
|
|
56
|
+
// interval is a full second and the concurrency is 1: with more than one
|
|
57
|
+
// request in flight the gate would still average a second apiece but would
|
|
58
|
+
// deliver them in bursts, which is the thing the policy rules out. The
|
|
59
|
+
// budget is per IP and does not improve with a token.
|
|
52
60
|
{
|
|
53
61
|
host: "crates.io",
|
|
54
|
-
anonInterval:
|
|
55
|
-
anonConcurrency:
|
|
56
|
-
authInterval:
|
|
57
|
-
authConcurrency:
|
|
62
|
+
anonInterval: 1000,
|
|
63
|
+
anonConcurrency: 1,
|
|
64
|
+
authInterval: 1000,
|
|
65
|
+
authConcurrency: 1,
|
|
58
66
|
},
|
|
59
67
|
// GitHub: 60 req/h anonymous, 5000 req/h authenticated. The limit is an
|
|
60
68
|
// hourly budget rather than a rate, so a batch of a few hundred lookups is
|
|
@@ -88,6 +96,61 @@ const HOST_POLICIES = [
|
|
|
88
96
|
authInterval: 250,
|
|
89
97
|
authConcurrency: 4,
|
|
90
98
|
},
|
|
99
|
+
// package.elm-lang.org publishes no limit: its robots.txt carries no
|
|
100
|
+
// `Crawl-delay` and the registry has no terms of use or documented API. What
|
|
101
|
+
// it does have is a single community-run origin rather than a CDN, so it is
|
|
102
|
+
// held to the same gentle allowance as the other unpublished host in this
|
|
103
|
+
// table instead of the CDN default. Like crates.io, its requests are kept off
|
|
104
|
+
// the Rust transport to leave one gate in charge; rate.rs still carries the
|
|
105
|
+
// same row so the two tables state one policy.
|
|
106
|
+
// Maven Central publishes no numeric limit. What it publishes instead is a
|
|
107
|
+
// policy: consumption limits are enforced against aggregate traffic, the
|
|
108
|
+
// thresholds are unpublished and moving downward, and the named offenders are
|
|
109
|
+
// "CI/CD platforms, security scanners, and other services that generate
|
|
110
|
+
// outsized traffic" — which is cdxgen's exact job description. A block is an
|
|
111
|
+
// escalating ban of up to 30 days, not a per-request 429, so the cost of
|
|
112
|
+
// being slightly too fast is losing Maven resolution for a month rather than
|
|
113
|
+
// retrying a second later. That asymmetry, not a published number, is what
|
|
114
|
+
// sets these rows.
|
|
115
|
+
//
|
|
116
|
+
// repo1 is the artifact store: Cloudflare-fronted (`cf-cache-status` on every
|
|
117
|
+
// response), and a POM fetch that hits the edge cache never reaches Sonatype.
|
|
118
|
+
// It gets a real allowance — 100 ms and 8 in flight, so ~10 req/s sustained —
|
|
119
|
+
// which is half the CDN default and orders of magnitude below anything that
|
|
120
|
+
// would show up in the top few percent of Central's traffic.
|
|
121
|
+
{
|
|
122
|
+
host: "repo1.maven.org",
|
|
123
|
+
anonInterval: 100,
|
|
124
|
+
anonConcurrency: 8,
|
|
125
|
+
authInterval: 100,
|
|
126
|
+
authConcurrency: 8,
|
|
127
|
+
},
|
|
128
|
+
// The search API is the opposite: a Solr query against the origin, one
|
|
129
|
+
// request per unknown jar hash, cacheable by nobody. cdxgen already treats it
|
|
130
|
+
// as fragile — a single timeout or 429 disables jar search for the rest of
|
|
131
|
+
// the run — so the gate is a serial 500 ms (~2 req/s). A jar sweep stays
|
|
132
|
+
// usable while staying far below anything worth throttling.
|
|
133
|
+
{
|
|
134
|
+
host: "search.maven.org",
|
|
135
|
+
anonInterval: 500,
|
|
136
|
+
anonConcurrency: 1,
|
|
137
|
+
authInterval: 500,
|
|
138
|
+
authConcurrency: 1,
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
host: "central.sonatype.com",
|
|
142
|
+
anonInterval: 500,
|
|
143
|
+
anonConcurrency: 1,
|
|
144
|
+
authInterval: 500,
|
|
145
|
+
authConcurrency: 1,
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
host: "package.elm-lang.org",
|
|
149
|
+
anonInterval: 250,
|
|
150
|
+
anonConcurrency: 4,
|
|
151
|
+
authInterval: 250,
|
|
152
|
+
authConcurrency: 4,
|
|
153
|
+
},
|
|
91
154
|
];
|
|
92
155
|
|
|
93
156
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cdxgenAgent, readEnvironmentVariable } from "../core/activity.js";
|
|
2
|
+
import { getUserAgent } from "../core/userAgent.js";
|
|
2
3
|
import { getOriginUrl } from "./envcontext.js";
|
|
3
4
|
import { prefetchJson } from "./fetchBatch.js";
|
|
4
5
|
|
|
@@ -123,7 +124,7 @@ export async function enrichFromForge(dir, aiCommits, options = {}) {
|
|
|
123
124
|
const githubHeaders = {
|
|
124
125
|
Accept: "application/vnd.github+json",
|
|
125
126
|
Authorization: `Bearer ${githubToken}`,
|
|
126
|
-
"User-Agent": "
|
|
127
|
+
"User-Agent": getUserAgent("api.github.com"),
|
|
127
128
|
};
|
|
128
129
|
const pullsUrlFor = (commit) =>
|
|
129
130
|
`https://api.github.com/repos/${owner}/${repo}/commits/${commit.hash}/pulls`;
|
|
@@ -214,7 +215,7 @@ export async function enrichFromForge(dir, aiCommits, options = {}) {
|
|
|
214
215
|
const commitsToQuery = aiCommits.slice(0, 5);
|
|
215
216
|
const gitlabHeaders = {
|
|
216
217
|
"PRIVATE-TOKEN": gitlabToken,
|
|
217
|
-
"User-Agent": "
|
|
218
|
+
"User-Agent": getUserAgent("gitlab.com"),
|
|
218
219
|
};
|
|
219
220
|
const mrUrlFor = (commit) =>
|
|
220
221
|
`https://gitlab.com/api/v4/projects/${projectEncoded}/repository/commits/${commit.hash}/merge_requests`;
|
|
@@ -45,10 +45,11 @@ const JVM_BUILD_TOOL_CANDIDATES = ["maven", "gradle", "sbt", "scala"];
|
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Anchored validator for sdkman version identifiers such as `3.9.9`,
|
|
48
|
-
* `4.0.0-rc-5`, `8.14`,
|
|
49
|
-
* shell command by the sdkman installer, so every
|
|
50
|
-
* safe token: it must start with a digit and may
|
|
51
|
-
* letters, dots, and
|
|
48
|
+
* `4.0.0-rc-5`, `8.14`, `21.0.7-tem`, or `26.0.2+1.1-tem`. Versions are
|
|
49
|
+
* interpolated into a shell command by the sdkman installer, so every
|
|
50
|
+
* identifier must be a single safe token: it must start with a digit and may
|
|
51
|
+
* only contain digits, ascii letters, dots, hyphens, and plus signs (`+` has
|
|
52
|
+
* no shell meaning and sdkman uses it in Temurin respin identifiers).
|
|
52
53
|
*
|
|
53
54
|
* Every separator must be followed by at least one alphanumeric character, and
|
|
54
55
|
* a single repeated group covers both the dotted numeric part and the vendor
|
|
@@ -63,7 +64,7 @@ export function isValidSdkmanVersion(version) {
|
|
|
63
64
|
typeof version === "string" &&
|
|
64
65
|
version.length > 0 &&
|
|
65
66
|
version.length <= MAX_VERSION_TOKEN_LENGTH &&
|
|
66
|
-
/^\d+(?:[
|
|
67
|
+
/^\d+(?:[-.+][0-9A-Za-z]+)*$/.test(version)
|
|
67
68
|
);
|
|
68
69
|
}
|
|
69
70
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { basename } from "node:path";
|
|
3
3
|
|
|
4
|
+
import * as toml from "smol-toml";
|
|
5
|
+
|
|
4
6
|
import {
|
|
5
7
|
sanitizeBomPropertyValue,
|
|
6
8
|
sanitizeBomUrl,
|
|
@@ -30,6 +32,16 @@ const MCP_CONFIG_PATTERNS = [
|
|
|
30
32
|
"**/opencode.json",
|
|
31
33
|
"opencode.jsonc",
|
|
32
34
|
"**/opencode.jsonc",
|
|
35
|
+
".gemini/settings.json",
|
|
36
|
+
"**/.gemini/settings.json",
|
|
37
|
+
".windsurf/mcp_config.json",
|
|
38
|
+
"**/.windsurf/mcp_config.json",
|
|
39
|
+
".zed/settings.json",
|
|
40
|
+
"**/.zed/settings.json",
|
|
41
|
+
".roo/mcp.json",
|
|
42
|
+
"**/.roo/mcp.json",
|
|
43
|
+
".codex/config.toml",
|
|
44
|
+
"**/.codex/config.toml",
|
|
33
45
|
];
|
|
34
46
|
|
|
35
47
|
const LOCAL_TUNNEL_HOST_PATTERNS = [
|
|
@@ -92,11 +104,32 @@ function configFormat(filePath) {
|
|
|
92
104
|
) {
|
|
93
105
|
return "cursor";
|
|
94
106
|
}
|
|
107
|
+
if (lowerPath.includes("/.gemini/")) {
|
|
108
|
+
return "gemini";
|
|
109
|
+
}
|
|
110
|
+
if (lowerPath.includes("/.windsurf/")) {
|
|
111
|
+
return "windsurf";
|
|
112
|
+
}
|
|
113
|
+
if (lowerPath.includes("/.zed/")) {
|
|
114
|
+
return "zed";
|
|
115
|
+
}
|
|
116
|
+
if (lowerPath.includes("/.roo/")) {
|
|
117
|
+
return "roo";
|
|
118
|
+
}
|
|
119
|
+
if (
|
|
120
|
+
lowerPath.includes("/.codex/") ||
|
|
121
|
+
lowerPath.endsWith(".codex/config.toml")
|
|
122
|
+
) {
|
|
123
|
+
return "codex";
|
|
124
|
+
}
|
|
95
125
|
return "generic-mcp-json";
|
|
96
126
|
}
|
|
97
127
|
|
|
98
128
|
function syntaxForFile(filePath) {
|
|
99
129
|
const lowerPath = filePath.toLowerCase();
|
|
130
|
+
if (lowerPath.endsWith(".toml")) {
|
|
131
|
+
return "toml";
|
|
132
|
+
}
|
|
100
133
|
return lowerPath.endsWith(".json") || lowerPath.endsWith(".jsonc")
|
|
101
134
|
? "json"
|
|
102
135
|
: "text";
|
|
@@ -105,7 +138,15 @@ function syntaxForFile(filePath) {
|
|
|
105
138
|
function extractServerMaps(config) {
|
|
106
139
|
const serverMaps = [];
|
|
107
140
|
for (const [key, value] of Object.entries(config || {})) {
|
|
108
|
-
if (
|
|
141
|
+
if (
|
|
142
|
+
[
|
|
143
|
+
"mcpServers",
|
|
144
|
+
"context_servers",
|
|
145
|
+
"servers",
|
|
146
|
+
"mcp",
|
|
147
|
+
"mcp_servers",
|
|
148
|
+
].includes(key)
|
|
149
|
+
) {
|
|
109
150
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
110
151
|
serverMaps.push({ configKey: key, servers: Object.entries(value) });
|
|
111
152
|
} else if (Array.isArray(value)) {
|
|
@@ -382,6 +423,106 @@ function trustProfile(officialSdk, exposureType, authPosture) {
|
|
|
382
423
|
return officialSdk ? "official-sdk+unknown" : "review-needed";
|
|
383
424
|
}
|
|
384
425
|
|
|
426
|
+
/**
|
|
427
|
+
* Extract an explicit MCP protocol version literal from a server config.
|
|
428
|
+
*
|
|
429
|
+
* @param {Object} serverConfig Parsed server configuration
|
|
430
|
+
* @returns {string|undefined} protocol version such as `2026-07-28`
|
|
431
|
+
*/
|
|
432
|
+
function protocolVersionForConfig(serverConfig) {
|
|
433
|
+
const candidate =
|
|
434
|
+
serverConfig?.protocolVersion ||
|
|
435
|
+
serverConfig?.protocol_version ||
|
|
436
|
+
serverConfig?.["MCP-Protocol-Version"] ||
|
|
437
|
+
serverConfig?.mcpProtocolVersion;
|
|
438
|
+
if (typeof candidate === "string" && /^\d{4}-\d{2}-\d{2}$/u.test(candidate)) {
|
|
439
|
+
return candidate;
|
|
440
|
+
}
|
|
441
|
+
return undefined;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Detect MCP features deprecated by the 2026-07-28 specification that a config
|
|
446
|
+
* still references.
|
|
447
|
+
*
|
|
448
|
+
* Detection is structural rather than free-text: the transport type, an
|
|
449
|
+
* explicit dynamic client registration endpoint, and declared capability
|
|
450
|
+
* objects are reliable signals, whereas substring scans over a stringified
|
|
451
|
+
* config fire on server names and unrelated blocks. Wire-level method names
|
|
452
|
+
* (`roots/list`, `sampling/createMessage`) are exchanged at runtime and never
|
|
453
|
+
* appear in a client config stanza, so they are intentionally not matched
|
|
454
|
+
* here; source analysis is the right place for those.
|
|
455
|
+
*
|
|
456
|
+
* @param {Object} serverConfig Parsed server configuration
|
|
457
|
+
* @param {string} transport Resolved transport
|
|
458
|
+
* @returns {string[]} sorted list of deprecated feature labels
|
|
459
|
+
*/
|
|
460
|
+
function deprecatedFeaturesForConfig(serverConfig, transport) {
|
|
461
|
+
const found = new Set();
|
|
462
|
+
// Legacy HTTP+SSE transport.
|
|
463
|
+
if (transport === "sse") {
|
|
464
|
+
found.add("sse-transport");
|
|
465
|
+
}
|
|
466
|
+
// Dynamic client registration, detected from an explicit endpoint or flag.
|
|
467
|
+
if (
|
|
468
|
+
serverConfig?.registration_endpoint ||
|
|
469
|
+
serverConfig?.auth?.registration_endpoint ||
|
|
470
|
+
serverConfig?.oauth?.registration_endpoint ||
|
|
471
|
+
serverConfig?.dynamicClientRegistration === true ||
|
|
472
|
+
serverConfig?.supportsDCR === true
|
|
473
|
+
) {
|
|
474
|
+
found.add("dynamic-client-registration");
|
|
475
|
+
}
|
|
476
|
+
// Capability objects the client explicitly declares. A capabilities block is
|
|
477
|
+
// the structured place these appear; a bare declaration of them is the
|
|
478
|
+
// deprecated shape under 2026-07-28.
|
|
479
|
+
const capabilities = serverConfig?.capabilities;
|
|
480
|
+
if (capabilities && typeof capabilities === "object") {
|
|
481
|
+
for (const feature of ["sampling", "logging", "roots"]) {
|
|
482
|
+
if (capabilities[feature] && typeof capabilities[feature] === "object") {
|
|
483
|
+
found.add(feature);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return Array.from(found).sort();
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Detect Client ID Metadata Document (CIMD) usage in a server config.
|
|
492
|
+
*
|
|
493
|
+
* @param {Object} serverConfig Parsed server configuration
|
|
494
|
+
* @returns {boolean} true when CIMD is configured
|
|
495
|
+
*/
|
|
496
|
+
function usesClientIdMetadataDocument(serverConfig) {
|
|
497
|
+
return Boolean(
|
|
498
|
+
serverConfig?.clientIdMetadataDocument ||
|
|
499
|
+
serverConfig?.client_id_metadata_document ||
|
|
500
|
+
serverConfig?.clientIdMetadataDocumentEnabled ||
|
|
501
|
+
serverConfig?.client_id_metadata_document_enabled ||
|
|
502
|
+
serverConfig?.oauth?.clientIdMetadataDocument,
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Detect whether a config binds tokens to a specific resource audience, per the
|
|
508
|
+
* hardened MCP 2026-07-28 authorization model.
|
|
509
|
+
*
|
|
510
|
+
* @param {Object} serverConfig Parsed server configuration
|
|
511
|
+
* @returns {boolean} true when audience-bound resource metadata is present
|
|
512
|
+
*/
|
|
513
|
+
function usesAudienceBoundTokens(serverConfig) {
|
|
514
|
+
const resourceMetadata =
|
|
515
|
+
serverConfig?.resourceMetadata || serverConfig?.oauth?.resourceMetadata;
|
|
516
|
+
if (resourceMetadata && typeof resourceMetadata === "object") {
|
|
517
|
+
return Boolean(
|
|
518
|
+
resourceMetadata.resource &&
|
|
519
|
+
(Array.isArray(resourceMetadata.authorization_servers) ||
|
|
520
|
+
resourceMetadata.authorizationServers),
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
return false;
|
|
524
|
+
}
|
|
525
|
+
|
|
385
526
|
function createServiceFromConfig(
|
|
386
527
|
filePath,
|
|
387
528
|
format,
|
|
@@ -511,6 +652,27 @@ function createServiceFromConfig(
|
|
|
511
652
|
if (authHints.has("oauth")) {
|
|
512
653
|
addUniqueProperty(properties, "cdx:mcp:auth:requiresOAuth", "true");
|
|
513
654
|
}
|
|
655
|
+
if (usesClientIdMetadataDocument(serverConfig)) {
|
|
656
|
+
addUniqueProperty(properties, "cdx:mcp:auth:cimd", "true");
|
|
657
|
+
}
|
|
658
|
+
if (usesAudienceBoundTokens(serverConfig)) {
|
|
659
|
+
addUniqueProperty(properties, "cdx:mcp:auth:audienceBound", "true");
|
|
660
|
+
}
|
|
661
|
+
const protocolVersion = protocolVersionForConfig(serverConfig);
|
|
662
|
+
if (protocolVersion) {
|
|
663
|
+
addUniqueProperty(properties, "cdx:mcp:protocolVersion", protocolVersion);
|
|
664
|
+
}
|
|
665
|
+
const deprecatedFeatures = deprecatedFeaturesForConfig(
|
|
666
|
+
serverConfig,
|
|
667
|
+
transport,
|
|
668
|
+
);
|
|
669
|
+
if (deprecatedFeatures.length) {
|
|
670
|
+
addUniqueProperty(
|
|
671
|
+
properties,
|
|
672
|
+
"cdx:mcp:deprecatedFeatures",
|
|
673
|
+
deprecatedFeatures.join(","),
|
|
674
|
+
);
|
|
675
|
+
}
|
|
514
676
|
if (
|
|
515
677
|
serverConfig?.protectedResourceMetadata ||
|
|
516
678
|
serverConfig?.resourceServerUrl ||
|
|
@@ -650,7 +812,9 @@ export const mcpConfigParser = {
|
|
|
650
812
|
}
|
|
651
813
|
let configJson;
|
|
652
814
|
try {
|
|
653
|
-
configJson =
|
|
815
|
+
configJson = filePath.toLowerCase().endsWith(".toml")
|
|
816
|
+
? toml.parse(raw)
|
|
817
|
+
: parseJsonLike(raw);
|
|
654
818
|
} catch {
|
|
655
819
|
continue;
|
|
656
820
|
}
|
|
@@ -6,6 +6,17 @@ const PROVIDER_TEXT_PATTERNS = [
|
|
|
6
6
|
["deepseek", /\bdeepseek\b/i],
|
|
7
7
|
["ollama", /\bollama\b/i],
|
|
8
8
|
["groq", /\bgroq\b/i],
|
|
9
|
+
["xai", /\bgrok\b|\bx\.ai\b/i],
|
|
10
|
+
["openrouter", /\bopenrouter\b/i],
|
|
11
|
+
// "bedrock" alone is also a WordPress stack and a Minecraft edition, so
|
|
12
|
+
// require AWS context before attributing it to Amazon Bedrock.
|
|
13
|
+
["aws-bedrock", /\b(?:aws|amazon)[\s-]?bedrock\b|\bbedrock[\s-]?runtime\b/i],
|
|
14
|
+
["cerebras", /\bcerebras\b/i],
|
|
15
|
+
// Drop the bare "kimi" alias (a common given name); keep the distinctive
|
|
16
|
+
// "moonshot" vendor token.
|
|
17
|
+
["moonshot", /\bmoonshot\b/i],
|
|
18
|
+
["zhipu", /\bzhipu\b|\bglm-[0-9]/i],
|
|
19
|
+
["qwen", /\bqwen\b|\bdashscope\b/i],
|
|
9
20
|
];
|
|
10
21
|
|
|
11
22
|
const INLINE_CREDENTIAL_PATTERNS = [
|