feat(audit): SHA-256 hash chain over the central AuditLog (ScadaBridge#41)
Audit roadmap T1 (alog.md §11.4): one chain per UTC calendar month of OccurredAtUtc. Each chained row carries ChainSeq (gap-free within its month) and RowHash = SHA-256(prev.RowHash || canonical(row)); the v1 canonical form (AuditChainHasher) is pinned by a known-answer vector computed independently. - Migration AddAuditLogHashChain: nullable ChainSeq/RowHash on dbo.AuditLog (metadata-only add), AuditLogChainHead (per-month chain end + partition-purge checkpoint anchor), AuditLogChainTombstone, two partition-aligned filtered indexes, scadabridge_audit_chainer role with a column-level UPDATE grant. Production script docs/plans/sql/AddAuditLogHashChain.sql. - AuditLogHashChain chains unchained rows oldest month first under an exclusive sp_getapplock (zero timeout, so a dual-active central pair still yields one chain), with an optimistic head check and a ChainSeq IS NULL row-count check that roll a pass back instead of leaving a gap. The ingest path is untouched. - Purges keep the chain verifiable: the partition switch records the month's checkpoint anchor and drops its tombstones; the per-channel DELETE writes a tombstone per chained row in the same transaction (OUTPUT INTO). Both, and the SourceNode backfill, hold the chain lock; the backfill skips chained rows. The switch staging table gains the two new columns. - AuditChainVerifier reports the first break (partition, ChainSeq, EventId, kind) for edited, deleted, reordered rows and tail truncation. - AuditLogHashChainService (central, default OFF via AuditLog:HashChain). Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
+6
-1
@@ -32,7 +32,7 @@ namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests;
|
||||
/// Additionally, the scan is line-oriented: DML where the keyword and table name appear
|
||||
/// on separate lines is an accepted, undetected edge case.
|
||||
///
|
||||
/// <b>Allow-list.</b> Two narrow maintenance-path exemptions carry the exact
|
||||
/// <b>Allow-list.</b> Three narrow exemptions carry the exact
|
||||
/// <see cref="AuditPurgeAllowedMarker"/> trailing comment:
|
||||
/// <list type="bullet">
|
||||
/// <item><description>
|
||||
@@ -47,6 +47,11 @@ namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests;
|
||||
/// one-time ops procedure; the append-only invariant still applies to all
|
||||
/// other columns and all other UPDATE forms.
|
||||
/// </description></item>
|
||||
/// <item><description>
|
||||
/// <c>AuditLogHashChain.WriteLinksAsync</c> (ScadaBridge#41): the chaining job's
|
||||
/// <c>UPDATE dbo.AuditLog SET ChainSeq, RowHash</c>, guarded by
|
||||
/// <c>ChainSeq IS NULL</c> — it only ever fills the two hash-chain link columns once.
|
||||
/// </description></item>
|
||||
/// </list>
|
||||
/// The allow-list is applied in the file-scan test only
|
||||
/// (<see cref="ConfigurationDatabase_ShouldNotContainAuditLogMutations"/>) — the
|
||||
|
||||
Reference in New Issue
Block a user