Files
Charon/backend/internal/security/audit_logger_test.go.bak
T
akanealw eec8c28fb3
Go Benchmark / Performance Regression Check (push) Has been cancelled
Cerberus Integration / Cerberus Security Stack Integration (push) Has been cancelled
Upload Coverage to Codecov / Backend Codecov Upload (push) Has been cancelled
Upload Coverage to Codecov / Frontend Codecov Upload (push) Has been cancelled
CodeQL - Analyze / CodeQL analysis (go) (push) Has been cancelled
CodeQL - Analyze / CodeQL analysis (javascript-typescript) (push) Has been cancelled
CrowdSec Integration / CrowdSec Bouncer Integration (push) Has been cancelled
Docker Build, Publish & Test / build-and-push (push) Has been cancelled
Quality Checks / Auth Route Protection Contract (push) Has been cancelled
Quality Checks / Codecov Trigger/Comment Parity Guard (push) Has been cancelled
Quality Checks / Backend (Go) (push) Has been cancelled
Quality Checks / Frontend (React) (push) Has been cancelled
Rate Limit integration / Rate Limiting Integration (push) Has been cancelled
Security Scan (PR) / Trivy Binary Scan (push) Has been cancelled
Supply Chain Verification (PR) / Verify Supply Chain (push) Has been cancelled
WAF integration / Coraza WAF Integration (push) Has been cancelled
Docker Build, Publish & Test / Security Scan PR Image (push) Has been cancelled
Repo Health Check / Repo health (push) Has been cancelled
History Rewrite Dry-Run / Dry-run preview for history rewrite (push) Has been cancelled
Prune Renovate Branches / prune (push) Has been cancelled
Renovate / renovate (push) Has been cancelled
Nightly Build & Package / sync-development-to-nightly (push) Has been cancelled
Nightly Build & Package / Trigger Nightly Validation Workflows (push) Has been cancelled
Nightly Build & Package / build-and-push-nightly (push) Has been cancelled
Nightly Build & Package / test-nightly-image (push) Has been cancelled
Nightly Build & Package / verify-nightly-supply-chain (push) Has been cancelled
Update GeoLite2 Checksum / update-checksum (push) Has been cancelled
Container Registry Prune / prune-ghcr (push) Has been cancelled
Container Registry Prune / prune-dockerhub (push) Has been cancelled
Container Registry Prune / summarize (push) Has been cancelled
Supply Chain Verification / Verify SBOM (push) Has been cancelled
Supply Chain Verification / Verify Release Artifacts (push) Has been cancelled
Supply Chain Verification / Verify Docker Image Supply Chain (push) Has been cancelled
Monitor Caddy Major Release / check-caddy-major (push) Has been cancelled
Weekly Nightly to Main Promotion / Verify Nightly Branch Health (push) Has been cancelled
Weekly Nightly to Main Promotion / Create Promotion PR (push) Has been cancelled
Weekly Nightly to Main Promotion / Trigger Missing Required Checks (push) Has been cancelled
Weekly Nightly to Main Promotion / Notify on Failure (push) Has been cancelled
Weekly Nightly to Main Promotion / Workflow Summary (push) Has been cancelled
Weekly Security Rebuild / Security Rebuild & Scan (push) Has been cancelled
changed perms
2026-04-22 18:19:14 +00:00

163 lines
4.7 KiB
Plaintext
Executable File

