nip/tests/test_security_event_logging...

444 lines
13 KiB
Nim

## tests/test_security_event_logging.nim
## Comprehensive tests for security event logging system (Task 11.1d)
import std/[unittest, times, json, os, strutils, options]
import ../src/nimpak/security/event_logger
import ../src/nimpak/security/revocation_manager
import ../src/nimpak/cli/audit_commands
suite "Security Event Logging System":
let testLogPath = "/tmp/nip_test_security.log"
let testCasPath = "/tmp/nip_test_cas"
let testCrlPath = "/tmp/nip_test_crl"
setup:
# Clean up test files
if fileExists(testLogPath):
removeFile(testLogPath)
if dirExists(testCasPath):
removeDir(testCasPath)
if dirExists(testCrlPath):
removeDir(testCrlPath)
createDir(testCasPath)
createDir(testCrlPath)
teardown:
# Clean up test files
if fileExists(testLogPath):
removeFile(testLogPath)
if dirExists(testCasPath):
removeDir(testCasPath)
if dirExists(testCrlPath):
removeDir(testCrlPath)
test "Security Event Logger Creation":
let logger = newSecurityEventLogger(testLogPath, testCasPath)
check:
logger.logPath == testLogPath
logger.casStore == testCasPath
logger.signingKey.isNone()
logger.lastEventHash == ""
logger.eventCounter == 0
test "Security Event Creation":
let event = createSecurityEvent(
EventKeyRevocation,
SeverityCritical,
"test-source",
"Test revocation event",
%*{"key_id": "test-key-123"}
)
check:
event.eventType == EventKeyRevocation
event.severity == SeverityCritical
event.source == "test-source"
event.message == "Test revocation event"
event.metadata["key_id"].getStr() == "test-key-123"
event.hashChainPrev == ""
event.hashChainCurrent == ""
event.signature.isNone()
test "Event Hash Calculation":
let event = createSecurityEvent(
EventSignatureVerification,
SeverityInfo,
"verifier",
"Signature verified",
%*{"package": "test-package"}
)
let hash = calculateEventHash(event)
check:
hash.startsWith("blake3-")
hash.len > 10
test "Security Event Logging":
var logger = newSecurityEventLogger(testLogPath, testCasPath)
var event = createSecurityEvent(
EventPackageVerification,
SeverityInfo,
"package-manager",
"Package verified successfully",
%*{"package": "htop", "version": "3.2.1"}
)
logger.logSecurityEvent(event)
check:
fileExists(testLogPath)
logger.eventCounter == 1
logger.lastEventHash == event.hashChainCurrent
event.hashChainPrev == "" # First event has no previous hash
event.hashChainCurrent != ""
test "Hash Chain Continuity":
var logger = newSecurityEventLogger(testLogPath, testCasPath)
# Log first event
var event1 = createSecurityEvent(EventKeyGeneration, SeverityInfo, "key-manager", "Key generated")
logger.logSecurityEvent(event1)
# Log second event
var event2 = createSecurityEvent(EventKeyRevocation, SeverityWarning, "key-manager", "Key revoked")
logger.logSecurityEvent(event2)
check:
event1.hashChainPrev == ""
event2.hashChainPrev == event1.hashChainCurrent
event2.hashChainCurrent != event1.hashChainCurrent
logger.eventCounter == 2
test "Revocation Event Logging":
var logger = newSecurityEventLogger(testLogPath, testCasPath)
let revocation = RevocationEvent(
keyId: "test-key-456",
reason: ReasonKeyCompromise,
reasonText: "Key compromised in security incident",
revocationDate: now().utc(),
supersededBy: some("test-key-789"),
affectedPackages: @["package1", "package2"],
emergencyRevocation: true,
responseActions: @["immediate_crl_update", "package_re_signing"]
)
logger.logKeyRevocation(revocation)
check:
fileExists(testLogPath)
logger.eventCounter == 1
test "Emergency Revocation Logging":
var logger = newSecurityEventLogger(testLogPath, testCasPath)
logger.logEmergencyRevocation("emergency-key-123", "Suspected compromise", @["critical-package"])
check:
fileExists(testLogPath)
logger.eventCounter == 1
test "Key Rollover Logging":
var logger = newSecurityEventLogger(testLogPath, testCasPath)
let rollover = RolloverEvent(
oldKeyId: "old-key-123",
newKeyId: "new-key-456",
rolloverType: "scheduled",
overlapPeriod: "30d",
affectedRepositories: @["stable", "testing"],
validationResults: %*{"packages_re_signed": 150, "errors": []}
)
logger.logKeyRollover(rollover)
check:
fileExists(testLogPath)
logger.eventCounter == 1
test "Signature Verification Logging":
var logger = newSecurityEventLogger(testLogPath, testCasPath)
# Log successful verification
logger.logSignatureVerification("test-package", "key-123", true)
# Log failed verification
logger.logSignatureVerification("bad-package", "key-456", false, "Invalid signature")
check:
fileExists(testLogPath)
logger.eventCounter == 2
test "Trust Violation Logging":
var logger = newSecurityEventLogger(testLogPath, testCasPath)
logger.logTrustViolation("suspicious-package", "Untrusted key used", "untrusted-key-789")
check:
fileExists(testLogPath)
logger.eventCounter == 1
test "CRL Update Logging":
var logger = newSecurityEventLogger(testLogPath, testCasPath)
logger.logCRLUpdate("https://crl.example.com/nexus.crl", @["revoked-key-1", "revoked-key-2"], true)
check:
fileExists(testLogPath)
logger.eventCounter == 1
test "Security Incident Logging":
var logger = newSecurityEventLogger(testLogPath, testCasPath)
logger.logSecurityIncident(
"key_compromise",
"Multiple keys compromised in coordinated attack",
@["repository-server", "signing-server"],
@["revoke_all_keys", "regenerate_infrastructure", "notify_users"]
)
check:
fileExists(testLogPath)
logger.eventCounter == 1
suite "Revocation Manager":
let testCrlPath = "/tmp/nip_test_crl"
let testCasPath = "/tmp/nip_test_cas"
let testLogPath = "/tmp/nip_test_security.log"
setup:
if dirExists(testCrlPath):
removeDir(testCrlPath)
if dirExists(testCasPath):
removeDir(testCasPath)
if fileExists(testLogPath):
removeFile(testLogPath)
createDir(testCrlPath)
createDir(testCasPath)
teardown:
if dirExists(testCrlPath):
removeDir(testCrlPath)
if dirExists(testCasPath):
removeDir(testCasPath)
if fileExists(testLogPath):
removeFile(testLogPath)
test "Revocation Manager Creation":
let logger = newSecurityEventLogger(testLogPath, testCasPath)
let manager = newRevocationManager(testC, testCasPath, logger)
check:
manager.crlPath == testCrlPath
manager.casStore == testCasPath
manager.distributionUrls.len == 0
manager.policies.len == 0
test "Default Rollover Policies":
let policies = getDefaultPolicies()
check:
policies.hasKey("ed25519")
policies.hasKey("dilithium")
policies["ed25519"].algorithm == "ed25519"
policies["ed25519"].quantumResistant == false
policies["dilithium"].quantumResistant == true
test "Emergency Revocation":
let logger = newSecurityEventLogger(testLogPath, testCasPath)
var manager = newRevocationManager(testCrlPath, testCasPath, logger)
let result = manager.emergencyRevocation(
"compromised-key-123",
"Key compromised in security breach",
@["critical-package-1", "critical-package-2"]
)
check:
result.isOk()
fileExists(testCrlPath / "revocation_list.nexus")
test "Scheduled Key Rollover":
let logger = newSecurityEventLogger(testLogPath, testCasPath)
var manager = newRevocationManager(testCrlPath, testCasPath, logger)
# Set up rollover policy
let policy = RolloverPolicy(
algorithm: "ed25519",
keySize: 256,
overlapPeriod: initDuration(days = 30),
gracePeriod: initDuration(days = 7),
autoRolloverInterval: initDuration(days = 365),
emergencyRolloverEnabled: true,
quantumResistant: false
)
manager.setRolloverPolicy("ed25519", policy)
let result = manager.scheduleKeyRollover("old-key-123", "ed25519", @["stable", "testing"])
check:
result.isOk()
result.get().rolloverType == "scheduled"
result.get().overlapPeriod == initDuration(days = 30)
test "Quantum Transition Planning":
let logger = newSecurityEventLogger(testLogPath, testCasPath)
var manager = newRevocationManager(testCrlPath, testCasPath, logger)
let result = manager.planQuantumTransition("classical-key-123", "dilithium")
check:
result.isOk()
result.get().rolloverType == "quantum-transition"
result.get().overlapPeriod == initDuration(days = 60)
result.get().affectedRepositories == @["all"]
test "Offline Revocation Package":
let logger = newSecurityEventLogger(testLogPath, testCasPath)
let manager = newRevocationManager(testCrlPath, testCasPath, logger)
let result = manager.createOfflineRevocationPackage(@["offline-key-1", "offline-key-2"])
check:
result.isOk()
fileExists(result.get())
suite "CLI Audit Commands":
test "Audit Command Parsing - Log":
let result = parseAuditCommand(@["log", "--follow", "--format", "json"])
check:
result.isOk()
result.get().command == AuditLog
result.get().follow == true
result.get().format == "json"
test "Audit Command Parsing - Keys":
let result = parseAuditCommand(@["keys", "--format", "table", "--verbose"])
check:
result.isOk()
result.get().command == AuditKeys
result.get().format == "table"
result.get().verbose == true
test "Audit Command Parsing - Packages":
let result = parseAuditCommand(@["packages", "--package", "htop", "--severity", "error"])
check:
result.isOk()
result.get().command == AuditPackages
result.get().packageName.isSome()
result.get().packageName.get() == "htop"
result.get().severity.isSome()
result.get().severity.get() == SeverityError
test "Audit Command Parsing - Integrity":
let result = parseAuditCommand(@["integrity", "--output", "/tmp/integrity_report.json"])
check:
result.isOk()
result.get().command == AuditIntegrity
result.get().outputFile.isSome()
result.get().outputFile.get() == "/tmp/integrity_report.json"
test "Invalid Audit Command":
let result = parseAuditCommand(@["invalid-command"])
check:
result.isErr()
result.errValue.contains("Unknown audit command")
test "Missing Required Arguments":
let result = parseAuditCommand(@["log", "--format"])
check:
result.isErr()
result.errValue.contains("--format requires a value")
suite "Integration Tests":
let testLogPath = "/tmp/nip_integration_security.log"
let testCasPath = "/tmp/nip_integration_cas"
let testCrlPath = "/tmp/nip_integration_crl"
setup:
if fileExists(testLogPath):
removeFile(testLogPath)
if dirExists(testCasPath):
removeDir(testCasPath)
if dirExists(testCrlPath):
removeDir(testCrlPath)
createDir(testCasPath)
createDir(testCrlPath)
teardown:
if fileExists(testLogPath):
removeFile(testLogPath)
if dirExists(testCasPath):
removeDir(testCasPath)
if dirExists(testCrlPath):
removeDir(testCrlPath)
test "Complete Security Workflow":
# Initialize components
var logger = newSecurityEventLogger(testLogPath, testCasPath)
var manager = newRevocationManager(testCrlPath, testCasPath, logger)
# Set up policies
let policies = getDefaultPolicies()
for algorithm, policy in policies:
manager.setRolloverPolicy(algorithm, policy)
# Simulate security events
logger.logSignatureVerification("package-1", "key-123", true)
logger.logSignatureVerification("package-2", "key-456", false, "Invalid signature")
# Perform emergency revocation
let revocationResult = manager.emergencyRevocation("key-456", "Compromised key", @["package-2"])
check revocationResult.isOk()
# Schedule rollover
let rolloverResult = manager.scheduleKeyRollover("key-123", "ed25519", @["stable"])
check rolloverResult.isOk()
# Verify log integrity
let integrityResult = logger.verifyLogIntegrity()
check integrityResult.valid
# Check that all events were logged
check:
logger.eventCounter >= 4 # At least 4 events logged
fileExists(testLogPath)
fileExists(testCrlPath / "revocation_list.nexus")
test "CLI Integration":
# Set up test environment
putEnv("NIP_SECURITY_LOG", testLogPath)
putEnv("NIP_CAS_STORE", testCasPath)
putEnv("NIP_CRL_PATH", testCrlPath)
# Initialize and log some events
var logger = newSecurityEventLogger(testLogPath, testCasPath)
logger.logSignatureVerification("test-package", "test-key", true)
logger.logTrustViolation("bad-package", "Untrusted source", "bad-key")
# Test CLI commands
let logResult = executeAuditCommand(@["log", "--format", "json"])
check logResult.isOk()
let integrityResult = executeAuditCommand(@["integrity", "--format", "table"])
check integrityResult.isOk()
when isMainModule:
# Run the tests
echo "Running Security Event Logging System Tests..."
echo "=" .repeat(50)