A transaction log that records transfers but silently omits login events, fee adjustments, and account cancellations is not a complete audit trail — it is a partial record that creates blind spots regulators will find. NIST 800-53 AU-2 requires organizations to determine which event types are auditable and to coordinate the audit function with other entities requiring audit-related information; login/logout events are explicitly called out. PCI-DSS 4.0 Req-10.2.1 enumerates specific event types that must be logged: all individual user access to cardholder data, all actions taken by any individual with root or administrative privileges, and all invalid logical access attempts. FINRA Rule 4511 requires complete books and records. CWE-778 (Insufficient Logging) is the vulnerability. An attacker who escalates privileges via a path that does not trigger a log event leaves no forensic evidence of the intrusion.
Medium because partial action coverage creates audit gaps that satisfy automated checks while concealing specific attack paths — particularly privilege escalation routes — from forensic investigation.
Instrument every significant user action through a centralized logger in src/services/audit-logger.ts to ensure consistent coverage:
const AUDITABLE_OPERATIONS = [
'login', 'logout', 'transfer', 'withdrawal', 'deposit',
'adjustment', 'cancellation', 'reversal', 'fee_change'
] as const;
export async function logAction(
userId: string,
operation: typeof AUDITABLE_OPERATIONS[number],
details: Record<string, unknown>
) {
await db('transaction_logs').insert({
user_id: userId,
operation_type: operation,
details: JSON.stringify(details),
timestamp: new Date().toISOString()
});
}
Call logAction in each route — for login events, call it after successful credential verification, not only on successful session creation, so failed login attempts are also captured.
ID: finserv-audit-trail.retention-compliance.complete-action-logging
Severity: medium
What to look for: Enumerate all user-facing operation endpoints (at minimum: login, logout, transfer, withdrawal, deposit, adjustment, cancellation, reversal, fee modification). Count every endpoint and classify each as "logged" or "not logged." Quote the actual operation_type values found in logging calls. At least 6 distinct action types should be recognized. An endpoint that performs a financial operation without writing to the audit log does not count as pass.
Pass criteria: At least 90% of significant user action endpoints are logged with operation type and outcome. Count all action types — at least 6 distinct types must be present (login, transfer, withdrawal, deposit, adjustment, cancellation). Report the ratio even on pass (e.g., "8 of 9 action types logged, including login/logout").
Fail criteria: Fewer than 90% of operation types have logging, or fewer than 6 distinct action types are logged, or login/logout events are not recorded.
Skip (N/A) when: Never — comprehensive action logging is required for compliance.
Detail on fail: Name the action types not logged. Example: "5 of 8 action types logged — fee adjustments, manual corrections, and logout are missing." or "Login/logout events are not recorded — 0 auth-event entries found in audit trail.".
Cross-reference: Check finserv-audit-trail.transaction-logging.transaction-immutable-logging for transaction-specific logging, and finserv-audit-trail.retention-compliance.config-change-logging for admin config changes.
Remediation: Ensure all user-initiated financial actions log events (in src/services/audit-logger.ts or equivalent):
async function adjustFee(userId, accountId, newFee, reason) {
const oldFee = await db.accounts.findOne({ accountId }).select('monthly_fee');
await db.accounts.update({ accountId }, { monthly_fee: newFee });
// Log the adjustment
await db.transactionLogs.create({
userId,
operationType: 'fee_adjustment',
details: {
accountId,
oldFee,
newFee,
reason,
adjustedBy: userId
},
timestamp: new Date().toISOString()
});
}
// Similarly for login events
app.post('/api/login', async (req, res) => {
const user = await authenticateUser(req.body.email, req.body.password);
if (user) {
await db.transactionLogs.create({
userId: user.id,
operationType: 'login',
timestamp: new Date().toISOString()
});
}
});