The classification-aware I5 audit path (§4.4): - DegradingSink.Reserve: central up → AuditCentral; central down + confidential → refuse (no buffer); central down + internal/public + buffer writable → AuditBuffered; central down + buffer unwritable → floor refuse. Record executes the reserved outcome and, when buffered, fires an ntfy alert. - FileBuffer: durable JSONL buffer that survives process restart; Confirm rewrites the file without a record, so a buffered record is cleared ONLY after its central write is confirmed. - LokiCentral: /ready probe + /loki/api/v1/push (full audit entry as the structured line). NtfyNotifier: degraded-state alerts; token only in the auth header, never logged (regression-tested). - Reconcile + StartReconcile: replay buffered records to central on recovery, confirm-then-clear per record; a failed push keeps the record buffered (no loss). SlogSink updated to the two-phase shape (always central, never fails) — the default when no loki endpoint is set. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
42 lines
1.3 KiB
Go
42 lines
1.3 KiB
Go
package audit_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"log/slog"
|
|
"testing"
|
|
|
|
"github.com/mathiasbq/hyperguild/ingestion/internal/audit"
|
|
"github.com/mathiasbq/hyperguild/ingestion/internal/capture"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestSlogSinkRecordsEntryAndSecurityEvents(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
sink := audit.NewSlogSink(slog.New(slog.NewTextHandler(&buf, nil)))
|
|
|
|
err := sink.Record(context.Background(), capture.AuditEntry{
|
|
Principal: "koala-cli",
|
|
Harness: "claude-code",
|
|
EffectiveClassification: "confidential",
|
|
Items: []string{"insight:wiki/a/facts/x.md"},
|
|
SecurityEvents: []string{"asserted-vs-derived origin mismatch"},
|
|
}, capture.AuditCentral)
|
|
require.NoError(t, err)
|
|
|
|
out := buf.String()
|
|
assert.Contains(t, out, "capture audit")
|
|
assert.Contains(t, out, "koala-cli")
|
|
assert.Contains(t, out, "confidential")
|
|
assert.Contains(t, out, "capture security event")
|
|
assert.Contains(t, out, "asserted-vs-derived origin mismatch")
|
|
}
|
|
|
|
func TestSlogSinkNilLoggerDefaults(t *testing.T) {
|
|
// nil logger must not panic.
|
|
require.NotPanics(t, func() {
|
|
_ = audit.NewSlogSink(nil).Record(context.Background(), capture.AuditEntry{}, capture.AuditCentral)
|
|
})
|
|
}
|