| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- package securityaudit
- import (
- "context"
- "log/slog"
- "strings"
- )
- const (
- EventConfigUpdated = "prompt_audit.config_updated"
- EventConfigLoaded = "prompt_guard.config_loaded"
- EventConfigReloadDegraded = "prompt_guard.config_reload_degraded"
- EventConfigTokenInvalid = "prompt_guard.config_token_invalid"
- EventProbeStarted = "prompt_audit.endpoint_probe_started"
- EventProbeFinished = "prompt_audit.endpoint_probe_finished"
- EventProbeFailed = "prompt_audit.endpoint_probe_failed"
- EventJobEnqueued = "prompt_audit.job_enqueued"
- EventEnqueueSkipped = "prompt_audit.enqueue_skipped"
- EventEnqueueDropped = "prompt_audit.enqueue_dropped"
- EventAuditStarted = "prompt_audit.started"
- EventProcessingReclaimed = "prompt_audit.processing_reclaimed"
- EventProcessed = "prompt_audit.processed"
- EventProcessFailed = "prompt_audit.process_failed"
- EventFindingRecorded = "prompt_audit.finding_recorded"
- EventChunkStarted = "prompt_audit.scan_chunk_started"
- EventChunkCompleted = "prompt_audit.scan_chunk_completed"
- EventChunkFailed = "prompt_audit.scan_chunk_failed"
- EventChunksAggregated = "prompt_audit.scan_chunks_aggregated"
- EventEvaluationStarted = "prompt_guard.evaluation_started"
- EventGuardAllowed = "prompt_guard.allowed"
- EventGuardBlocked = "prompt_guard.blocked"
- EventGuardFailed = "prompt_guard.failed"
- EventResultRecordFailed = "prompt_guard.result_record_failed"
- EventEventDeleted = "prompt_audit.event_deleted"
- EventEventsDeleted = "prompt_audit.events_deleted"
- EventDeletePreviewed = "prompt_audit.events_delete_previewed"
- EventEventsFilterDeleted = "prompt_audit.events_filter_deleted"
- )
- var knownLogEvents = map[string]struct{}{
- EventConfigUpdated: {}, EventConfigLoaded: {}, EventConfigReloadDegraded: {}, EventConfigTokenInvalid: {},
- EventProbeStarted: {}, EventProbeFinished: {}, EventProbeFailed: {},
- EventJobEnqueued: {}, EventEnqueueSkipped: {}, EventEnqueueDropped: {},
- EventAuditStarted: {}, EventProcessingReclaimed: {}, EventProcessed: {}, EventProcessFailed: {}, EventFindingRecorded: {},
- EventChunkStarted: {}, EventChunkCompleted: {}, EventChunkFailed: {}, EventChunksAggregated: {},
- EventEvaluationStarted: {}, EventGuardAllowed: {}, EventGuardBlocked: {}, EventGuardFailed: {}, EventResultRecordFailed: {},
- EventEventDeleted: {}, EventEventsDeleted: {}, EventDeletePreviewed: {}, EventEventsFilterDeleted: {},
- }
- var allowedLogFields = map[string]struct{}{
- "request_id": {}, "user_id": {}, "api_key_id": {}, "group_id": {}, "provider": {},
- "protocol": {}, "endpoint": {}, "model": {}, "job_id": {}, "event_id": {},
- "config_version": {}, "guard_endpoint_id": {}, "decision": {}, "risk_level": {},
- "action": {}, "chunk_index": {}, "chunk_total": {}, "chunk_chars": {}, "input_chars": {},
- "input_limit": {}, "latency_ms": {}, "status": {}, "error_code": {}, "error_kind": {},
- "queue_length": {}, "queue_capacity": {}, "stage": {}, "upstream_dispatched": {},
- "billing_preconsumed": {}, "worker_id": {}, "reclaimed_total": {}, "attempts": {},
- "max_attempts": {}, "claim_version": {}, "http_status": {}, "retryable": {},
- }
- func LogInfo(event string, fields map[string]any) {
- if _, ok := knownLogEvents[event]; !ok {
- return
- }
- slog.LogAttrs(context.Background(), slog.LevelInfo, event, safeAttrs(fields)...)
- }
- func LogWarn(event string, fields map[string]any) {
- if _, ok := knownLogEvents[event]; !ok {
- return
- }
- slog.LogAttrs(context.Background(), slog.LevelWarn, event, safeAttrs(fields)...)
- }
- func LogError(event string, fields map[string]any) {
- if _, ok := knownLogEvents[event]; !ok {
- return
- }
- slog.LogAttrs(context.Background(), slog.LevelError, event, safeAttrs(fields)...)
- }
- func safeAttrs(fields map[string]any) []slog.Attr {
- attrs := make([]slog.Attr, 0, len(fields))
- for key, value := range fields {
- key = strings.TrimSpace(key)
- if _, allowed := allowedLogFields[key]; !allowed {
- continue
- }
- if text, ok := value.(string); ok {
- if key == "error_kind" || key == "error_code" {
- value = stableErrorCode(text)
- } else {
- value = TrimRunes(strings.TrimSpace(text), 256)
- }
- }
- attrs = append(attrs, slog.Any(key, value))
- }
- return attrs
- }
- func mergeLogFields(base map[string]any, extra map[string]any) map[string]any {
- result := make(map[string]any, len(base)+len(extra))
- for key, value := range base {
- result[key] = value
- }
- for key, value := range extra {
- result[key] = value
- }
- return result
- }
- func requestLogFields(req Request) map[string]any {
- return map[string]any{
- "request_id": req.RequestID, "user_id": req.UserID, "api_key_id": req.APIKeyID,
- "group_id": pointerLogID(req.GroupID), "provider": req.Provider, "protocol": req.Protocol,
- "endpoint": req.Endpoint, "model": req.Model, "stage": req.Stage,
- }
- }
- func snapshotLogFields(snapshot PromptSnapshot) map[string]any {
- return map[string]any{
- "request_id": snapshot.RequestID, "user_id": snapshot.UserID, "api_key_id": snapshot.APIKeyID,
- "group_id": pointerLogID(snapshot.GroupID), "provider": snapshot.Provider, "protocol": snapshot.Protocol,
- "endpoint": snapshot.Endpoint, "model": snapshot.Model, "stage": snapshot.Stage,
- }
- }
- func jobLogFields(job *Job) map[string]any {
- if job == nil {
- return map[string]any{}
- }
- fields := snapshotLogFields(job.Snapshot)
- fields["job_id"] = job.ID
- fields["config_version"] = job.ConfigVersion
- fields["claim_version"] = job.ClaimVersion
- return fields
- }
- func stableErrorCode(code string) string {
- code = strings.ToLower(strings.TrimSpace(code))
- if code == "" {
- return "unknown_error"
- }
- for _, char := range code {
- if (char >= 'a' && char <= 'z') || (char >= '0' && char <= '9') || char == '_' || char == '-' || char == '.' {
- continue
- }
- return "redacted_error"
- }
- return TrimRunes(code, 64)
- }
- func stableErrorMessage(code string) string {
- switch stableErrorCode(code) {
- case ErrorCodeBlocked:
- return "Prompt Guard blocked the request"
- case ErrorCodeUnavailable, "payload_store_unavailable", "payload_missing":
- return "Prompt Audit dependency is unavailable"
- case ErrorCodeInvalidResponse:
- return "Prompt Guard returned an invalid response"
- case "queue_full", "queue_admission_busy":
- return "Prompt Audit queue is unavailable"
- case "worker_panic":
- return "Prompt Audit worker failed"
- case "config_load_failed", "config_ttl_reload_failed", "config_invalidation_reload_failed":
- return "Prompt Audit configuration could not be loaded"
- default:
- return "Prompt Audit operation failed"
- }
- }
- func sanitizeStoredError(code string) (string, string) {
- stableCode := stableErrorCode(code)
- return stableCode, TrimRunes(stableErrorMessage(stableCode), 160)
- }
|