CVE-2026-14537: Verified Reproduction
CVE-2026-14537: mcp-toolbox authorization bypass: unauthenticated tool invocation via direct HTTP API
CVE-2026-14537 is verified against googleapis/genai-toolbox · github. Affected versions: >= v1.3.0 (2026-05-21) and <= v1.4.0 (2026-06-04). Fixed in v1.5.0 (2026-06-18). Vulnerability class: Auth Bypass. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00318.
What Is CVE-2026-14537?
CVE-2026-14537 is a high-severity Auth Bypass vulnerability affecting googleapis/genai-toolbox >= v1.3.0 (2026-05-21) and <= v1.4.0 (2026-06-04). Pruva has independently reproduced it and publishes a verified, runnable proof-of-concept (reproduction REPRO-2026-00318).
CVE-2026-14537 Severity
CVE-2026-14537 is rated high severity.
High — serious impact or readily exploitable. Prioritize remediation.
Affected googleapis/genai-toolbox Versions
googleapis/genai-toolbox · github versions >= v1.3.0 (2026-05-21) and <= v1.4.0 (2026-06-04) are affected.
How to Reproduce CVE-2026-14537
pruva-verify REPRO-2026-00318 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00318/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-14537
- reached the target end-to-end
- full exploit chain demonstrated
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
Unauthenticated POST /api/tool/protected-tool/invoke with JSON body {"sql": "SELECT 'CVE-2026-14537-PWNED' AS marker"} and no Authorization header
- POST /api/tool/{toolName}/invoke on mcp-toolbox v1.4.0 started with --enable-api plus an mcpEnabled generic authService; tool protected only by MCP-model scopesRequired
reproduction_steps.sh How the agent worked
Root Cause and Exploit Chain for CVE-2026-14537
google/mcp-toolbox (repository googleapis/genai-toolbox) versions v1.3.0–v1.4.0
suffer from an incorrect-authorization vulnerability (CWE-863). When the server
is configured with an MCP-enabled authorization service (mcpEnabled: true,
OAuth scopes via scopesRequired) and the legacy direct HTTP API is enabled
(--enable-api), the legacy endpoint POST /api/tool/{toolName}/invoke
executes tools without enforcing the MCP authorization policy. An
unauthenticated remote attacker can invoke tools that the operator believes are
protected by OAuth scopes, because scope enforcement exists only on the /mcp
endpoint path.
- Component:
internal/server/api.go(toolInvokeHandler) together withinternal/server/server.go(mcpAuthMiddlewaremounted only under/mcp). - Affected versions: v1.3.0 (2026-05-21) through v1.4.0 (2026-06-04).
- Fixed in: v1.5.0 (2026-06-18), fix commit
a6ff910a602adece11f0a6581d6211e5927f7182("fix(server): fail if MCP auth is enabled together with enable-api (#3435)"). - Risk: high (CVSS 4.0 8.1). Any tool protected solely by the MCP
authorization model (an
mcpEnabledauthService plus tool-levelscopesRequired, with no legacyauthRequired) is remotely invocable with no credentials at all, including destructive tools (e.g.sqlite-execute-sql, SQL execution tools against production databases).
Impact Parity
- Disclosed/claimed maximum impact: authorization bypass — unauthenticated
remote invocation of scope-protected tools via the direct HTTP API
(
expected_impact=authz_bypass, surfaceapi_remote). - Reproduced impact in this run: identical — HTTP 200 and actual tool
execution (arbitrary SQL against the configured SQLite source) via
POST /api/tool/protected-tool/invokewith noAuthorizationheader, while the same unauthenticated caller receives HTTP 401 on/mcp. - Parity:
full. - Not demonstrated: nothing beyond the claimed impact (no code execution was claimed or required).
Root Cause
Authorization in mcp-toolbox v1.3.0/v1.4.0 is split across two independent enforcement points:
- MCP path (
/mcp):mcpAuthMiddleware(internal/server/server.go) validates the Bearer token viaValidateMCPAuth(including authServicescopesRequired), and the MCPtools/callhandler additionally enforces tool-level scopes throughmcputil.ValidateScopes(ctx, tool.GetScopesRequired(), ...)(internal/server/mcp/v20250618/method.go). - Legacy HTTP API path (
/api, enabled by--enable-api): the router ininternal/server/api.gohas no MCP auth middleware, andtoolInvokeHandleronly enforces the legacyauthRequiredmechanism (tool.Authorized(verifiedAuthServices)), which returnstrueunconditionally when a tool declares noauthRequired(IsAuthorized: "no authorization requirement"). Tool-levelscopesRequiredis never consulted on this path.
Consequently, a tool protected only by the modern MCP scope model
(scopesRequired, no legacy authRequired) is fully open on the legacy HTTP
API: the unauthenticated request passes IsAuthorized([]) and the tool
executes. The fix in v1.5.0 does not add scope checks to the legacy endpoint;
instead it makes the dangerous configuration fail closed at startup:
cmd/root.go and internal/server/server.go (InitializeConfigs) refuse to
run when any authService IsMCPEnabled() and EnableAPI are both set
("MCP Auth cannot be enabled together with the legacy HTTP API"), and a new
IsMCPEnabled() method was added to the AuthServiceConfig interface for
that check.
Fix commit: a6ff910a602adece11f0a6581d6211e5927f7182 (PR #3435).
Reproduction Steps
- Run
bundle/repro/reproduction_steps.sh(self-contained; installs Go 1.26.3 if needed, clones/uses the prepared repo cache, builds the real product at v1.4.0 and v1.5.0). - The script starts a local OIDC authorization-server stub
(
bundle/repro/oidc_stub.py), then launches the real v1.4.0 server withbundle/repro/tools.yaml(generic authServicemcpEnabled: true+scopesRequired: [read:files]; toolprotected-toolof typesqlite-execute-sqlwithscopesRequired: [execute:sql]and noauthRequired) and flags--enable-api --toolbox-url --port 5000. - It then sends the attacker request
POST /api/tool/protected-tool/invokewith body{"sql": "SELECT 'CVE-2026-14537-PWNED' AS marker"}and noAuthorizationheader, followed by a contrast request to/mcpwithout a token, and finally attempts to start v1.5.0 with the identical config and flags.
Expected evidence:
- v1.4.0 legacy API: HTTP 200 with the marker
CVE-2026-14537-PWNEDin the JSON response (tool executed unauthenticated) — vulnerable. - v1.4.0
/mcp: HTTP 401 with aWWW-Authenticatechallenge — the MCP path enforces authorization correctly. - v1.5.0: exits at startup logging "MCP Auth cannot be enabled together with the legacy HTTP API" and never serves the API — fixed (fail closed).
Evidence
bundle/logs/reproduction_steps.log— full run transcript.bundle/logs/vuln_server.log— v1.4.0 server startup (MCP auth + legacy API both active).bundle/logs/vuln_api_invoke_status.txt/vuln_api_invoke_body.json— HTTP status and response body of the unauthenticated invoke (200 + marker).bundle/logs/vuln_mcp_noauth_status.txt/vuln_mcp_noauth_body.json— 401 from/mcpwithout a token (contrast control).bundle/logs/fixed_server.log— v1.5.0 startup refusal message.bundle/logs/oidc_stub.log— OIDC discovery requests made by the server at startup (proves the real MCP auth stack was initialized).bundle/repro/runtime_manifest.json— structured runtime evidence manifest.
Environment: linux/amd64, Go 1.26.3, mcp-toolbox built from source at tags v1.4.0 (d67cfbe8ddc) and v1.5.0, python3 OIDC stub on 127.0.0.1:8099.
Recommendations / Next Steps
- Upgrade to v1.5.0 or later; do not run
--enable-apitogether with MCP-enabled authorization services on affected versions. - Operators on v1.3.0/v1.4.0 who must keep the legacy API should add explicit
legacy
authRequiredentries to every tool (the legacy mechanism is still enforced on/api), or front the server with a proxy that blocks/api. - Long-term: the legacy
/apiendpoints are deprecated; migrate clients to the standard/mcpJSON-RPC endpoint where MCP authorization is enforced.
Additional Notes
- The reproduction is idempotent: the script rebuilds only when the resolved commit changes, restarts all services on each run, and cleans up background processes via a trap.
- Edge cases: tools that DO declare legacy
authRequiredreferencing the mcpEnabled generic authService are not bypassed on/api(claims come from the MCP context, which is empty there, yielding 401); the bypass applies to tools protected only via the MCP scope model, which is the configuration the fix commit targets ("clients could potentially bypass MCP authorization policies by using the legacy HTTP API"). - No public PoC existed; this reproduction was built from the fix-commit analysis of PR #3435.
CVE-2026-14537 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
go version 2>&1; which go; ls /pruva/project-cache 2>&1; ls /pruva/project-cache/repo-mirrors 2>&1pruva-command: 1: go: not found repo-mirrors
cd /pruva/project-cache && git clone --bare https://github.com/googleapis/genai-toolbox repo-mirrors/mcp-toolbox.git 2>&1 | tail -3; git --git-dir=repo-mirrors/mcp-toolbox.git tag | grep -E 'v1\.[345]\.0'Cloning into bare repository 'repo-mirrors/mcp-toolbox.git'... v1.3.0 v1.4.0 v1.5.0
git -C /pruva/project-cache clone repo-mirrors/mcp-toolbox.git repo 2>&1 | tail -1; git -C /pruva/project-cache/repo checkout v1.4.0 2>&1 | tail -2; ls /pruva/project-cache/repodone. HEAD is now at d67cfbe8ddc chore(main): release 1.4.0 (#3301) AGENTS.md CHANGELOG.md CLAUDE.md CODE_OF_CONDUCT.md CONTRIBUTING.md
grep -rn "api/tool" /pruva/project-cache/repo/internal/server/*.go | head -20/pruva/project-cache/repo/internal/server/server_test.go:382: url: "/api/toolset",
/pruva/project-cache/repo/internal/server/server_test.go:387: url: "/api/tool/tool_one",
/pruva/project-cache/repo/internal/server/server_test.go:392: url: "/api/tool/tool_one/invoke",
/pruva/project-cache/repo/internal/server/server_test.go:537: url: "/api/toolset",
/pruva/project-cache/repo/internal/server/server_test.go:542: url: "/api/tool/tool_one",
/pruva/project-cache/repo/internal/server/server_test.go:549: url: "/api/tool/tool_one/invoke",
/pruva/project-cache/repo/internal/server/server_test.go:926: url := fmt.Sprintf("http://%s:%d/api/tool/list", addr, port)
grep -rn "invoke" /pruva/project-cache/repo/internal/server/api.go 2>/dev/null | head; ls /pruva/project-cache/repo/internal/server/47: r.Post("/invoke", func(w http.ResponseWriter, r *http.Request) { toolInvokeHandler(s, w, r) })
113:// toolInvokeHandler handles the API request to invoke a specific Tool.
115: ctx, span := s.instrumentation.Tracer.Start(r.Context(), "toolbox/server/tool/invoke")
api.go
api_test.go
common_test.go
config.go
mcpgit -C /pruva/project-cache/repo diff v1.4.0 v1.5.0 --stat | tail -30{"cwd":"/workspace","duration_ms":88,"exit_code":0,"stderr":"","stdout":" npm/server-win32-arm64/scripts/downloadBinary.js | 2 +-\n npm/server-win32-arm64/version.txt | 1 -\n npm/server-win32-x64/package-lock.json | 4 +-\n npm/server-win32-x64/package.json | 2 +-\n npm/server-win32-x64/scripts/downloadBinary.js | 2 +-\n npm/server-win32-x64/version.txt | 1 -\n npm/server/README.md | 2 +-\n npm/server/package-lock.json | 51 +-\n npm/server/package.json | 12 +-\n pypi/setup.py | 122 +--\n pypi/src/toolbox_server/__init__.py | 2 +-\n pypi/tests/__init__.py | 1 -\n pypi/tests/{test_wrapper.py => test_main.py} | 9 +-\n server.json | 4 +-\n .../alloydbainl/alloydb_ai_nl_integration_test.go | 2 +-\n tests/alloydbainl/alloydb_ai_nl_mcp_test.go | 4 +-\n tests/auth/auth_integration_test.go | 37 +-\n tests/bigquery/bigquery_integration_test.go | 4… [truncated]Artifacts and Evidence for CVE-2026-14537
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-14537
Upgrade googleapis/genai-toolbox · github to v1.5.0 (2026-06-18) or later.
FAQ: CVE-2026-14537
Is CVE-2026-14537 exploitable?
How severe is CVE-2026-14537?
What type of vulnerability is CVE-2026-14537?
Which versions of googleapis/genai-toolbox are affected by CVE-2026-14537?
Is there a fix for CVE-2026-14537?
How can I reproduce CVE-2026-14537?
Is the CVE-2026-14537 reproduction verified?
References for CVE-2026-14537
Authoritative sources for CVE-2026-14537 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.