3.4 KiB
3.4 KiB
Exception Application Audit Trail (policy.exception_applications)
Module
Policy
Status
IMPLEMENTED
Description
Records every instance of an exception being applied to a finding in a dedicated policy.exception_applications table, capturing exception ID, finding context, original and applied status, purl, vulnerability ID, and evaluation run ID. Exposed via ledger export for compliance.
Implementation Details
- ExceptionApplication Model:
src/Policy/__Libraries/StellaOps.Policy.Exceptions/Models/ExceptionApplication.cs- Sealed record with fields: Id (Guid), TenantId, ExceptionId, FindingId, VulnerabilityId, OriginalStatus, AppliedStatus, EffectName, EffectType, EvaluationRunId, PolicyBundleDigest, AppliedAt, Metadata
Create()static factory method enforces non-null ExceptionId/FindingId, accepts deterministic applicationId and appliedAt timestamps- Metadata stored as
ImmutableDictionary<string, string>for extensibility
- IExceptionApplicationRepository:
src/Policy/__Libraries/StellaOps.Policy.Exceptions/Repositories/IExceptionApplicationRepository.csRecordAsync(application)-- persists a single application recordRecordBatchAsync(applications)-- bulk persist for batch evaluation- Query by ExceptionId, FindingId, VulnerabilityId, EvaluationRunId, TimeRange
GetStatisticsAsync(tenantId, filter?)returnsExceptionApplicationStatistics(TotalApplications, UniqueExceptions, UniqueFindings, UniqueVulnerabilities, ByEffectType counts, ByAppliedStatus counts, EarliestApplication, LatestApplication)CountAsync(tenantId, filter?)for total count with optional filterExceptionApplicationFilterrecord supports paging (Limit/Offset), date range (FromDate/ToDate), and field filters
- PostgresExceptionApplicationRepository:
src/Policy/__Libraries/StellaOps.Policy.Exceptions/Repositories/PostgresExceptionApplicationRepository.cs-- Postgres persistence for thepolicy.exception_applicationstable - ExceptionEvaluator:
src/Policy/__Libraries/StellaOps.Policy.Exceptions/Services/ExceptionEvaluator.cs-- creates ExceptionApplication records when exceptions match findings during policy evaluation
E2E Test Plan
- Apply exception to finding; query
GetByExceptionIdAsync(tenantId, exceptionId); verify record includes correct ExceptionId, FindingId, OriginalStatus, AppliedStatus, EffectName, EffectType - Apply exception with VulnerabilityId; query
GetByVulnerabilityIdAsync(tenantId, vulnId); verify record returned with correct VulnerabilityId - Apply exception during batch evaluation; verify EvaluationRunId is populated; query
GetByEvaluationRunIdAsync(tenantId, runId)and verify all applications for that run - Apply exception; verify AppliedAt timestamp matches evaluation time (deterministic)
- Apply exception with PolicyBundleDigest; verify digest is recorded in the application record
- Call
RecordBatchAsyncwith 5 applications; verify all 5 are persisted - Call
GetByTimeRangeAsync(tenantId, from, to)with a range encompassing 3 applications; verify exactly 3 returned - Call
GetStatisticsAsync(tenantId)after 10 applications across 3 exceptions and 5 findings; verify TotalApplications=10, UniqueExceptions=3, UniqueFindings=5, ByEffectType counts sum to 10 - Call
CountAsync(tenantId, filter)with EffectType="suppress" filter; verify count matches expected