Skip to content

Release v0.51.3 - #6725

Merged
rdimitrov merged 1 commit into
mainfrom
release/v0.51.3
Sep 26, 2026
Merged

rdimitrov merged 1 commit into
mainfrom
release/v0.51.3

Conversation

@toolhive-release-app

Copy link
Copy Markdown
Contributor

Release v0.51.3

Version Bump

patch release

Files Updated

  • VERSION
  • deploy/charts/operator-crds/Chart.yaml (path: version)
  • deploy/charts/operator-crds/Chart.yaml (path: appVersion)
  • deploy/charts/operator/Chart.yaml (path: version)
  • deploy/charts/operator/Chart.yaml (path: appVersion)
  • deploy/charts/operator/values.yaml (path: operator.image)
  • deploy/charts/operator/values.yaml (path: operator.toolhiveRunnerImage)
  • deploy/charts/operator/values.yaml (path: operator.vmcpImage)
  • Helm chart docs (via helm-docs)

Next Steps

  1. Review this PR
  2. Merge to main
  3. Release automation will handle the rest

Checklist

  • Version bump is correct
  • All CI checks pass

Release-Triggered-By: rdimitrov
@codecov

codecov Bot commented Sep 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.27%. Comparing base (68465b9) to head (db5140d).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #6725   +/-   ##
=======================================
  Coverage   79.26%   79.27%           
=======================================
  Files         801      801           
  Lines       81168    81168           
=======================================
+ Hits        64338    64346    +8     
+ Misses      16825    16817    -8     
  Partials        5        5           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rdimitrov
rdimitrov merged commit 88bb284 into main Sep 26, 2026
44 checks passed
@rdimitrov
rdimitrov deleted the release/v0.51.3 branch September 26, 2026 21:33
@github-actions

Copy link
Copy Markdown
Contributor

📝 Generated release notes for v0.51.3

Auto-generated by the release-notes skill. Review and, if good, apply with:

gh release edit v0.51.3 --notes-file <paste-below>.md
Click to expand release notes

🚀 Toolhive v0.51.3 is live!

This patch release fixes a high-severity security issue in the embedded authorization server by binding each OAuth login to the browser that started it. It also makes vMCP's priority conflict resolution fail closed and tightens vMCP shutdown so audit records and file descriptors are not lost.

🔒 Security

Embedded authorization server: upstream callback bound to the initiating browser (GHSA-2gjv-f568-6cxp, High)

/oauth/callback completed a login for whichever browser arrived with a valid upstream state. An attacker who started an authorization for their own client could hand the upstream identity provider URL to a victim and receive an authorization code minted for the victim's identity. The device flow's verification-page login shared the same gap.

Both flows now bind the login to the browser that started it: /oauth/authorize and POST /oauth/device set a per-flow cookie whose hash is stored on the pending record, and the callback completes only when the same browser presents it. The device verification form additionally requires an anti-forgery token, so a cross-site page cannot start a device login from a victim's browser.

What changes for operators and tooling

  • Interactive sign-in must be completed in the browser that opened /oauth/authorize or the device verification page, with cookies enabled for that host. Headless drivers that walk the flow with a plain HTTP client must carry cookies between steps, and must load the device verification form before posting a user_code.
  • The upstream callback (redirect_uri, defaulting to {resourceUrl}/oauth/callback in the operator) must share a hostname with the browser-facing authorize URL (authorizationEndpointBaseUrl, else issuer). If the authorize URL is https, a non-loopback callback must be https too. Mismatched deployments log a WARN at startup naming the upstream, and every browser login through it is rejected until fixed.
  • When authorizationEndpointBaseUrl is set, the device flow's verification_uri is now advertised from that base URL instead of the issuer.
  • During a rolling upgrade, a login started on a v0.51.2 replica and finished on a v0.51.3 replica fails within the 10-minute pending window and must be restarted; one started on v0.51.3 and finished on v0.51.2 is not checked.

⚠️ Breaking Changes

  • Embedded auth server: upstream callback must share a host with the authorize URL — browser logins now fail with 400 when an upstream's redirectUri is on a different hostname than authorizationEndpointBaseUrl (or issuer), when an https authorize URL is paired with a non-loopback http callback, or when headless clients don't keep cookies. Point redirectUri at the authorize host and register it with your IdP (migration guide)
  • vMCP priority conflict resolution drops collisions involving unlisted backends — a tool name that collides across backends, where any of those backends is missing from priorityOrder, is now removed from every backend instead of going to the listed backend or being prefixed. Add every colliding backend to priorityOrder, or switch to conflictResolution: prefix (migration guide)

Migration guide: Embedded auth server browser binding

Only deployments that use the embedded authorization server (MCPExternalAuthConfig of type embeddedAuthServer, or the equivalent run config) are affected. The CRD schemas have not changed; only field descriptions were updated, so existing manifests still validate. What changed is runtime behavior. The binding cookie is host-only and is set on the browser-facing authorize host. A callback served from a different hostname never receives it, so the login is rejected. Token refresh and token exchange are not affected.

