Harden prompt debug redaction
This commit is contained in:
@@ -171,7 +171,7 @@ func (w *PromptDebugWriter) WritePreparation(ref PromptDebugRef, preparation pro
|
||||
artifact.RenderedMessages = promptDebugMessages(debug.RenderedMessages)
|
||||
artifact.StructuredSchema = copyRawJSON(debug.StructuredSchema)
|
||||
artifact.Endpoint = safePromptDebugEndpoint(debug.Endpoint)
|
||||
parameters, err := redactPromptDebugParameters(debug.ParametersJSON)
|
||||
parameters, err := safePromptDebugParameters(debug.ParametersJSON)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -404,55 +404,55 @@ func copyRawJSON(value []byte) json.RawMessage {
|
||||
|
||||
func safePromptDebugEndpoint(value string) string {
|
||||
parsed, err := url.Parse(value)
|
||||
if err != nil {
|
||||
if err != nil || parsed.Scheme == "" || parsed.Host == "" {
|
||||
return ""
|
||||
}
|
||||
parsed.User = nil
|
||||
parameters := parsed.Query()
|
||||
for key := range parameters {
|
||||
if isPromptDebugSecretKey(key) {
|
||||
parameters[key] = []string{"[redacted]"}
|
||||
}
|
||||
}
|
||||
parsed.RawQuery = parameters.Encode()
|
||||
parsed.Fragment = ""
|
||||
return parsed.String()
|
||||
return (&url.URL{Scheme: parsed.Scheme, Host: parsed.Host}).String()
|
||||
}
|
||||
|
||||
func redactPromptDebugParameters(value []byte) (json.RawMessage, error) {
|
||||
func safePromptDebugParameters(value []byte) (json.RawMessage, error) {
|
||||
if len(value) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var decoded any
|
||||
var decoded map[string]json.RawMessage
|
||||
if err := json.Unmarshal(value, &decoded); err != nil {
|
||||
return nil, fmt.Errorf("decode prompt debug parameters: %w", err)
|
||||
return nil, fmt.Errorf("prompt debug parameters must be a JSON object")
|
||||
}
|
||||
redactPromptDebugValue(decoded)
|
||||
encoded, err := json.Marshal(decoded)
|
||||
parameters := make(map[string]json.RawMessage)
|
||||
for _, field := range []struct {
|
||||
name string
|
||||
value func(json.RawMessage) bool
|
||||
}{
|
||||
{name: "temperature", value: promptDebugJSONNumber},
|
||||
{name: "max_tokens", value: promptDebugJSONInteger},
|
||||
{name: "top_p", value: promptDebugJSONNumber},
|
||||
{name: "timeout_seconds", value: promptDebugJSONInteger},
|
||||
{name: "service_tier", value: promptDebugJSONString},
|
||||
{name: "reasoning_effort", value: promptDebugJSONString},
|
||||
} {
|
||||
value, ok := decoded[field.name]
|
||||
if ok && field.value(value) {
|
||||
parameters[field.name] = append(json.RawMessage(nil), value...)
|
||||
}
|
||||
}
|
||||
encoded, err := json.Marshal(parameters)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("encode prompt debug parameters: %w", err)
|
||||
return nil, fmt.Errorf("encode safe prompt debug parameters: %w", err)
|
||||
}
|
||||
return encoded, nil
|
||||
}
|
||||
|
||||
func redactPromptDebugValue(value any) {
|
||||
switch typed := value.(type) {
|
||||
case map[string]any:
|
||||
for key, item := range typed {
|
||||
if isPromptDebugSecretKey(key) {
|
||||
typed[key] = "[redacted]"
|
||||
continue
|
||||
}
|
||||
redactPromptDebugValue(item)
|
||||
}
|
||||
case []any:
|
||||
for _, item := range typed {
|
||||
redactPromptDebugValue(item)
|
||||
}
|
||||
}
|
||||
func promptDebugJSONNumber(value json.RawMessage) bool {
|
||||
var decoded float64
|
||||
return json.Unmarshal(value, &decoded) == nil
|
||||
}
|
||||
|
||||
func isPromptDebugSecretKey(key string) bool {
|
||||
normalized := strings.NewReplacer("_", "", "-", "", " ", "").Replace(strings.ToLower(key))
|
||||
return strings.Contains(normalized, "credential") || strings.Contains(normalized, "secret") || strings.Contains(normalized, "password") || strings.Contains(normalized, "token") || strings.Contains(normalized, "apikey") || strings.Contains(normalized, "authorization")
|
||||
func promptDebugJSONInteger(value json.RawMessage) bool {
|
||||
var decoded int
|
||||
return json.Unmarshal(value, &decoded) == nil
|
||||
}
|
||||
|
||||
func promptDebugJSONString(value json.RawMessage) bool {
|
||||
var decoded string
|
||||
return json.Unmarshal(value, &decoded) == nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user