diff --git a/coderd/x/chatd/chatdebug/redaction.go b/coderd/x/chatd/chatdebug/redaction.go index 6ac8d6d71b..fc4677c710 100644 --- a/coderd/x/chatd/chatdebug/redaction.go +++ b/coderd/x/chatd/chatdebug/redaction.go @@ -162,19 +162,44 @@ func consumeJSONEOF(decoder *json.Decoder) error { return err } +// safeRateLimitHeaderNames lists rate-limit headers that contain +// "token" in the name but carry numeric usage counters, not +// credentials. They are checked in isSensitiveName before the +// generic "token" substring match so they pass through unredacted. +// Add new entries here when a provider introduces a rate-limit +// header family containing "token" (e.g. Anthropic's per-modality, +// Priority Tier, or fast-mode headers). var safeRateLimitHeaderNames = map[string]struct{}{ - "anthropic-ratelimit-requests-limit": {}, - "anthropic-ratelimit-requests-remaining": {}, - "anthropic-ratelimit-requests-reset": {}, - "anthropic-ratelimit-tokens-limit": {}, - "anthropic-ratelimit-tokens-remaining": {}, - "anthropic-ratelimit-tokens-reset": {}, - "x-ratelimit-limit-requests": {}, - "x-ratelimit-limit-tokens": {}, - "x-ratelimit-remaining-requests": {}, - "x-ratelimit-remaining-tokens": {}, - "x-ratelimit-reset-requests": {}, - "x-ratelimit-reset-tokens": {}, + "anthropic-ratelimit-requests-limit": {}, + "anthropic-ratelimit-requests-remaining": {}, + "anthropic-ratelimit-requests-reset": {}, + "anthropic-ratelimit-tokens-limit": {}, + "anthropic-ratelimit-tokens-remaining": {}, + "anthropic-ratelimit-tokens-reset": {}, + "anthropic-ratelimit-input-tokens-limit": {}, + "anthropic-ratelimit-input-tokens-remaining": {}, + "anthropic-ratelimit-input-tokens-reset": {}, + "anthropic-ratelimit-output-tokens-limit": {}, + "anthropic-ratelimit-output-tokens-remaining": {}, + "anthropic-ratelimit-output-tokens-reset": {}, + "anthropic-priority-input-tokens-limit": {}, + "anthropic-priority-input-tokens-remaining": {}, + "anthropic-priority-input-tokens-reset": {}, + "anthropic-priority-output-tokens-limit": {}, + "anthropic-priority-output-tokens-remaining": {}, + "anthropic-priority-output-tokens-reset": {}, + "anthropic-fast-input-tokens-limit": {}, + "anthropic-fast-input-tokens-remaining": {}, + "anthropic-fast-input-tokens-reset": {}, + "anthropic-fast-output-tokens-limit": {}, + "anthropic-fast-output-tokens-remaining": {}, + "anthropic-fast-output-tokens-reset": {}, + "x-ratelimit-limit-requests": {}, + "x-ratelimit-limit-tokens": {}, + "x-ratelimit-remaining-requests": {}, + "x-ratelimit-remaining-tokens": {}, + "x-ratelimit-reset-requests": {}, + "x-ratelimit-reset-tokens": {}, } // isSensitiveName reports whether a name (header or query parameter) diff --git a/coderd/x/chatd/chatdebug/redaction_test.go b/coderd/x/chatd/chatdebug/redaction_test.go index c60d72a802..9fefe26118 100644 --- a/coderd/x/chatd/chatdebug/redaction_test.go +++ b/coderd/x/chatd/chatdebug/redaction_test.go @@ -88,18 +88,54 @@ func TestRedactHeaders(t *testing.T) { t.Parallel() headers := http.Header{ - "Anthropic-Ratelimit-Tokens-Limit": {"1000000"}, - "Anthropic-Ratelimit-Tokens-Remaining": {"999000"}, - "Anthropic-Ratelimit-Tokens-Reset": {"2026-03-31T08:55:26Z"}, - "X-RateLimit-Limit-Tokens": {"120000"}, - "X-RateLimit-Remaining-Tokens": {"119500"}, - "X-RateLimit-Reset-Tokens": {"12ms"}, + "Anthropic-Ratelimit-Tokens-Limit": {"1000000"}, + "Anthropic-Ratelimit-Tokens-Remaining": {"999000"}, + "Anthropic-Ratelimit-Tokens-Reset": {"2026-03-31T08:55:26Z"}, + "Anthropic-Ratelimit-Input-Tokens-Limit": {"200000"}, + "Anthropic-Ratelimit-Input-Tokens-Remaining": {"199000"}, + "Anthropic-Ratelimit-Input-Tokens-Reset": {"2026-03-31T08:55:26Z"}, + "Anthropic-Ratelimit-Output-Tokens-Limit": {"80000"}, + "Anthropic-Ratelimit-Output-Tokens-Remaining": {"79500"}, + "Anthropic-Ratelimit-Output-Tokens-Reset": {"2026-03-31T08:55:26Z"}, + "Anthropic-Priority-Input-Tokens-Limit": {"10000"}, + "Anthropic-Priority-Input-Tokens-Remaining": {"9618"}, + "Anthropic-Priority-Input-Tokens-Reset": {"2026-03-31T08:55:26Z"}, + "Anthropic-Priority-Output-Tokens-Limit": {"10000"}, + "Anthropic-Priority-Output-Tokens-Remaining": {"6000"}, + "Anthropic-Priority-Output-Tokens-Reset": {"2026-03-31T08:55:26Z"}, + "Anthropic-Fast-Input-Tokens-Limit": {"50000"}, + "Anthropic-Fast-Input-Tokens-Remaining": {"49000"}, + "Anthropic-Fast-Input-Tokens-Reset": {"2026-03-31T08:55:26Z"}, + "Anthropic-Fast-Output-Tokens-Limit": {"25000"}, + "Anthropic-Fast-Output-Tokens-Remaining": {"24000"}, + "Anthropic-Fast-Output-Tokens-Reset": {"2026-03-31T08:55:26Z"}, + "X-RateLimit-Limit-Tokens": {"120000"}, + "X-RateLimit-Remaining-Tokens": {"119500"}, + "X-RateLimit-Reset-Tokens": {"12ms"}, } redacted := chatdebug.RedactHeaders(headers) require.Equal(t, "1000000", redacted["Anthropic-Ratelimit-Tokens-Limit"]) require.Equal(t, "999000", redacted["Anthropic-Ratelimit-Tokens-Remaining"]) require.Equal(t, "2026-03-31T08:55:26Z", redacted["Anthropic-Ratelimit-Tokens-Reset"]) + require.Equal(t, "200000", redacted["Anthropic-Ratelimit-Input-Tokens-Limit"]) + require.Equal(t, "199000", redacted["Anthropic-Ratelimit-Input-Tokens-Remaining"]) + require.Equal(t, "2026-03-31T08:55:26Z", redacted["Anthropic-Ratelimit-Input-Tokens-Reset"]) + require.Equal(t, "80000", redacted["Anthropic-Ratelimit-Output-Tokens-Limit"]) + require.Equal(t, "79500", redacted["Anthropic-Ratelimit-Output-Tokens-Remaining"]) + require.Equal(t, "2026-03-31T08:55:26Z", redacted["Anthropic-Ratelimit-Output-Tokens-Reset"]) + require.Equal(t, "10000", redacted["Anthropic-Priority-Input-Tokens-Limit"]) + require.Equal(t, "9618", redacted["Anthropic-Priority-Input-Tokens-Remaining"]) + require.Equal(t, "2026-03-31T08:55:26Z", redacted["Anthropic-Priority-Input-Tokens-Reset"]) + require.Equal(t, "10000", redacted["Anthropic-Priority-Output-Tokens-Limit"]) + require.Equal(t, "6000", redacted["Anthropic-Priority-Output-Tokens-Remaining"]) + require.Equal(t, "2026-03-31T08:55:26Z", redacted["Anthropic-Priority-Output-Tokens-Reset"]) + require.Equal(t, "50000", redacted["Anthropic-Fast-Input-Tokens-Limit"]) + require.Equal(t, "49000", redacted["Anthropic-Fast-Input-Tokens-Remaining"]) + require.Equal(t, "2026-03-31T08:55:26Z", redacted["Anthropic-Fast-Input-Tokens-Reset"]) + require.Equal(t, "25000", redacted["Anthropic-Fast-Output-Tokens-Limit"]) + require.Equal(t, "24000", redacted["Anthropic-Fast-Output-Tokens-Remaining"]) + require.Equal(t, "2026-03-31T08:55:26Z", redacted["Anthropic-Fast-Output-Tokens-Reset"]) require.Equal(t, "120000", redacted["X-RateLimit-Limit-Tokens"]) require.Equal(t, "119500", redacted["X-RateLimit-Remaining-Tokens"]) require.Equal(t, "12ms", redacted["X-RateLimit-Reset-Tokens"])