PATCH release, security. GHSA-5p27-64mv-pr73, CVSS 3.1 9.1 (Critical): an unauthenticated caller could obtain a read+write bearer token and bypass the owner API key entirely. Affected only when MCP_OAUTH_ENABLED=true and Dynamic Client Registration is open (MCP_DCR_REGISTRATION_KEY unset, which is the default when OAuth is on) — MCP_OAUTH_ENABLED itself defaults to false, so a default install was never exposed. If you run OAuth with open DCR, upgrade now. The rest of this release is repository housekeeping left over from the GitHub-mirror reinstatement: dependency updates past their open advisories and a PyPI metadata fix.
Note on v11.8.1: its tag was created through the release API rather than a git push, so
release.ymlnever fired and v11.8.1 was never published to PyPI or Docker Hub. If you install from either, you are coming from 11.8.0 and this release carries both sets of changes. The v11.8.1 entry in CHANGELOG.md lists what that adds.
Security
- fix(oauth): reject
client_credentialsfrom clients that never registered for it (GHSA-5p27-64mv-pr73, CVE requested). Registering a public client via/oauth/registerand replaying its returned credentials against/oauth/tokenwithgrant_type=client_credentialsbypassed the owner API key entirely, handing an unauthenticated caller a read+write bearer token. Four defects lined up:registration.pyissued aclient_secreteven to clients that registered withtoken_endpoint_auth_method=none(an RFC 7591 public client authenticates with PKCE, not a secret);_handle_client_credentials_grantnever checked the client's registeredgrant_types(RFC 6749 section 4.4) or its auth method, so a public client could authenticate as if it were confidential; andstore_client()hashed unconditionally, persisting an absent secret as the SHA-256 of the empty string — not exploitable, since the constant-time comparison rejects empty input before the hash is consulted, but indistinguishable from a real one in storage. Theauthorization_codegrant was never affected: it already read the auth method off the stored client, and this fix copies that pattern. Affected v10.20.0 through v11.8.1. Operators who cannot upgrade immediately: setMCP_DCR_REGISTRATION_KEY, orMCP_OAUTH_ENABLED=false. Reported privately by Forrof, with a working proof of concept.
Changed
- A public client's registration response no longer contains a
client_secret. This is the intended consequence of the fix above, and the one behaviour change to be aware of: register withtoken_endpoint_auth_method=noneand theclient_secretfield is now absent rather than populated. That is what RFC 7591 prescribes — a public client authenticates with PKCE — and it is what theauthorization_codeflow already assumed. A client that was reading the secret out of its own registration response and using it was relying on the vulnerability. - chore(deps): move the locked Python and JS dependencies past their open advisories (#249).
uv.lock: aiohttp, cryptography, gitpython, joserfc, pydantic-settings, pyjwt, pypdf, python-multipart, starlette.tests/bridge/package-lock.jsonandtests/integration/package-lock.json: js-yaml. - chore: publish repository URLs on PyPI (#247).
pyproject.tomlhad no[project.urls]section at all, so the PyPI project page for the main distribution showed no Homepage, Repository, Documentation or Issues link.pyproject-lite.tomlalready carried one; both now note that they are kept in sync. - ci: restore CodeQL on the GitHub mirror (#252) — the one workflow the mirror is allowed to carry, since Forgejo has no equivalent security-analysis job.
- chore: point the mirror's issue template config at Codeberg (#251).
- docs: describe the GitHub mirror instead of a suspended account (#248).