Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
67 lines
2.3 KiB
Go
67 lines
2.3 KiB
Go
package securityaudit
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"log/slog"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestPromptAuditLogAllowlistAndErrorsDoNotLeakCanarySecrets(t *testing.T) {
|
|
const canary = "PROMPT_AUDIT_CANARY_SECRET_DO_NOT_PERSIST"
|
|
var output bytes.Buffer
|
|
previous := slog.Default()
|
|
slog.SetDefault(slog.New(slog.NewJSONHandler(&output, nil)))
|
|
t.Cleanup(func() { slog.SetDefault(previous) })
|
|
|
|
LogWarn(EventConfigReloadDegraded, map[string]any{
|
|
"status": "degraded",
|
|
"error_code": "config_reload_failed",
|
|
"error_kind": "Authorization: Bearer " + canary,
|
|
"token": canary,
|
|
"body": canary,
|
|
"base_url": "https://guard.example.test/path?api_key=" + canary,
|
|
"raw_prompt": "prompt " + canary,
|
|
})
|
|
require.NotContains(t, output.String(), canary)
|
|
require.NotContains(t, output.String(), "api_key=")
|
|
require.Contains(t, output.String(), EventConfigReloadDegraded)
|
|
|
|
beforeUnknown := output.Len()
|
|
LogWarn("prompt_audit.typo_event", map[string]any{"status": "failed"})
|
|
require.Equal(t, beforeUnknown, output.Len(), "events outside the stable dictionary must not be emitted")
|
|
require.Len(t, knownLogEvents, 28)
|
|
|
|
_, err := NormalizeBaseURL("https://guard.example.test/path?token=" + canary)
|
|
require.Error(t, err)
|
|
require.NotContains(t, err.Error(), canary)
|
|
}
|
|
|
|
func TestPromptGuardFailureLogUsesCompleteAllowlistedContextAndNoSideEffects(t *testing.T) {
|
|
var output bytes.Buffer
|
|
previous := slog.Default()
|
|
slog.SetDefault(slog.New(slog.NewJSONHandler(&output, nil)))
|
|
t.Cleanup(func() { slog.SetDefault(previous) })
|
|
groupID := int64(9)
|
|
snapshot := PromptSnapshot{
|
|
RequestID: "req-1", UserID: 2, APIKeyID: 3, GroupID: &groupID,
|
|
Provider: "openai", Protocol: "openai_chat", Endpoint: "/v1/chat/completions",
|
|
Model: "gpt-test", Stage: "http",
|
|
}
|
|
logGuardFailure(snapshot, ActiveConfig{ConfigVersion: 7}, DecisionUnavailable, ErrorCodeUnavailable, "guard-1", 25*time.Millisecond)
|
|
|
|
var entry map[string]any
|
|
require.NoError(t, json.Unmarshal(output.Bytes(), &entry))
|
|
for key := range snapshotLogFields(snapshot) {
|
|
require.Contains(t, entry, key)
|
|
}
|
|
require.EqualValues(t, 7, entry["config_version"])
|
|
require.Equal(t, ErrorCodeUnavailable, entry["error_code"])
|
|
require.Equal(t, false, entry["upstream_dispatched"])
|
|
require.Equal(t, false, entry["billing_preconsumed"])
|
|
require.EqualValues(t, 25, entry["latency_ms"])
|
|
}
|