Logs include before-and-after balance snapshots
Why it matters
Logging that a withdrawal of $500 occurred is not the same as being able to prove the account held $1,200 beforehand and $700 afterward. Without balance_before and balance_after captured atomically at execution time, your reconciliation process must recompute balances by replaying every prior transaction — a process that breaks the moment any log row is missing, corrupt, or out of order. NIST 800-53 AU-3 requires audit records to contain sufficient information to establish what happened; a transaction log with no balance context fails that standard because it cannot independently confirm whether the stated amount was valid at the moment it was applied. FINRA Rule 4511 and PCI-DSS v4.0 Req-10.2.2 (which enumerates required audit-log fields — user, event type, date/time, success/failure, origin, affected resource) together cover the content-of-audit-records expectation. CWE-1021 (Improper Restriction of Rendered UI Layers) is a secondary risk — UI showing a derived balance can diverge from the ledger if the log is the authoritative source and lacks before/after data.
Severity rationale
High because missing balance snapshots make it impossible to detect mid-session manipulation or correctly reconstruct account state after any data inconsistency without full transaction replay.
Remediation
Extend transaction_logs with two non-nullable decimal columns in db/migrations/ or prisma/schema.prisma:
ALTER TABLE transaction_logs
ADD COLUMN balance_before DECIMAL(19,4) NOT NULL,
ADD COLUMN balance_after DECIMAL(19,4) NOT NULL;
In your service, read the account balance inside the same database transaction before committing the operation:
async function executeWithdrawal(userId: string, amount: number) {
return db.transaction(async (trx) => {
const { balance } = await trx('accounts')
.where({ user_id: userId }).forUpdate().first();
const newBalance = balance - amount;
await trx('accounts').where({ user_id: userId }).update({ balance: newBalance });
await trx('transaction_logs').insert({
user_id: userId, operation_type: 'withdrawal',
amount, balance_before: balance, balance_after: newBalance,
timestamp: new Date().toISOString()
});
});
}
DetectionPro
The detection method for this pattern is part of AuditBuffet Pro.
The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the Financial Audit Trail Audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- cwe · CWE-1021 — Improper Restriction of Rendered UI Layers or Frames
- nist:rev5 · AU-3 — Content of Audit Records
- external · FINRA-Rule-4511 — FINRA Rule 4511 — General Requirements for Books and Records
- pci-dss:4.0 · Req 10.2.2 — Audit log records include user identification, event type, date/time, success/failure, origin, and affected resource
Taxons
History
- 2026-04-18·v1.0.0·Initial import from finserv-audit-trail·automated