Reject API keys that are not valid header values at construction
Independent review Codex gpt-6-sol high: APPROVED (0 findings)
CI / ci (push) Successful in 1m30s
CI / ci (pull_request) Successful in 1m29s

FromSettings now fails with llm.ErrInvalidKey for a key with a control
character such as CR or LF, for every preset that sends a key, and
openai.New rejects such a key too, so neither defers the failure to the
first request. anthropic.New already rejected it. No error quotes the key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-04 09:08:46 -04:00
co-authored by Claude Opus 5.5
parent 31322f239e
commit 08be8891da
5 changed files with 42 additions and 2 deletions
+14 -1
View File
@@ -164,7 +164,7 @@ var all = map[string]Preset{
// FromSettings returns the Provider s describes, using key as its API key.
// It fails with a configuration error from package llm — ErrUnknownProvider,
// ErrMissingKey, ErrUnexpectedKey, ErrMissingBaseURL, ErrMissingModel,
// ErrMissingKey, ErrUnexpectedKey, ErrInvalidKey, ErrMissingBaseURL, ErrMissingModel,
// ErrMissingPublicURL or ErrInvalidSetting — before contacting any vendor.
//
// The Provider sends s.Temperature with every request whose Temperature is
@@ -230,6 +230,8 @@ func check(p Preset, s llm.Settings, key string) error {
return llm.ErrMissingKey
case p.Key == KeyNone && key != "":
return llm.ErrUnexpectedKey
case !validHeaderValue(key):
return llm.ErrInvalidKey
}
switch {
case p.BaseURL == "" && s.BaseURL == "":
@@ -252,6 +254,17 @@ func check(p Preset, s llm.Settings, key string) error {
return nil
}
// validHeaderValue reports whether value has no control character other
// than tab, so it can be sent as an HTTP header value.
func validHeaderValue(value string) bool {
for i := 0; i < len(value); i++ {
if c := value[i]; (c < 0x20 && c != '\t') || c == 0x7f {
return false
}
}
return true
}
// configured applies the household temperature and the preset's
// temperature quirk to every Generate.
type configured struct {