62 lines
3.0 KiB
Go
62 lines
3.0 KiB
Go
package kmsrotationconformance
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"strings"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestEvaluateRealKMSRequiresBothConsumptionModes(t *testing.T) {
|
||
|
|
t.Parallel()
|
||
|
|
capture, now := rotationFixture("KMS_HSM")
|
||
|
|
report := Evaluate(capture, "0.64.0-rc.1", "sha256:"+strings.Repeat("a", 64), now)
|
||
|
|
if report.Result != "PASS" || report.EvidenceScope != "REAL_KMS_HSM" || report.ExternalValidationRequired || report.RotationCount != 2 || len(report.ConsumptionModes) != 2 {
|
||
|
|
t.Fatalf("unexpected report: %+v", report)
|
||
|
|
}
|
||
|
|
capture.Rotations = capture.Rotations[:1]
|
||
|
|
report = Evaluate(capture, "0.64.0-rc.1", "sha256:"+strings.Repeat("a", 64), now)
|
||
|
|
if report.Result != "FAIL" || !report.ExternalValidationRequired {
|
||
|
|
t.Fatal("real KMS evidence without rolling restart mode was accepted")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestEvaluateLocalRotationPreservesExternalGate(t *testing.T) {
|
||
|
|
t.Parallel()
|
||
|
|
capture, now := rotationFixture("LOCAL")
|
||
|
|
capture.Environment = "LOCAL"
|
||
|
|
capture.Rotations = capture.Rotations[:1]
|
||
|
|
report := Evaluate(capture, "0.64.0-rc.1", "sha256:"+strings.Repeat("a", 64), now)
|
||
|
|
if report.Result != "PASS" || report.EvidenceScope != "LOCAL_SECRET" || !report.ExternalValidationRequired {
|
||
|
|
t.Fatalf("local evidence boundary changed: %+v", report)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestStrictKMSCaptureAndReport(t *testing.T) {
|
||
|
|
t.Parallel()
|
||
|
|
if _, err := ParseCapture([]byte(`{"schema_version":"kms-rotation-capture-v1","unknown":true}`)); err == nil {
|
||
|
|
t.Fatal("unknown capture field was accepted")
|
||
|
|
}
|
||
|
|
if _, err := ParseReport([]byte(`{"schema_version":"kms-rotation-conformance-report-v1","unknown":true}`)); err == nil {
|
||
|
|
t.Fatal("unknown report field was accepted")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func rotationFixture(source string) (Capture, time.Time) {
|
||
|
|
now := time.Date(2026, 8, 25, 12, 0, 0, 0, time.UTC)
|
||
|
|
rotation := func(secret, consumer, mode string, offset int) RotationEvidence {
|
||
|
|
return RotationEvidence{
|
||
|
|
SecretRef: secret, ConsumerRef: consumer, ConsumptionMode: mode, OldVersionRef: secret + ":v1", NewVersionRef: secret + ":v2",
|
||
|
|
StartedAt: now.Add(time.Duration(-10+offset) * time.Minute), CompletedAt: now.Add(time.Duration(-8+offset) * time.Minute),
|
||
|
|
NewCredentialVerified: true, OldCredentialRejected: true, BusinessContinuityVerified: true, SecretValueNotExported: true,
|
||
|
|
PreRotationProbeRef: "probe:before:" + secret, NewCredentialProbeRef: "probe:new:" + secret,
|
||
|
|
OldRejectionProbeRef: "probe:old-rejected:" + secret, ContinuityProbeRef: "probe:continuity:" + secret, AccessAuditRef: "audit:" + secret,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return Capture{
|
||
|
|
SchemaVersion: CaptureSchema, EvidenceSource: source, CandidateVersion: "0.64.0-rc.1", BuildDigest: "sha256:" + strings.Repeat("a", 64),
|
||
|
|
Environment: "STAGING", ProviderRef: "cloud-secret-manager:voice-platform", WorkloadIdentityRef: "workload-identity:voice-platform-api", ProviderAuditRef: "provider-audit:masked-001",
|
||
|
|
AttestedByRef: "security-reviewer:masked-001", AttestedAt: now.Add(-time.Minute), CollectedAt: now,
|
||
|
|
Rotations: []RotationEvidence{rotation("device-secret:terminal-001", "api", "DYNAMIC_READ", 0), rotation("provider-callback-secret", "callbacks", "ROLLING_RESTART", 3)},
|
||
|
|
}, now
|
||
|
|
}
|