Restrict OAuth client branding URLs to http(s)
CIMD and registration accepted any URI scheme for client_uri and logo_uri, so allowlisted metadata could surface javascript: links on sign-in. Validate absolute http/https at ingest and only expose those schemes in branding. Signed-off-by: Bryan Frimin <bryan@probo.com>
This commit is contained in:
@@ -647,12 +647,26 @@ func NewCIMDClient(
|
||||
}
|
||||
|
||||
if logoURI != nil && *logoURI != "" {
|
||||
u := uri.URI(*logoURI)
|
||||
u, err := uri.Parse(*logoURI)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse logo_uri: %w", err)
|
||||
}
|
||||
if !u.IsHTTP() {
|
||||
return nil, fmt.Errorf("logo_uri must be an absolute http or https URL")
|
||||
}
|
||||
|
||||
client.LogoURI = &u
|
||||
}
|
||||
|
||||
if clientURI != nil && *clientURI != "" {
|
||||
u := uri.URI(*clientURI)
|
||||
u, err := uri.Parse(*clientURI)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse client_uri: %w", err)
|
||||
}
|
||||
if !u.IsHTTP() {
|
||||
return nil, fmt.Errorf("client_uri must be an absolute http or https URL")
|
||||
}
|
||||
|
||||
client.ClientURI = &u
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,10 @@ package coredata_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/uri"
|
||||
)
|
||||
@@ -100,3 +102,72 @@ func TestOAuth2Client_IsRedirectURIAllowed(t *testing.T) {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewCIMDClient_WebURIs(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run(
|
||||
"accepts https client_uri and logo_uri",
|
||||
func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
clientURI := "https://mcp.example.com"
|
||||
logoURI := "https://mcp.example.com/logo.png"
|
||||
|
||||
client, err := coredata.NewCIMDClient(
|
||||
"https://mcp.example.com/oauth/metadata.json",
|
||||
"Example MCP",
|
||||
[]string{"https://mcp.example.com/callback"},
|
||||
nil,
|
||||
&logoURI,
|
||||
&clientURI,
|
||||
time.Now(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, client.ClientURI)
|
||||
require.NotNil(t, client.LogoURI)
|
||||
assert.Equal(t, "https://mcp.example.com", client.ClientURI.String())
|
||||
assert.Equal(t, "https://mcp.example.com/logo.png", client.LogoURI.String())
|
||||
},
|
||||
)
|
||||
|
||||
t.Run(
|
||||
"rejects non-web client_uri",
|
||||
func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
clientURI := "javascript://example.com/%0Aalert(1)"
|
||||
|
||||
_, err := coredata.NewCIMDClient(
|
||||
"https://mcp.example.com/oauth/metadata.json",
|
||||
"Example MCP",
|
||||
[]string{"https://mcp.example.com/callback"},
|
||||
nil,
|
||||
nil,
|
||||
&clientURI,
|
||||
time.Now(),
|
||||
)
|
||||
require.Error(t, err)
|
||||
},
|
||||
)
|
||||
|
||||
t.Run(
|
||||
"rejects non-web logo_uri",
|
||||
func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
logoURI := "data://example.com/image"
|
||||
|
||||
_, err := coredata.NewCIMDClient(
|
||||
"https://mcp.example.com/oauth/metadata.json",
|
||||
"Example MCP",
|
||||
[]string{"https://mcp.example.com/callback"},
|
||||
nil,
|
||||
&logoURI,
|
||||
nil,
|
||||
time.Now(),
|
||||
)
|
||||
require.Error(t, err)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user