fix(security): bound fan-out and method surface and scrub relayed text

- Add a process-wide gate on MCP tools/list backend connections. The
  per-request limit alone let N callers open N times as many.
- Reject CONNECT and TRACE on native passthrough and on MCP and A2A
  targets with 405.
- Scrub URL query strings and the target URL from a backend JSON-RPC
  error message before it reaches the caller.
- Replace the value of redis.address in GET /admin/api/config. The
  users file path stays visible by design.
- Escape line breaks in every log line so an upstream string cannot
  forge log entries.
- Add stripClientAnthropicBeta. When true, POST /v1/messages does not
  forward the client anthropic-beta header. The default stays false.
This commit is contained in:
2026-10-15 22:20:07 +01:00
parent 8c4bd16a22
commit d0d29b8c52
10 changed files with 272 additions and 6 deletions
+12
View File
@@ -44,6 +44,14 @@ var redactedConfigKeys = map[string]bool{
"password": true,
}
// hiddenConfigKeys names JSON keys whose non-empty value is replaced
// outright: RedisConfig.Address ("address"), which can embed credentials in
// a redis:// URL. UsersConfig.File stays visible on purpose, as a path
// reveals where user keys live without holding one.
var hiddenConfigKeys = map[string]bool{
"address": true,
}
// baseURLLikeConfigKeys names every JSON object key redactConfig passes
// through sanitizeBaseURL (admin.go) instead of the literal/env/file
// redaction rule above: ProviderConfig.BaseURL ("baseUrl") and
@@ -102,6 +110,10 @@ func redactValue(v any) {
switch {
case redactedConfigKeys[k] && isString:
t[k] = redactSecretString(s)
case hiddenConfigKeys[k] && isString:
if s != "" {
t[k] = redactedSecretPlaceholder
}
case baseURLLikeConfigKeys[k] && isString:
t[k] = sanitizeBaseURL(s)
default: