Failed/rejected transactions logged with reason code
Why it matters
A failed authorization denial, insufficient-funds rejection, or rate-limit block is not a neutral event — it is a potential indicator of credential stuffing, account takeover, or fraud probing. If it is not logged with a reason code, your security team has no signal to detect these patterns. PCI-DSS 4.0 Req-10.2.1.7 explicitly requires logging of invalid logical access attempts. NIST 800-53 AU-2 requires logging of failed login attempts and failed authentication events. FINRA Rule 4511 requires complete books and records, which include rejected transactions. CWE-778 (Insufficient Logging) covers the failure to record security-relevant events. An attacker probing for accounts with insufficient-funds errors on micro-transactions to enumerate valid account IDs will leave no trace if rejections are silently swallowed.
Severity rationale
Low because failed-transaction logging is a detective control — its absence does not enable attacks directly, but it eliminates the forensic evidence needed to detect and investigate fraud after the fact.
Remediation
Wrap every failure path in src/services/transaction.ts with an explicit log write before throwing, and define a fixed set of reason codes as a TypeScript union or enum:
type FailReason =
| 'INSUFFICIENT_FUNDS'
| 'ACCOUNT_FROZEN'
| 'AUTHORIZATION_DENIED'
| 'INVALID_INPUT'
| 'RATE_LIMITED'
| 'ACCOUNT_NOT_FOUND';
async function logFailure(
userId: string, operation: string, amount: number, reason: FailReason
) {
await db('transaction_logs').insert({
user_id: userId, operation_type: operation, amount,
result: 'fail', reason_code: reason,
timestamp: new Date().toISOString()
});
}
// In executeTransfer:
if (account.balance < amount) {
await logFailure(userId, 'transfer', amount, 'INSUFFICIENT_FUNDS');
throw new TransactionError('Insufficient funds');
}
A catch-all block at the route level does not satisfy this requirement — each named failure condition needs its own reason_code recorded before the exception propagates.
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-778 — Insufficient Logging
- nist:rev5 · AU-2 — Event Logging — log failed access attempts and errors
- pci-dss:4.0 · Req 10.2.1.7 — Log all invalid logical access attempts
- external · FINRA-Rule-4511 — FINRA Rule 4511 — records of all transactions including rejections
Taxons
History
- 2026-04-18·v1.0.0·Initial import from finserv-audit-trail·automated