You are affected if any of the following apply:

  1. An upstream's redirectUri (explicit, or the operator default {resourceUrl}/oauth/callback) is on a different hostname than authorizationEndpointBaseUrl (or issuer when that is unset).
  2. The authorize URL is https but the callback is plain http on a non-loopback host.
  3. Automated or headless sign-in (E2E tests, scripted device approval) drives the flow without a cookie jar, or POSTs a user_code to /oauth/device without first loading the form.

Before

embeddedAuthServer:
  issuer: https://auth.internal.example.com
  authorizationEndpointBaseUrl: https://login.example.com
  upstreamProviders:
    - name: okta
      oidcConfig:
        redirectUri: https://auth.internal.example.com/oauth/callback

After

embeddedAuthServer:
  issuer: https://auth.internal.example.com
  authorizationEndpointBaseUrl: https://login.example.com
  upstreamProviders:
    - name: okta
      oidcConfig:
        redirectUri: https://login.example.com/oauth/callback

Migration steps

  1. After upgrading, check auth server logs for startup warnings saying an upstream's redirect_uri host differs from the authorize host, or uses plain http while the authorize URL is https.
  2. For each upstream that is flagged, set redirectUri to https://<authorize-host>/oauth/callback, or move authorizationEndpointBaseUrl/issuer onto the callback's host.
  3. Register the new callback URL with the upstream IdP, keeping the old one until the rollout is complete, and make sure your ingress routes /oauth/callback and /oauth/device on that host to the auth server.
  4. Update headless or scripted clients to keep cookies across the whole flow and to GET /oauth/device before submitting a user_code.
  5. Ask users to retry any login that was in progress during the rolling upgrade.

Commit: 024c042 — Advisory GHSA-2gjv-f568-6cxp

Migration guide: vMCP priority conflict resolution

Affects vMCP users with conflictResolution: priority whose colliding backends are not all listed in priorityOrder. Previously:

  • if a listed backend collided with an unlisted one, the listed backend won the bare tool name;
  • if all colliding backends were unlisted, each copy was kept under a prefixed name.

Now, both cases drop every candidate from tools/list and routing. This is fail-closed: Cedar tool authorization is name-only, so handing a name to another backend could redirect an existing permit, and a prefixed name could get around existing name-scoped forbid policies. Config validation does not catch this; the only signal is an error log: dropped tool conflict involving backend not in priority order. Conflict-free tools, and conflicts where every backend is listed, behave as before.

Before

aggregation:
  conflictResolution: priority
  conflictResolutionConfig:
    priorityOrder:
      - github
# create_issue exposed by both github and jira: now dropped from both

After

aggregation:
  conflictResolution: priority
  conflictResolutionConfig:
    priorityOrder:
      - github
      - jira

Migration steps

  1. Search vMCP logs for dropped tool conflict involving backend not in priority order to find the tools and backends affected.
  2. Add every backend that can collide to priorityOrder. With defaultToolVisibility: deny, each priorityOrder entry also needs a matching tools entry.
  3. If you relied on the old prefixed names for unlisted backends, switch to conflictResolution: prefix, then review your name-scoped Cedar policies for the new names.

PR: #6127 — Fixes #6097

🐛 Bug Fixes

  • Fixed a file descriptor leak by closing the vMCP workflow audit log writer at shutdown and when construction fails; stdout/stderr are never closed (#6110)
  • vMCP no longer tears down core, audit, and session resources while HTTP requests are still draining, so long-running workflows keep their final audit records. For Go embedders, Stop now returns an error after a failed drain and should be retried with a fresh context, and serving and shutdown errors are both kept via errors.Join (#6715)

🧹 Misc

  • Fixed a spelling error flagged by codespell in the browser-binding doc comment (#6722)
  • Regenerated the OpenAPI docs and Go SDK to pick up the updated redirect_uri description; there are no schema changes (#6724)

📦 Dependencies

Module Version
charm.land/bubbletea/v2 v2.0.10
github.com/aws/aws-sdk-go-v2 v1.47.1
github.com/aws/aws-sdk-go-v2/config v1.33.6
github.com/aws/aws-sdk-go-v2/service/sts v1.51.1
github.com/onsi/gomega v1.44.0
k8s.io/api v0.37.1
k8s.io/apiextensions-apiserver v0.37.1
k8s.io/apimachinery v0.37.1
k8s.io/client-go v0.37.1
anthropics/claude-code-action (action) v1.0.235
github/codeql-action (action) v4.38.2
golangci/golangci-lint (action) v2.14.0
ghcr.io/modelcontextprotocol/inspector (image) 2.8.0
registry (image) digest 852b3e4
Full commit log

What's Changed

🔗 Full changelog: v0.51.2...v0.51.3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release size/XS Extra small PR: < 100 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant