Summary
The public catalogue UI served at GET / (file internal/api/handlers/v0/ui_index.html) is vulnerable to stored cross-site scripting via the server.websiteUrl field of any published server.json. Server-side validation in internal/validators/validators.go (validateWebsiteURL) only checks that the URL parses, is absolute, and uses the https scheme; it does not reject quote characters. Client-side, the value is interpolated into a double-quoted href attribute via innerHTML, using a homegrown escapeHtml helper that performs the standard textContent → innerHTML round-trip. Per the HTML serialisation algorithm, that round-trip encodes only &, <, > and U+00A0 inside text nodes, it does not encode " or '. A literal " in websiteUrl therefore breaks out of the href attribute, allowing arbitrary on* event handlers to be appended to the same <a> element. The Content-Security-Policy on / is script-src 'self' 'unsafe-inline' https://cdn.tailwindcss.com, so the injected event handlers execute.
Any user able to obtain a publish token (e.g. via POST /v0/auth/github-at with their own GitHub account, or POST /v0/auth/none on a deployment that has anonymous auth enabled) can plant a poisoned record visible to every visitor of the registry homepage.
Affected component
- Validator:
internal/validators/validators.go,validateWebsiteURL(lines 153–199) - Sink:
internal/api/handlers/v0/ui_index.html,toggleDetails(card, item)at line 432, thehrefattribute built aroundescapeHtml(server.websiteUrl) - Helper:
escapeHtmldefined atinternal/api/handlers/v0/ui_index.htmllines 494–498
Proof of concept
Obtain a Registry JWT for any namespace you control (a GitHub OAuth exchange against a throwaway account suffices):
TOKEN=$(curl -sS -X POST https://registry.modelcontextprotocol.io/v0/auth/github-at \ -H 'Content-Type: application/json' \ -d '{"github_token":"<gh-pat>"}' | jq -r .registry_token)Publish a server with a poisoned
websiteUrl. The literal"is preserved end-to-end:curl -sS -X POST https://registry.modelcontextprotocol.io/v0/publish \ -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ --data-binary @- <<'EOF' { "$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-29/server.schema.json", "name": "io.github.<your-account>/xss-poc", "version": "0.0.1", "description": "hover the website link", "websiteUrl": "https://example.com/\"onmouseover=alert(document.domain)//" } EOFVisit
https://registry.modelcontextprotocol.io/, search forxss-poc, click the card to expand it, then hover the Website link in the details panel. The injectedonmouseoverfires andalert(document.domain)runs on theregistry.modelcontextprotocol.ioorigin.
Why server-side validation does not catch this
Go's net/url.Parse accepts literal " in the path component:
input="https://example.com/\"onmouseover=alert(1)//" IsAbs=true Scheme="https" Path="/\"onmouseover=alert(1)//"
Neither the Huma format:"uri" annotation nor validateWebsiteURL's scheme/IsAbs triplet rejects this string. The architecture's existing protection, repository.url is regex-locked to ^https?://(www\.)?github\.com/[\w.-]+/[\w.-]+/?$ and therefore cannot contain quotes, does not extend to websiteUrl, which has no allowlist.
Why client-side escapeHtml does not catch this
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
Per the HTML5 spec (§13.3 Serialising HTML fragments), the only characters encoded inside the text content of an element are &, <, >, and U+00A0. " and ' are not encoded because in a text-content context they are not special. The helper is therefore safe in element-text contexts (where it is correctly used for name, version, description, etc.) but unsafe inside an attribute value, which is precisely where it is invoked for href on lines 432 and 426.
Suggested remediation (any one suffices)
- Replace the homegrown
escapeHtmlwith an attribute-safe encoder that also escapes",', backtick, and=, the OWASP HTML attribute-encoding rule. - Avoid building the
hrefvia string templates. UsesetAttribute('href', value)instead,setAttributeis not subject to HTML tokenisation, so no breakout is possible. - Tighten
validateWebsiteURLto reject any URL whose raw bytes contain",',<,>,,\t, or\n, or, conservatively, store the canonical re-serialised form (parsedURL.String()percent-encodes such characters in the path). - Drop
'unsafe-inline'fromscript-srcafter auditing the inline scripts on the page.
Approach (3) is the smallest server-side change and immediately neutralises the exploit for any new publishes; approaches (1) or (2) close the class of bug at the sink so future fields with similar patterns are safe by default.
Impact
- Stored XSS on the official MCP Registry homepage. The malicious entry sits in the public catalogue alongside legitimate ones; any user expanding the entry triggers the payload.
- Because the page is served on the official
registry.modelcontextprotocol.ioorigin, the injected script can:- Read and overwrite
localStorage(baseUrl,customUrl), pinning the user's subsequent reads to an attacker-controlled "Custom" base URL. - Issue any same-origin or cross-origin XHR (
connect-src *is granted). - Phish for Registry JWTs by injecting fake auth flows on the trusted origin.
- Read and overwrite
- The CSP
script-src 'self' 'unsafe-inline' https://cdn.tailwindcss.comdoes not block this because'unsafe-inline'permits inline event-handler attributes.
Untrusted input is rendered as active markup in a victim's browser, which can run script in their session. Typical impact: session or credential theft, and actions taken as the user.
CVE-2026-44429 has a CVSS score of 5.4 (Medium). The vector is network-reachable, low privileges required, and user interaction required. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (1.7.7); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Remediation advice
Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.
Frequently Asked Questions
- What is CVE-2026-44429? CVE-2026-44429 is a medium-severity cross-site scripting (XSS) vulnerability in github.com/modelcontextprotocol/registry (go), affecting versions < 1.7.7. It is fixed in 1.7.7. Untrusted input is rendered as active markup in a victim's browser, which can run script in their session.
- How severe is CVE-2026-44429? CVE-2026-44429 has a CVSS score of 5.4 (Medium). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of github.com/modelcontextprotocol/registry are affected by CVE-2026-44429? github.com/modelcontextprotocol/registry (go) versions < 1.7.7 is affected.
- Is there a fix for CVE-2026-44429? Yes. CVE-2026-44429 is fixed in 1.7.7. Upgrade to this version or later.
- Is CVE-2026-44429 exploitable, and should I be worried? Whether CVE-2026-44429 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-44429 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-44429? Upgrade
github.com/modelcontextprotocol/registryto 1.7.7 or later.