io.github.moelayyan90/xguard
version 0.6.2
- scanned 2026-10-16
- source github.com/moelayyan90/XGuard
- commit 91c23ae6c34b
- transport streamable-http
- files read 128
- methodology 0.2.0
- ruleset 0.1.1
No tag matching version 0.6.2 was found in this repository, so the scan read the default branch instead. The findings below describe that code, not necessarily the released version.
6measured on 99% of this axis
12 vendored or minified paths were excluded before scanning; machine-generated bundles are not the server's own code; 1 file could not be parsed and was not scanned
74measured on 80% of this axis
57
100measured on 50% of this axis
22measured on 80% of this axis
The evidence
Every deduction below names the artefact that produced it. Nothing on this page was executed: the source was read, never run, and no running server was contacted.
Code Safety
Showing the 50 most severe of 65 findings on this axis. The score was computed over all of them — findings beyond the fourth of a severity still deduct at a tenth of their weight — so the remainder is omitted from this page, not from the arithmetic.
- apps/relay/src/core/network-policy.js:39mcp-js-ssrf-metadata-endpoint (critical/medium)
The cloud instance-metadata endpoint is contacted directly — the standard path from SSRF to credential theft on a hosted server.
if (!host || host.length > 253 || host === "localhost" || !host.includes(".")) return false; if (host.endsWith(".local") || host.endsWith(".internal") || host.endsWith(".localhost") || host.endsWith(".home") || host.endsWith(".lan")) return false; if (host === "metadata.google.internal" || host === "metadata.azure.internal" || host.endsWith(".xguardgate.com") || host === "xguardgate.com") return false; if (isPrivateIpv4(host) || isPrivateIpv6(host)) return false; return true; - apps/relay/src/execution-first-test.js:143mcp-js-dynamic-code-execution (critical/medium)
Caller-influenced input reaches an evaluator. eval, the Function constructor and vm.runInNewContext all execute their argument; none of them sandbox anything by default, vm least of all despite the name.
const html = await response.text(); const { document } = parseHTML(html); assert.ok(document.querySelector('link[rel="canonical"]')); for (const script of document.querySelectorAll("script")) { assert.doesNotThrow(() => new Function(script.textContent)); assert.ok(script.getAttribute("nonce")); } assert.doesNotMatch(html, /localStorage|sessionStorage|\.innerHTML\s*=/); } - apps/relay/src/paid-agent-entry-test.js:216mcp-js-ssrf-metadata-endpoint (critical/medium)
The cloud instance-metadata endpoint is contacted directly — the standard path from SSRF to credential theft on a hosted server.
"https://127.0.0.1/", "https://10.0.0.1/", "https://169.254.169.254/latest/meta-data/", "https://[::1]/", "https://[::ffff:127.0.0.1]/", - apps/relay/src/paid-agent-entry-test.js:219mcp-js-ssrf-metadata-endpoint (critical/medium)
The cloud instance-metadata endpoint is contacted directly — the standard path from SSRF to credential theft on a hosted server.
"https://[::1]/", "https://[::ffff:127.0.0.1]/", "https://metadata.google.internal/", "https://api.xguardgate.com/identity", "https://localhost/", - apps/relay/src/paid-agent-entry-test.js:313mcp-js-ssrf-metadata-endpoint (critical/medium)
The cloud instance-metadata endpoint is contacted directly — the standard path from SSRF to credential theft on a hosted server.
method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ url: "https://169.254.169.254/latest/meta-data/", testnet: true }), }), env, {}); assert.equal(response.status, 403); - apps/relay/src/seller-entry-test.js:274mcp-js-ssrf-metadata-endpoint (critical/medium)
The cloud instance-metadata endpoint is contacted directly — the standard path from SSRF to credential theft on a hosted server.
test('Seller origin and path policy reject private destinations, encoded traversal and query credentials', () => { for (const target of ['https://127.0.0.1/', 'https://169.254.169.254/', 'https://api.xguardgate.com/', 'https://example.com/?key=secret', 'https://user:pass@example.com/']) assert.throws(() => safeSellerBase(target)); for (const path of ['/../private', '/%252e%252e/private', '//other.com/', '/a%2fb', '/a\\b']) assert.throws(() => safeSellerPath(path)); }); - apps/relay/src/a2a-entry.js:250mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
for (const name of ["payment-required", "payment-response", "x-xguard-quote", "x-xguard-payment-environment", "x-xguard-payment-rail", "x-xguard-payment-identifier", "x-xguard-replay", "x-xguard-proof", "x-xguard-receipt"]) { const header = paid.headers.get(name); if (header) exposed[name] = header; } const state = paid.status === 402 ? "INPUT_REQUIRED" : paid.ok ? "COMPLETED" : "FAILED"; - apps/relay/src/core/payment-context.js:33mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
for (const key of ["paymentPayload", "payment_payload", "payment", "payload"]) { if (typeof body[key] !== "string") continue; try { body[key] = decodePaymentSignatureHeader(body[key]); } catch { return block("invalid_payment_payload", { field: key, expected: "full payment object or base64-encoded x402 Payment-Signature payload" }); } normalizations.push("encoded_payment_envelope"); - apps/relay/src/outcome-catalog.js:181mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
for (const key of ["capability", "sources", "urls", "url", "html", "action", "limit", "max_age_seconds", "query", "testnet"]) { if (raw[key] !== undefined && inside[key] !== undefined && !same(raw[key], inside[key])) return invalid("ambiguous_intent", `Conflicting ${key} values.`); if (raw[key] !== undefined) merged[key] = raw[key]; } if (typeof raw.intent === "string") { - apps/relay/src/paid-agent-entry.js:712mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
if (!Object.hasOwn(raw, key)) continue; if (Object.hasOwn(source, key) && source[key] !== raw[key]) issues.push({ path: key, code: "conflicting_values", message: `The outer ${key} conflicts with its input envelope.` }); else source[key] = raw[key]; } } - apps/relay/src/paid-agent-entry.js:2415mcp-js-prototype-pollution-merge-loop (medium/medium)
Keys from a caller-supplied object are copied onto a target without excluding __proto__ / constructor / prototype. A tool argument carrying __proto__ then mutates Object.prototype for the whole process, which can turn an unrelated later property read into attacker-controlled data.
for (const name of ["payment-response", "x-xguard-request-id", "x-xguard-payment-identifier", "x-xguard-replay", "x-xguard-proof", "x-xguard-receipt"]) { const value = paid.headers.get(name); if (value) exposed[name] = value; } return mcpTransportResponse(mcpEnvelope(message.id, value, exposed), message); - apps/edge-gate/src/index.js:175mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
if (cached && cached.expiresAt > now) return cached.rules; const response = await fetch(target, { headers: { accept: "application/json" }, redirect: "error", - apps/edge-gate/src/index.js:198mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
async function proxyToOrigin(request, env) { const target = originUrlFor(request.url, env); const upstream = await fetch(new Request(target, request)); const headers = new Headers(upstream.headers); headers.set("x-xguard-edge-gate", "enforced"); - apps/relay/src/action-rail.js:370mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
let upstream; try { upstream = await fetch(String(permit.target), { method: String(permit.method || "POST").toUpperCase(), headers, - apps/relay/src/agent-token-usage.js:28mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
// Workers supports manual/follow, not redirect:error. Never forward the key // to a redirected origin; non-2xx replies below fail authentication closed. response = await fetch(url.href, { method: "GET", redirect: "manual", signal: AbortSignal.timeout(5000), headers: { authorization: `Bearer ${key}`, accept: "application/json" } }); // Never treat arbitrary HTTP 200 pages or an incomplete reply as authentication. const parsed = await readPublicJson(response, MAX_USAGE_BYTES); - apps/relay/src/core/network-policy.js:61mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
endpoint.searchParams.set("name", hostname); endpoint.searchParams.set("type", type); const response = await fetch(endpoint, { headers: { accept: resolver.accept }, signal: AbortSignal.timeout(Math.min(8000, Math.max(1000, timeoutMs))), redirect: "follow" }); if (!response.ok || !resolver.hosts.has(new URL(response.url || endpoint).hostname)) throw new Error("dns_resolver_unavailable"); const candidate = await response.json().catch(() => null); - apps/relay/src/core/reconcile-payment.js:19mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
try { await Promise.any(reconciliationRpcUrls(env).map(async endpoint => { const response = await fetch(endpoint, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_chainId", params: [] }), redirect: "manual", signal: AbortSignal.timeout(2000) }); if (!response.ok || (await response.json()).result !== "0x2105") throw new Error("rpc_chain_unavailable"); })); - apps/relay/src/outcome-http-test.js:100mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
validAfter: "0", validBefore: String(Math.floor(Date.now() / 1000) + 300), nonce: `0x${(++nonce).toString(16).padStart(64, "0")}` } }, extensions: challenge.extensions }); const buy = async body => { const challengeResponse = await request("/v1/execute", body); const challenge = await challengeResponse.json(); assert.equal(challengeResponse.status, 402, JSON.stringify(challenge)); const quote = challengeResponse.headers.get("x-xguard-quote"); - apps/relay/src/outcome-http-test.js:105mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
const proof = payload(challenge); const headers = { "x-xguard-quote": quote, "payment-signature": encodePaymentSignatureHeader(proof) }; return { response: await request("/v1/execute", body, headers), quote, proof, headers, challenge }; }; return { env, counts, request, payload, buy, base, logs }; - apps/relay/src/paid-agent-entry.js:928mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
if (!resolved.ok) throw Object.assign(new Error(resolved.code), { code: resolved.code }); try { response = await fetch(current, { method: input.method, headers: { accept: "application/json,text/plain,text/html,application/xml,text/xml;q=0.9,*/*;q=0.1", "user-agent": `XGuard-Web-Fetch/${VERSION}` }, - apps/relay/src/rail.js:285mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
try { const upstream = await fetch(String(permit.target), { method: String(permit.method || "POST").toUpperCase(), headers, - apps/relay/src/seller-entry-test.js:42mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
} const request = (env, path, init = {}) => app.fetch(new Request(`https://api.xguardgate.com${path}`, init), env, {}); const post = (env, path, body, token) => request(env, path, { method: 'POST', headers: { 'content-type': 'application/json', ...(token ? { authorization: `Bearer ${token}` } : {}) }, body: JSON.stringify(body) }); async function onboard(env, changes = {}) { const registered = await post(env, '/v1/sellers', { name: 'Fixture seller' }); assert.equal(registered.status, 201, await registered.clone().text()); - apps/relay/src/seller-entry-test.js:70mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
} async function challenge(env, endpoint, init = {}) { const response = await request(env, new URL(endpoint).pathname, init); assert.equal(response.status, 402, await response.clone().text()); return { body: await response.json(), quote: response.headers.get('x-xguard-quote') }; } - apps/relay/src/seller-entry-test.js:94mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
const stored = await sellerCall(env, '/seller/service-get', { service_id: seller.service.service_id }); assert.ok(stored.service.secret_envelope); assert.ok(!JSON.stringify(stored).includes('fixture-upstream-secret')); assert.equal((await request(env, new URL(seller.service.endpoint).pathname)).status, 503); assert.equal((await request(env, '/v1/sellers/dashboard')).status, 401); assert.equal((await request(env, '/internal/revenue-funnel')).status, 401); - apps/relay/src/seller-entry-test.js:95mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
assert.ok(stored.service.secret_envelope); assert.ok(!JSON.stringify(stored).includes('fixture-upstream-secret')); assert.equal((await request(env, new URL(seller.service.endpoint).pathname)).status, 503); assert.equal((await request(env, '/v1/sellers/dashboard')).status, 401); assert.equal((await request(env, '/internal/revenue-funnel')).status, 401); const catalog = await (await request(env, '/v1/marketplace/services')).json(); - apps/relay/src/seller-entry-test.js:96mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
assert.equal((await request(env, new URL(seller.service.endpoint).pathname)).status, 503); assert.equal((await request(env, '/v1/sellers/dashboard')).status, 401); assert.equal((await request(env, '/internal/revenue-funnel')).status, 401); const catalog = await (await request(env, '/v1/marketplace/services')).json(); assert.equal(catalog.services.length, 1); assert.equal(catalog.services[0].service_id, 'feed-digest'); - apps/relay/src/seller-entry-test.js:97mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
assert.equal((await request(env, '/v1/sellers/dashboard')).status, 401); assert.equal((await request(env, '/internal/revenue-funnel')).status, 401); const catalog = await (await request(env, '/v1/marketplace/services')).json(); assert.equal(catalog.services.length, 1); assert.equal(catalog.services[0].service_id, 'feed-digest'); }); - apps/relay/src/seller-entry-test.js:106mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
assert.equal(calls.upstream, 0); assert.equal(quote.body.extensions.xguard.allocation.platform_fee_atomic, '3000'); const path = new URL(seller.service.endpoint).pathname, headers = paidHeaders(quote); const first = await request(env, path, { headers }); assert.equal(first.status, 200, await first.clone().text()); assert.deepEqual(await first.json(), { data: 'useful fixture result' }); assert.ok(first.headers.get('x-xguard-receipt')); assert.ok(first.headers.get('x-xguard-proof')); - apps/relay/src/seller-entry-test.js:109mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
assert.deepEqual(await first.json(), { data: 'useful fixture result' }); assert.ok(first.headers.get('x-xguard-receipt')); assert.ok(first.headers.get('x-xguard-proof')); const replay = await request(env, path, { headers }); assert.equal(replay.headers.get('x-xguard-replay'), 'true'); assert.deepEqual({ v: calls.verify, s: calls.settle, u: calls.upstream }, { v: 1, s: 1, u: 1 }); const verification = await post(env, '/v1/proofs/verify', { proof: first.headers.get('x-xguard-proof') }); - apps/relay/src/seller-entry-test.js:119mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
const after = await sellerCall(env, '/seller/summary'); assert.equal(after.classes.SYNTHETIC.seller_payout_atomic, '0'); assert.equal(after.classes.SYNTHETIC.same_owner_retained_atomic, '97000'); assert.equal(after.classes.SYNTHETIC.funnel.xguard_fee_earned, 1); const recovered = await request(env, `/v1/marketplace/results/${quote.body.extensions.xguard.paymentIdentifier}`, { headers: { 'x-xguard-quote': quote.quote } }); assert.equal(recovered.status, 200); assert.equal(recovered.headers.get('x-xguard-replay'), 'true'); assert.equal(calls.upstream, 1); assert.equal((await request(env, `/v1/marketplace/results/${quote.body.extensions.xguard.paymentIdentifier}`)).status, 403); - apps/relay/src/seller-entry-test.js:121mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
const recovered = await request(env, `/v1/marketplace/results/${quote.body.extensions.xguard.paymentIdentifier}`, { headers: { 'x-xguard-quote': quote.quote } }); assert.equal(recovered.status, 200); assert.equal(recovered.headers.get('x-xguard-replay'), 'true'); assert.equal(calls.upstream, 1); assert.equal((await request(env, `/v1/marketplace/results/${quote.body.extensions.xguard.paymentIdentifier}`)).status, 403); }); - apps/relay/src/seller-entry-test.js:127mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
const env = environment(), calls = network(t, { valid: false }), seller = await onboard(env); const q = await challenge(env, seller.service.endpoint); assert.equal((await request(env, new URL(seller.service.endpoint).pathname, { headers: paidHeaders(q) })).status, 402); assert.equal(calls.settle, 0); assert.equal(calls.upstream, 0); const summary = await sellerCall(env, '/seller/summary'); - apps/relay/src/seller-entry-test.js:235mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
const env = environment(), calls = network(t); const seller = await onboard(env); const path = new URL(seller.service.endpoint).pathname; const init = { method: 'POST', headers: { 'content-type': 'application/json', 'idempotency-key': 'fixture-write-1' }, body: '{"job":"one"}' }; assert.equal((await request(env, path, { method: 'POST' })).status, 400); const q = await challenge(env, seller.service.endpoint, init); const paid = { ...init, headers: { ...init.headers, ...paidHeaders(q) } }; - apps/relay/src/seller-entry-test.js:238mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
const q = await challenge(env, seller.service.endpoint, init); const paid = { ...init, headers: { ...init.headers, ...paidHeaders(q) } }; assert.equal((await request(env, path, { ...paid, body: '{"job":"other"}' })).status, 400); assert.equal((await request(env, path, paid)).status, 200); const q2 = await challenge(env, seller.service.endpoint, init); - apps/relay/src/seller-entry-test.js:239mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
const paid = { ...init, headers: { ...init.headers, ...paidHeaders(q) } }; assert.equal((await request(env, path, { ...paid, body: '{"job":"other"}' })).status, 400); assert.equal((await request(env, path, paid)).status, 200); const q2 = await challenge(env, seller.service.endpoint, init); const second = await request(env, path, { ...init, headers: { ...init.headers, ...paidHeaders(q2, { nonce: '4' }) } }); - apps/relay/src/seller-entry-test.js:241mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
assert.equal((await request(env, path, paid)).status, 200); const q2 = await challenge(env, seller.service.endpoint, init); const second = await request(env, path, { ...init, headers: { ...init.headers, ...paidHeaders(q2, { nonce: '4' }) } }); assert.equal(second.status, 409); assert.equal(calls.settle, 1); assert.equal(calls.upstream, 1); }); - apps/relay/src/seller-entry-test.js:251mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
const init = { method: 'POST', headers: { 'content-type': 'application/json', 'idempotency-key': 'predictable-order-key' }, body: '{"job":"one"}' }; const forgedQuote = await challenge(env, seller.service.endpoint, init); const forged = await request(env, path, { ...init, headers: { ...init.headers, ...paidHeaders(forgedQuote) } }); assert.equal(forged.status, 402); assert.equal(calls.settle, 0); assert.equal(calls.upstream, 0); valid = true; - apps/relay/src/seller-entry-test.js:256mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
const buyerQuote = await challenge(env, seller.service.endpoint, init); const buyerRequest = { ...init, headers: { ...init.headers, ...paidHeaders(buyerQuote, { nonce: '4' }) } }; const paid = await request(env, path, buyerRequest); assert.equal(paid.status, 200, await paid.clone().text()); const replay = await request(env, path, buyerRequest); - apps/relay/src/seller-entry-test.js:258mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
const paid = await request(env, path, buyerRequest); assert.equal(paid.status, 200, await paid.clone().text()); const replay = await request(env, path, buyerRequest); assert.equal(replay.status, 200); assert.equal(replay.headers.get('x-xguard-replay'), 'true'); assert.deepEqual({ verify: calls.verify, settle: calls.settle, upstream: calls.upstream }, { verify: 2, settle: 1, upstream: 1 }); - apps/relay/src/seller-entry-test.js:266mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
const env = environment(), calls = network(t, { failExecution: true }); const seller = await onboard(env); const q = await challenge(env, seller.service.endpoint), headers = paidHeaders(q), path = new URL(seller.service.endpoint).pathname; const failed = await request(env, path, { headers }); assert.equal(failed.status, 502); assert.equal((await failed.json()).error.details.new_payment_required, false); assert.equal((await request(env, path, { headers })).status, 502); - apps/relay/src/seller-entry-test.js:268mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
const failed = await request(env, path, { headers }); assert.equal(failed.status, 502); assert.equal((await failed.json()).error.details.new_payment_required, false); assert.equal((await request(env, path, { headers })).status, 502); assert.equal(calls.settle, 1); assert.equal(calls.upstream, 1); const revenue = await sellerCall(env, '/seller/summary'); assert.equal(revenue.classes.SYNTHETIC.gross_atomic, '100000'); assert.equal(revenue.classes.SYNTHETIC.platform_fee_atomic, '0'); assert.equal(revenue.classes.SYNTHETIC.seller_credit_atomic, '0'); - apps/relay/src/seller-entry-test.js:281mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
const env = environment(), secret = 'fixture-credential-987', calls = network(t, { secret }); const seller = await onboard(env, { upstream_auth: { header: 'authorization', prefix: 'Bearer ', secret } }); const q = await challenge(env, seller.service.endpoint); assert.equal(calls.upstream, 0); const response = await request(env, new URL(seller.service.endpoint).pathname, { headers: paidHeaders(q) }); assert.equal(response.status, 502); assert.ok(!(await response.text()).includes(secret)); assert.equal(new Headers(calls.outgoing.headers).get('authorization'), `Bearer ${secret}`); - apps/relay/src/seller-upstream.js:36mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
// No buyer authorization, payment signature, cookies or arbitrary headers are // forwarded. Redirects never carry the seller's credential to another origin. const upstream = await fetch(target, { method: m.method, headers: outgoing, body: ['GET', 'HEAD'].includes(m.method) ? undefined : fromBase64(m.body_base64), redirect: 'manual', signal: AbortSignal.timeout(12000) }); if (upstream.status >= 300 && upstream.status < 400) throw new SellerError('upstream_redirect_blocked', 502); const bytes = await readBoundedBody(upstream.body, SELLER_MAX_RESPONSE_BYTES); - apps/relay/src/x402-market.js:33mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
const started = performance.now(); try { const response = await fetch(url, { headers: { accept: "application/json", - scripts/ats100-ci.mjs:23mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
let response; try { response = await fetch(api, { method: "POST", headers: { "content-type": "application/json", "user-agent": "xguard-ats100-github-action/1.0" }, - scripts/inspect-buyer-discovery.mjs:6mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
async function probe(path, options, summarize) { try { const response = await fetch(cdp + path, { ...options, redirect: "error", signal: AbortSignal.timeout(15000), headers: { accept: "application/json", "content-type": "application/json", "user-agent": "XGuard-Discovery-Observer/1.0" }, - scripts/inspect-commercial.mjs:14mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
try { const results = await Promise.all(["/v1/balance", "/v1/ledger"].map(async path => { const response = await fetch("https://hooks.xguardgate.com" + path, { headers: { authorization: `Bearer ${process.env.XGUARD_OPERATOR_KEY}` }, signal: AbortSignal.timeout(15000), redirect: "error" }); if (!response.ok) throw new Error(`billing_HTTP_${response.status}`); return response.json(); - scripts/live-admin-smoke.mjs:8mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
const origin = process.env.LIVE_ORIGIN || 'https://xguardgate.com'; const headers = { authorization: `Bearer ${key}`, accept: 'application/json', 'user-agent': 'XGuardLive-admin-smoke/6.0', 'content-type': 'application/json' }; const response = await fetch(origin + '/admin', { headers, signal: AbortSignal.timeout(25000) }); assert.equal(response.status, 200, 'Owner authentication failed'); const body = await response.json(); assert.ok(body.stats); assert.ok(body.state.config); - scripts/live-admin-smoke.mjs:11mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
assert.equal(response.status, 200, 'Owner authentication failed'); const body = await response.json(); assert.ok(body.stats); assert.ok(body.state.config); const bootstrap = await fetch(origin + '/admin/bootstrap', { method: 'POST', headers, body: '{}', signal: AbortSignal.timeout(25000) }); assert.equal(bootstrap.status, 200, 'Bootstrap failed'); console.log(JSON.stringify({ owner_access: 'verified', curated_collection: 'queued', monetization_mode: body.state.config.mode, paid_test_requests: 0 })); - scripts/live-diagnostics.mjs:50mcp-js-ssrf-nonliteral-url (medium/low)
An outbound request is made to a URL that is not a literal. If any part is caller-supplied the server can be aimed at internal hosts it can reach and the caller cannot.
for (const path of ['/healthz', '/fact/npm/undici/latest-version', '/']) { try { const response = await fetch('https://xguardgate.com' + path, { headers: { 'user-agent': 'XGuardLive-diagnostics/6.0', accept: 'application/json' }, signal: AbortSignal.timeout(15000) }); await response.body?.cancel(); console.log(JSON.stringify({ probe: path, status: response.status })); } catch { console.log(JSON.stringify({ probe: path, status: 'network-error' })); }
Auth Posture
- authentication_model
source reads a credential header but no enforcing middleware or rejection path was found — `03` §4's "present but optional or trivially bypassable" band
- transport_security
declared endpoint(s) are HTTPS: https://xguardgate.com/mcp; scored 80, not 100: `03` §4 reserves 100 for HTTPS *with HSTS*, and an HSTS header can only be seen by connecting, which `04` §9 does not do. This is an unverifiable band, not an observed absence.
- secret_handling
detect-secrets matched 13 credential-shaped string(s), every one from a keyword or entropy heuristic (Base64 High Entropy String, Basic Auth Credentials, Hex High Entropy String, Secret Keyword). Those flag placeholders, variable names and content hashes as readily as credentials, so they do not establish the committed credential `03` §4's 0 asserts, and this sub-check is not scored; Secret Keyword at apps/billing/src/index.test.js:42; Secret Keyword at apps/relay/src/agent-token-usage-test.js:303; Secret Keyword at apps/relay/src/canonical-entry.js:345; Basic Auth Credentials at apps/relay/src/core/reconcile-payment-test.js:8; Secret Keyword at apps/relay/src/core/seller-policy.js:86; Hex High Entropy String at apps/relay/src/discovery-entry.js:11; Secret Keyword at apps/relay/src/execution-entry.js:45; Base64 High Entropy String at apps/relay/src/rail-entry.js:10; Basic Auth Credentials at apps/relay/src/seller-entry-test.js:274; Basic Auth Credentials at apps/relay/test/live.test.mjs:23; …and 3 more matches
- authorization_granularity
22 tool registrations with conditional access logic present in the same source
Dependency Health
- apps/relay/package-lock.jsonproxy-addr 2.0.7 — GHSA-jqcg-44mw-7w3h
transitive, CVSS 9.1, fixed in 2.0.8
https://osv.dev/vulnerability/GHSA-jqcg-44mw-7w3h - apps/relay/package-lock.json@modelcontextprotocol/sdk 1.29.0 — GHSA-6qxp-vccf-f47h
direct, CVSS 7.5, fixed in 1.31.0
https://osv.dev/vulnerability/GHSA-6qxp-vccf-f47h - apps/relay/package-lock.jsonsharp 0.35.4 — GHSA-wq5f-xc86-pv6w
transitive, CVSS 8.9, fixed in 0.35.5
https://osv.dev/vulnerability/GHSA-wq5f-xc86-pv6w - apps/relay/package-lock.jsonip-address 10.7.0 — GHSA-h3mg-xc3c-68pw
transitive, CVSS 6.3, fixed in 10.7.1
https://osv.dev/vulnerability/GHSA-h3mg-xc3c-68pw - apps/relay/package-lock.jsonip-address 10.7.0 — GHSA-j6r3-76f7-8jcv
transitive, CVSS 6.3, fixed in 10.7.1
https://osv.dev/vulnerability/GHSA-j6r3-76f7-8jcv - apps/relay/package-lock.jsonfast-uri 3.1.7 — GHSA-hrr3-gc8f-f4qj
transitive, CVSS 4.8, fixed in 3.1.8
https://osv.dev/vulnerability/GHSA-hrr3-gc8f-f4qj
Maintenance
- recency
last commit 8 day(s) before 2026-10-16
- issue_responsiveness
every issue read was filed by the project itself (50 by its maintainers or bots), so none is someone outside waiting for an answer, and `03` §5's question — will anyone read an issue I file? — has nothing to measure.
- release_cadence
released 26 day(s) ago (by releases), inside `03` §5's 30-day window
- bus_factor
this repository's 12-month history is longer than the bounded walk `04` §7 performs, and the authors found so far span more than one of `03` §5's bands. A limit of ours, not a finding about the server.
- repository_signals
`03` §5 scores this by quartile WITHIN the server's category, which needs a distribution across every scanned server and so cannot be computed while scanning one. It is a post-pass over the crawl and that pass does not exist yet.
Transparency
- license
LICENSE present with no SPDX-License-Identifier tag; identified as 'Apache-2.0' from its text
- readme_quality
README.md: over the size floor but none of `03` §7's content heuristics matched
- declared_scopes
specific scope or access documentation is present, but `03` §7's 100 and 60 bands both compare it against scopes INFERRED from static analysis, which is not built yet. Scoring 100 on an empty inference would be a vacuous truth — 'all ∅ inferred scopes are documented'.
- changelog
no CHANGELOG in the fetched source. `03` §7's partial credit for tagged releases carrying release notes is not applied here — that needs the forge API, which this check does not call
- security_contact
no SECURITY.md and no disclosure contact in the README, so a finder has no route to report a vulnerability privately
Contest a finding
Every number here comes from a rule reading published source, and rules get things wrong. If something on this page is incorrect — or correct but mitigated by a check the rule cannot see — open an issue and we answer it in writing. A finding we got wrong is removed and the rule is fixed; one that stands is answered on the record.
Anyone can file: the maintainer, a researcher who disagrees with the classification, or a user who tested the server and could not reproduce it. Name this server and the rule or advisory ID shown beside the finding above. We aim to reply within five business days and to resolve within ten.