package security
import (
"encoding/json"
"strings"
"testing"
"time"
)
// TestAuditEvent_JSONSerialization tests that audit events serialize correctly to JSON.
func TestAuditEvent_JSONSerialization(t *testing.T) {
event := AuditEvent{
Timestamp: "2025-12-31T12:00:00Z",
Action: "url_validation",
Host: "example.com",
RequestID: "test-123",
Result: "blocked",
ResolvedIPs: []string{"192.168.1.1", "10.0.0.1"},
BlockedReason: "private_ip",
UserID: "user123",
SourceIP: "203.0.113.1",
}
// Serialize to JSON
jsonBytes, err := json.Marshal(event)
if err != nil {
t.Fatalf("Failed to marshal AuditEvent: %v", err)
}
// Verify all fields are present
jsonStr := string(jsonBytes)
expectedFields := []string{
"timestamp", "action", "host", "request_id", "result",
"resolved_ips", "blocked_reason", "user_id", "source_ip",
}
for _, field := range expectedFields {
if !strings.Contains(jsonStr, field) {
t.Errorf("JSON output missing field: %s", field)
}
}
// Deserialize and verify
var decoded AuditEvent
err = json.Unmarshal(jsonBytes, &decoded)
if err != nil {
t.Fatalf("Failed to unmarshal AuditEvent: %v", err)
}
if decoded.Timestamp != event.Timestamp {
t.Errorf("Timestamp mismatch: got %s, want %s", decoded.Timestamp, event.Timestamp)
}
if decoded.UserID != event.UserID {
t.Errorf("UserID mismatch: got %s, want %s", decoded.UserID, event.UserID)
}
if len(decoded.ResolvedIPs) != len(event.ResolvedIPs) {
t.Errorf("ResolvedIPs length mismatch: got %d, want %d", len(decoded.ResolvedIPs), len(event.ResolvedIPs))
}
}
// TestAuditLogger_LogURLValidation tests audit logging of URL validation events.
func TestAuditLogger_LogURLValidation(t *testing.T) {
logger := NewAuditLogger()
event := AuditEvent{
Action: "url_test",
Host: "malicious.com",
RequestID: "req-456",
Result: "blocked",
ResolvedIPs: []string{"169.254.169.254"},
BlockedReason: "metadata_endpoint",
UserID: "attacker",
SourceIP: "198.51.100.1",
}
// This will log to standard logger, which we can't easily capture in tests
// But we can verify it doesn't panic
logger.LogURLValidation(event)
// Verify timestamp was auto-added if missing
event2 := AuditEvent{
Action: "test",
Host: "test.com",
}
logger.LogURLValidation(event2)
}
// TestAuditLogger_LogURLTest tests the convenience method for URL tests.
func TestAuditLogger_LogURLTest(t *testing.T) {
logger := NewAuditLogger()
// Should not panic
logger.LogURLTest("example.com", "req-789", "user456", "192.0.2.1", "allowed")
}
// TestAuditLogger_LogSSRFBlock tests the convenience method for SSRF blocks.
func TestAuditLogger_LogSSRFBlock(t *testing.T) {
logger := NewAuditLogger()
resolvedIPs := []string{"10.0.0.1", "192.168.1.1"}
// Should not panic
logger.LogSSRFBlock("internal.local", resolvedIPs, "private_ip", "user123", "203.0.113.5")
}
// TestGlobalAuditLogger tests the global audit logger functions.
func TestGlobalAuditLogger(t *testing.T) {
// Test global functions don't panic
LogURLTest("test.com", "req-global", "user-global", "192.0.2.10", "allowed")
LogSSRFBlock("blocked.local", []string{"127.0.0.1"}, "loopback", "user-global", "198.51.100.10")
}
// TestAuditEvent_RequiredFields tests that required fields are enforced.
func TestAuditEvent_RequiredFields(t *testing.T) {
// CRITICAL: UserID field must be present for attribution
event := AuditEvent{
Timestamp: time.Now().UTC().Format(time.RFC3339),
Action: "ssrf_block",
Host: "malicious.com",
RequestID: "req-security",
Result: "blocked",
ResolvedIPs: []string{"192.168.1.1"},
BlockedReason: "private_ip",
UserID: "attacker123", // REQUIRED per Supervisor review
SourceIP: "203.0.113.100",
}
jsonBytes, err := json.Marshal(event)
if err != nil {
t.Fatalf("Failed to marshal: %v", err)
}
// Verify UserID is in JSON output
if !strings.Contains(string(jsonBytes), "attacker123") {
t.Errorf("UserID not found in audit log JSON")
}
}
// TestAuditLogger_TimestampFormat tests that timestamps use RFC3339 format.
func TestAuditLogger_TimestampFormat(t *testing.T) {
logger := NewAuditLogger()
event := AuditEvent{
Action: "test",
Host: "test.com",
// Timestamp intentionally omitted to test auto-generation
}
// Capture the event by marshaling after logging
// In real scenario, LogURLValidation sets the timestamp
if event.Timestamp == "" {
event.Timestamp = time.Now().UTC().Format(time.RFC3339)
}
// Parse the timestamp to verify it's valid RFC3339
_, err := time.Parse(time.RFC3339, event.Timestamp)
if err != nil {
t.Errorf("Invalid timestamp format: %s, error: %v", event.Timestamp, err)
}
logger.LogURLValidation(event)
}