mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
The client went ahead when an authorization server's metadata omitted code_challenge_methods_supported. The spec makes refusing a MUST: without it the client cannot tell whether the server verifies the challenge it is sent. It now refuses with ErrOAuthPKCEUnadvertised before any browser opens, and the error names the escape hatch. Some real authorization servers leave the field out, so a server entry can set oauth_allow_missing_pkce_metadata = true. It is honoured only from a config the user keeps in their own home (user config, legacy user config, ~/.claude.json): a project's config, its .mcp.json and an installed package are written by someone else, and none of them may relax the check for whoever opens or installs them. MCPConfigSource.UserAuthorized() also covers project sources, so it could not be the test. PKCE S256 is still sent when the check is relaxed. The renderer writes the flag back, or the next config save would drop it. The per-server overrides moved from render.go to render_plugin.go to keep render.go within its complexity and size budget. Security review (internal/ext/plugin is sensitive): the default is stricter than before; the only relaxation is per server, from user-owned sources, pinned by a test per source; the authorization URL still carries an S256 challenge, pinned by the test. plugin, pluginspec, config and boot tests pass on a clean worktree; golangci-lint reports 0 issues; the coverage gate does not flag internal/ext/plugin.
33 lines
1.1 KiB
Go
33 lines
1.1 KiB
Go
package pluginspec
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"reasonix/internal/contract/config"
|
|
)
|
|
|
|
// Only a config the user keeps in their own home may relax the PKCE check. A
|
|
// project's config, its .mcp.json and an installed package are written by
|
|
// someone else, and the flag in them is ignored.
|
|
func TestPKCEOptOutIsHonouredOnlyFromTheUsersOwnConfig(t *testing.T) {
|
|
cases := map[config.MCPConfigSource]bool{
|
|
config.MCPSourceUserConfig: true,
|
|
config.MCPSourceLegacyUser: true,
|
|
config.MCPSourceClaudeUser: true,
|
|
config.MCPSourceClaudeLocal: true,
|
|
config.MCPSourceProjectConfig: false,
|
|
config.MCPSourceProjectMCPJSON: false,
|
|
config.MCPSourcePluginPackage: false,
|
|
config.MCPSourceUnknown: false,
|
|
}
|
|
for source, want := range cases {
|
|
spec := FromEntry(config.PluginEntry{
|
|
Name: "remote", Type: "http", URL: "https://mcp.example.test/mcp",
|
|
Source: source, OAuthAllowMissingPKCEMetadata: true,
|
|
}, "", Options{})
|
|
if spec.OAuthAllowMissingPKCEMetadata != want {
|
|
t.Errorf("source %q: opt-out honoured = %v, want %v", source, spec.OAuthAllowMissingPKCEMetadata, want)
|
|
}
|
|
}
|
|
}
|