mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
fix: clean Bedrock headers (#24718)
Bedrock chat provider requests can inherit Anthropic public API headers from the process environment, which causes mixed Anthropic and Bedrock auth headers on signed requests. Update the Anthropic SDK fork so its Bedrock middleware strips Anthropic-only headers before signing requests, and keep a chatprovider regression test for the production request shape. > Mux is acting on Mike's behalf.
This commit is contained in:
@@ -2,6 +2,7 @@ package chatprovider_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
@@ -967,6 +968,87 @@ func TestModelFromConfig_Bedrock(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// TestModelFromConfig_BedrockStripsAnthropicHeaders is a regression test
|
||||
// for a bug where the Anthropic SDK reads ANTHROPIC_API_KEY from the
|
||||
// process environment and adds X-Api-Key and Anthropic-Version headers to
|
||||
// every request. On Bedrock, these headers conflict with SigV4 signing and
|
||||
// cause auth failures. The SDK's Bedrock middleware strips them before
|
||||
// signing. This test verifies the outgoing request shape with both
|
||||
// Anthropic and AWS credentials present.
|
||||
func TestModelFromConfig_BedrockStripsAnthropicHeaders(t *testing.T) {
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
|
||||
t.Setenv("ANTHROPIC_API_KEY", "anthropic-env-key")
|
||||
t.Setenv("AWS_REGION", "us-east-2")
|
||||
t.Setenv("AWS_ACCESS_KEY_ID", "test-access-key")
|
||||
t.Setenv("AWS_SECRET_ACCESS_KEY", "test-secret-key")
|
||||
t.Setenv("AWS_SESSION_TOKEN", "test-session-token")
|
||||
|
||||
type requestCapture struct {
|
||||
Authorization string
|
||||
AnthropicVersion string
|
||||
XAPIKey string
|
||||
Body string
|
||||
ReadError error
|
||||
}
|
||||
|
||||
requests := make(chan requestCapture, 1)
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
|
||||
requests <- requestCapture{
|
||||
Authorization: r.Header.Get("Authorization"),
|
||||
AnthropicVersion: r.Header.Get("Anthropic-Version"),
|
||||
XAPIKey: r.Header.Get("X-Api-Key"),
|
||||
Body: string(body),
|
||||
ReadError: err,
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(bedrockNonStreamingResponse())
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
model, err := chatprovider.ModelFromConfig(
|
||||
fantasybedrock.Name,
|
||||
"anthropic.claude-opus-4-6-v1",
|
||||
chatprovider.ProviderAPIKeys{
|
||||
ByProvider: map[string]string{
|
||||
fantasybedrock.Name: "",
|
||||
},
|
||||
BaseURLByProvider: map[string]string{
|
||||
fantasybedrock.Name: server.URL,
|
||||
},
|
||||
},
|
||||
chatprovider.UserAgent(),
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, model)
|
||||
|
||||
_, err = model.Generate(ctx, fantasy.Call{
|
||||
Prompt: []fantasy.Message{
|
||||
{
|
||||
Role: fantasy.MessageRoleUser,
|
||||
Content: []fantasy.MessagePart{
|
||||
fantasy.TextPart{Text: "hello"},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
got := testutil.TryReceive(ctx, t, requests)
|
||||
require.NoError(t, got.ReadError)
|
||||
require.Empty(t, got.AnthropicVersion)
|
||||
require.Empty(t, got.XAPIKey)
|
||||
require.Contains(t, got.Authorization, "AWS4-HMAC-SHA256")
|
||||
require.NotContains(t, got.Authorization, "anthropic-version")
|
||||
require.NotContains(t, got.Authorization, "x-api-key")
|
||||
require.Contains(t, got.Body, `"anthropic_version":"bedrock-2023-05-31"`)
|
||||
}
|
||||
|
||||
func bedrockNonStreamingResponse() map[string]any {
|
||||
return map[string]any{
|
||||
"id": "msg_01Test",
|
||||
|
||||
@@ -89,10 +89,10 @@ replace github.com/spf13/afero => github.com/aslilac/afero v0.0.0-20250403163713
|
||||
// See: https://github.com/coder/fantasy/commits/f83367a4a205
|
||||
replace charm.land/fantasy => github.com/coder/fantasy v0.0.0-20260426185602-951a49c681df
|
||||
|
||||
// coder/coder uses a fork of charmbracelet's fork of the Anthropic Go SDK with some
|
||||
// additional performance improvements.
|
||||
// See: https://github.com/coder/anthropic-sdk-go/commits/a31d7d0e7067
|
||||
replace github.com/charmbracelet/anthropic-sdk-go => github.com/coder/anthropic-sdk-go v0.0.0-20260415160422-a31d7d0e7067
|
||||
// coder/coder uses a fork of charmbracelet's fork of the Anthropic Go SDK
|
||||
// with performance improvements and Bedrock header cleanup.
|
||||
// See: https://github.com/coder/anthropic-sdk-go/commits/3be8e193ec89
|
||||
replace github.com/charmbracelet/anthropic-sdk-go => github.com/coder/anthropic-sdk-go v0.0.0-20260424230212-3be8e193ec89
|
||||
|
||||
// Replace sdks with our own optimized forks until relevant upstream PRs are merged.
|
||||
// https://github.com/anthropics/anthropic-sdk-go/pull/262
|
||||
|
||||
@@ -314,8 +314,8 @@ github.com/coder/agentapi-sdk-go v0.0.0-20250505131810-560d1d88d225 h1:tRIViZ5JR
|
||||
github.com/coder/agentapi-sdk-go v0.0.0-20250505131810-560d1d88d225/go.mod h1:rNLVpYgEVeu1Zk29K64z6Od8RBP9DwqCu9OfCzh8MR4=
|
||||
github.com/coder/aisdk-go v0.0.9 h1:Vzo/k2qwVGLTR10ESDeP2Ecek1SdPfZlEjtTfMveiVo=
|
||||
github.com/coder/aisdk-go v0.0.9/go.mod h1:KF6/Vkono0FJJOtWtveh5j7yfNrSctVTpwgweYWSp5M=
|
||||
github.com/coder/anthropic-sdk-go v0.0.0-20260415160422-a31d7d0e7067 h1:v1RAkUO21u0QH6UlUueSHMbgFf++BZZW41Rj6LM2eWo=
|
||||
github.com/coder/anthropic-sdk-go v0.0.0-20260415160422-a31d7d0e7067/go.mod h1:hqlYqR7uPKOKfnNeicUbZp0Ps0GeYFlKYtwh5HGDCx8=
|
||||
github.com/coder/anthropic-sdk-go v0.0.0-20260424230212-3be8e193ec89 h1:IVJutHfU944mb4D66K7XdPwKMAJrNC9FOq6JB4bveuI=
|
||||
github.com/coder/anthropic-sdk-go v0.0.0-20260424230212-3be8e193ec89/go.mod h1:hqlYqR7uPKOKfnNeicUbZp0Ps0GeYFlKYtwh5HGDCx8=
|
||||
github.com/coder/boundary v0.8.4-0.20260304164748-566aeea939ab h1:HrlxyTmMQpOHfSKzRU1vf5TxrmV6vL5OiWq+Dvn5qh0=
|
||||
github.com/coder/boundary v0.8.4-0.20260304164748-566aeea939ab/go.mod h1:BhJhyKW/+zZQzaGZ3vn27if2k0Vx5xLXzq7ZCQx5gPk=
|
||||
github.com/coder/bubbletea v1.2.2-0.20241212190825-007a1cdb2c41 h1:SBN/DA63+ZHwuWwPHPYoCZ/KLAjHv5g4h2MS4f2/MTI=
|
||||
|
||||
Reference in New Issue
Block a user