Race Condition Vulnerabilities in Financial Transaction Processing Systems

Critical Risk Business Logic
race-conditionfinancial-transactionsconcurrencydouble-spendingaccount-balancetransaction-limitsdata-integrity

What it is

A critical vulnerability where concurrent processing of financial transactions can lead to race conditions that allow double-spending, inconsistent account balances, unauthorized fund transfers, or bypassing transaction limits. These issues arise when multiple threads or processes access shared financial data simultaneously without proper synchronization, potentially causing significant financial losses and data integrity violations.

// VULNERABLE: Financial transaction processing with race conditions app.post('/api/transfer', async (req, res) => { const { fromAccount, toAccount, amount } = req.body; try { // PROBLEM: Non-atomic balance checks const from = await Account.findById(fromAccount); const to = await Account.findById(toAccount); if (from.balance < amount) { return res.status(400).json({ error: 'Insufficient funds' }); } // RACE CONDITION: Separate balance updates from.balance -= amount; to.balance += amount; await from.save(); await to.save(); res.json({ success: true }); } catch (error) { res.status(500).json({ error: error.message }); } });
// SECURE: Race-condition-safe financial transaction processing const { body, validationResult } = require('express-validator'); const { v4: uuidv4 } = require('uuid'); app.post('/api/transfer', [ body('fromAccountId').isUUID().withMessage('Invalid from account ID'), body('toAccountId').isUUID().withMessage('Invalid to account ID'), body('amount').isFloat({ min: 0.01 }).withMessage('Amount must be positive'), body('description').optional().isLength({ max: 255 }).withMessage('Description too long'), body('idempotencyKey').optional().isUUID().withMessage('Invalid idempotency key') ], async (req, res) => { try { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } const { fromAccountId, toAccountId, amount, description, idempotencyKey } = req.body; // Generate idempotency key if not provided const finalIdempotencyKey = idempotencyKey || uuidv4(); // Initialize secure financial processor const processor = new SecureFinancialProcessor(); const idempotencyManager = new IdempotencyManager(); // Check for duplicate transaction const existingTransaction = await idempotencyManager.checkExistingOperation( finalIdempotencyKey, 'transfer' ); if (existingTransaction.exists) { return res.json({ success: true, ...existingTransaction.result, message: 'Transaction already processed', isIdempotent: true }); } // Process transfer with atomic operations and distributed locking const transferResult = await processor.processSecureTransfer({ fromAccountId, toAccountId, amount: parseFloat(amount), description, userId: req.user.id }, finalIdempotencyKey); // Log successful transfer console.info('Transfer completed successfully', { transactionId: transferResult.transactionId, fromAccountId, toAccountId, amount, userId: req.user.id, timestamp: new Date().toISOString() }); res.json({ success: true, transactionId: transferResult.transactionId, fromBalance: transferResult.fromBalance, toBalance: transferResult.toBalance, amount: parseFloat(amount), processedAt: new Date().toISOString(), isIdempotent: false }); } catch (error) { console.error('Transfer processing error:', { error: error.message, stack: error.stack, fromAccountId: req.body.fromAccountId, toAccountId: req.body.toAccountId, amount: req.body.amount, userId: req.user?.id, timestamp: new Date().toISOString() }); // Determine appropriate error response const statusCode = error.message.includes('Insufficient funds') ? 400 : error.message.includes('not found') ? 404 : error.message.includes('limit') ? 429 : 500; res.status(statusCode).json({ error: error.message, timestamp: new Date().toISOString(), requestId: finalIdempotencyKey }); } } );

💡 Why This Fix Works

The secure implementation uses distributed locking to prevent race conditions, implements atomic database transactions with proper isolation levels, includes comprehensive idempotency protection to prevent duplicate processing, uses optimistic locking with version control for data consistency, and provides detailed audit trails for all financial operations. The system ensures that concurrent transactions cannot create inconsistent account states or bypass financial limits.

Why it happens

Financial systems that process transactions without proper locking mechanisms allow attackers to initiate multiple simultaneous transactions that reference the same account balance, potentially spending funds multiple times before the balance is updated.

Root causes

Double-Spending Through Concurrent Transaction Processing

Financial systems that process transactions without proper locking mechanisms allow attackers to initiate multiple simultaneous transactions that reference the same account balance, potentially spending funds multiple times before the balance is updated.

Preview example – JAVASCRIPT
// VULNERABLE: Concurrent transaction processing without locking
const express = require('express');
const app = express();

// PROBLEM: Transaction processing without atomic operations
app.post('/api/transfer', async (req, res) => {
    const { fromAccountId, toAccountId, amount } = req.body;
    
    try {
        // VULNERABLE: Non-atomic balance checks and updates
        const fromAccount = await Account.findById(fromAccountId);
        const toAccount = await Account.findById(toAccountId);
        
        if (!fromAccount || !toAccount) {
            return res.status(404).json({ error: 'Account not found' });
        }
        
        // PROBLEM 1: Check-then-act race condition
        if (fromAccount.balance < amount) {
            return res.status(400).json({ error: 'Insufficient funds' });
        }
        
        // RACE CONDITION: Balance can change between check and update
        // Multiple concurrent requests can pass the balance check
        
        // PROBLEM 2: Non-atomic balance updates
        fromAccount.balance -= amount;
        toAccount.balance += amount;
        
        // VULNERABLE: Separate save operations
        await fromAccount.save();
        await toAccount.save();
        
        // PROBLEM 3: Transaction record created after balance updates
        const transaction = await Transaction.create({
            fromAccountId,
            toAccountId,
            amount,
            type: 'transfer',
            status: 'completed',
            timestamp: new Date()
        });
        
        res.json({
            success: true,
            transactionId: transaction.id,
            newBalance: fromAccount.balance
        });
        
    } catch (error) {
        res.status(500).json({ error: 'Transfer failed' });
    }
});

// VULNERABLE: Withdrawal processing with race conditions
app.post('/api/withdraw', async (req, res) => {
    const { accountId, amount } = req.body;
    
    try {
        const account = await Account.findById(accountId);
        
        if (!account) {
            return res.status(404).json({ error: 'Account not found' });
        }
        
        // PROBLEM: Daily limit check without atomic operations
        const today = new Date().toDateString();
        const todayWithdrawals = await Transaction.aggregate([
            {
                $match: {
                    fromAccountId: accountId,
                    type: 'withdrawal',
                    timestamp: {
                        $gte: new Date(today),
                        $lt: new Date(new Date(today).getTime() + 24 * 60 * 60 * 1000)
                    }
                }
            },
            {
                $group: {
                    _id: null,
                    totalWithdrawn: { $sum: '$amount' }
                }
            }
        ]);
        
        const todayTotal = todayWithdrawals[0]?.totalWithdrawn || 0;
        const dailyLimit = account.dailyWithdrawalLimit || 1000;
        
        // RACE CONDITION: Multiple withdrawals can bypass daily limit
        if (todayTotal + amount > dailyLimit) {
            return res.status(400).json({ error: 'Daily withdrawal limit exceeded' });
        }
        
        // RACE CONDITION: Balance check and update not atomic
        if (account.balance < amount) {
            return res.status(400).json({ error: 'Insufficient funds' });
        }
        
        account.balance -= amount;
        await account.save();
        
        const transaction = await Transaction.create({
            fromAccountId: accountId,
            amount,
            type: 'withdrawal',
            status: 'completed',
            timestamp: new Date()
        });
        
        res.json({
            success: true,
            transactionId: transaction.id,
            newBalance: account.balance
        });
        
    } catch (error) {
        res.status(500).json({ error: 'Withdrawal failed' });
    }
});

// Attack scenarios:
// 1. Submit multiple simultaneous transfers from same account
// 2. Concurrent withdrawals to bypass daily limits
// 3. Rapid-fire transactions during balance checks
// 4. Race conditions during account closure/freeze
// 5. Concurrent credit/debit operations

Credit Card Payment Processing Race Conditions

Payment processing systems that handle credit card transactions without proper concurrency controls can allow duplicate charges, bypassing credit limits, or processing payments after card cancellation through race conditions in authorization and capture flows.

Preview example – JAVASCRIPT
// VULNERABLE: Credit card processing with race condition flaws
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

class PaymentProcessor {
    constructor() {
        this.processingPayments = new Set(); // Insufficient for distributed systems
    }
    
    // VULNERABLE: Payment processing without proper locking
    async processPayment(paymentData) {
        const {
            cardId,
            amount,
            orderId,
            customerId
        } = paymentData;
        
        try {
            // PROBLEM 1: Simple duplicate prevention (not distributed)
            const paymentKey = `${cardId}_${orderId}_${amount}`;
            
            if (this.processingPayments.has(paymentKey)) {
                throw new Error('Payment already processing');
            }
            
            this.processingPayments.add(paymentKey);
            
            // PROBLEM 2: Non-atomic credit limit checking
            const customer = await Customer.findById(customerId);
            const card = await CreditCard.findById(cardId);
            
            if (!card || card.status !== 'active') {
                throw new Error('Invalid or inactive card');
            }
            
            // RACE CONDITION: Credit limit check not atomic
            const pendingCharges = await Transaction.aggregate([
                {
                    $match: {
                        cardId: cardId,
                        status: 'pending',
                        createdAt: { $gte: new Date(Date.now() - 24 * 60 * 60 * 1000) }
                    }
                },
                {
                    $group: {
                        _id: null,
                        totalPending: { $sum: '$amount' }
                    }
                }
            ]);
            
            const currentPending = pendingCharges[0]?.totalPending || 0;
            const availableCredit = card.creditLimit - card.currentBalance - currentPending;
            
            // VULNERABLE: Multiple transactions can pass this check simultaneously
            if (amount > availableCredit) {
                throw new Error('Insufficient credit available');
            }
            
            // PROBLEM 3: External API call without transaction context
            const paymentIntent = await stripe.paymentIntents.create({
                amount: amount * 100, // Convert to cents
                currency: 'usd',
                payment_method: card.stripePaymentMethodId,
                customer: customer.stripeCustomerId,
                confirm: true,
                metadata: {
                    orderId: orderId,
                    cardId: cardId
                }
            });
            
            // PROBLEM 4: Database updates after external payment
            const transaction = await Transaction.create({
                cardId,
                customerId,
                orderId,
                amount,
                stripePaymentIntentId: paymentIntent.id,
                status: paymentIntent.status === 'succeeded' ? 'completed' : 'pending',
                createdAt: new Date()
            });
            
            // RACE CONDITION: Card balance update not atomic
            if (paymentIntent.status === 'succeeded') {
                card.currentBalance += amount;
                await card.save();
            }
            
            // Clean up in-memory tracking
            this.processingPayments.delete(paymentKey);
            
            return {
                success: true,
                transactionId: transaction.id,
                paymentIntentId: paymentIntent.id,
                status: transaction.status
            };
            
        } catch (error) {
            this.processingPayments.delete(paymentKey);
            throw error;
        }
    }
    
    // VULNERABLE: Refund processing with race conditions
    async processRefund(transactionId, amount, reason) {
        try {
            const transaction = await Transaction.findById(transactionId);
            
            if (!transaction || transaction.status !== 'completed') {
                throw new Error('Transaction not found or not eligible for refund');
            }
            
            // PROBLEM: Refund amount validation without locking
            const existingRefunds = await Refund.aggregate([
                {
                    $match: {
                        transactionId: transactionId,
                        status: 'completed'
                    }
                },
                {
                    $group: {
                        _id: null,
                        totalRefunded: { $sum: '$amount' }
                    }
                }
            ]);
            
            const totalRefunded = existingRefunds[0]?.totalRefunded || 0;
            
            // RACE CONDITION: Multiple refunds can exceed original amount
            if (totalRefunded + amount > transaction.amount) {
                throw new Error('Refund amount exceeds transaction amount');
            }
            
            // External refund processing
            const stripeRefund = await stripe.refunds.create({
                payment_intent: transaction.stripePaymentIntentId,
                amount: amount * 100,
                reason: reason
            });
            
            // PROBLEM: Card balance update without atomic operation
            const card = await CreditCard.findById(transaction.cardId);
            card.currentBalance -= amount;
            await card.save();
            
            const refund = await Refund.create({
                transactionId,
                amount,
                reason,
                stripeRefundId: stripeRefund.id,
                status: stripeRefund.status === 'succeeded' ? 'completed' : 'pending',
                createdAt: new Date()
            });
            
            return {
                success: true,
                refundId: refund.id,
                status: refund.status
            };
            
        } catch (error) {
            throw error;
        }
    }
}

// VULNERABLE: API endpoints without concurrency protection
const paymentProcessor = new PaymentProcessor();

app.post('/api/payments/charge', async (req, res) => {
    const { cardId, amount, orderId } = req.body;
    
    try {
        // PROBLEM: No request deduplication or idempotency
        const result = await paymentProcessor.processPayment({
            cardId,
            amount: parseFloat(amount),
            orderId,
            customerId: req.user.id
        });
        
        res.json(result);
        
    } catch (error) {
        res.status(400).json({ error: error.message });
    }
});

// Attack scenarios:
// 1. Submit multiple identical payment requests simultaneously
// 2. Concurrent payments to bypass credit limits
// 3. Race conditions during card status changes
// 4. Multiple refunds for same transaction
// 5. Concurrent payment and refund operations

Inventory and Stock Management Race Conditions

E-commerce systems that manage product inventory without proper concurrency controls can allow overselling, negative stock levels, or inventory inconsistencies when multiple customers purchase the same limited-stock items simultaneously.

Preview example – JAVASCRIPT
// VULNERABLE: Inventory management with race condition vulnerabilities
const express = require('express');
const app = express();

class InventoryManager {
    constructor() {
        this.reservationTimeouts = new Map();
    }
    
    // VULNERABLE: Stock checking and reservation without atomic operations
    async reserveStock(productId, quantity, customerId) {
        try {
            const product = await Product.findById(productId);
            
            if (!product) {
                throw new Error('Product not found');
            }
            
            // PROBLEM 1: Non-atomic stock availability check
            const availableStock = await this.getAvailableStock(productId);
            
            // RACE CONDITION: Multiple customers can pass this check
            if (availableStock < quantity) {
                throw new Error('Insufficient stock available');
            }
            
            // PROBLEM 2: Reservation without atomic stock update
            const reservation = await StockReservation.create({
                productId,
                customerId,
                quantity,
                status: 'pending',
                expiresAt: new Date(Date.now() + 15 * 60 * 1000), // 15 minutes
                createdAt: new Date()
            });
            
            // VULNERABLE: Stock update after reservation creation
            product.reservedStock += quantity;
            await product.save();
            
            // Set timeout for automatic release
            this.setReservationTimeout(reservation.id, 15 * 60 * 1000);
            
            return {
                reservationId: reservation.id,
                expiresAt: reservation.expiresAt
            };
            
        } catch (error) {
            throw error;
        }
    }
    
    // PROBLEM: Stock calculation without considering concurrent operations
    async getAvailableStock(productId) {
        const product = await Product.findById(productId);
        
        // VULNERABLE: Aggregation without locking
        const activeReservations = await StockReservation.aggregate([
            {
                $match: {
                    productId: productId,
                    status: 'pending',
                    expiresAt: { $gt: new Date() }
                }
            },
            {
                $group: {
                    _id: null,
                    totalReserved: { $sum: '$quantity' }
                }
            }
        ]);
        
        const reservedQuantity = activeReservations[0]?.totalReserved || 0;
        
        // RACE CONDITION: Stock can change between calculation and use
        return product.totalStock - product.soldStock - reservedQuantity;
    }
    
    // VULNERABLE: Purchase confirmation without atomic operations
    async confirmPurchase(reservationId, paymentId) {
        try {
            const reservation = await StockReservation.findById(reservationId);
            
            if (!reservation || reservation.status !== 'pending') {
                throw new Error('Invalid or expired reservation');
            }
            
            // PROBLEM: Payment verification without transaction context
            const payment = await Payment.findById(paymentId);
            
            if (!payment || payment.status !== 'completed') {
                throw new Error('Payment not completed');
            }
            
            // RACE CONDITION: Multiple confirmations possible
            const product = await Product.findById(reservation.productId);
            
            // PROBLEM: Non-atomic stock updates
            product.soldStock += reservation.quantity;
            product.reservedStock -= reservation.quantity;
            
            await product.save();
            
            // Update reservation status
            reservation.status = 'confirmed';
            reservation.confirmedAt = new Date();
            reservation.paymentId = paymentId;
            
            await reservation.save();
            
            // Clear timeout
            if (this.reservationTimeouts.has(reservationId)) {
                clearTimeout(this.reservationTimeouts.get(reservationId));
                this.reservationTimeouts.delete(reservationId);
            }
            
            return {
                success: true,
                confirmedQuantity: reservation.quantity
            };
            
        } catch (error) {
            throw error;
        }
    }
    
    // PROBLEM: Reservation timeout handling without proper cleanup
    setReservationTimeout(reservationId, timeoutMs) {
        const timeout = setTimeout(async () => {
            try {
                // RACE CONDITION: Timeout and confirmation can happen simultaneously
                const reservation = await StockReservation.findById(reservationId);
                
                if (reservation && reservation.status === 'pending') {
                    // Release reserved stock
                    const product = await Product.findById(reservation.productId);
                    product.reservedStock -= reservation.quantity;
                    await product.save();
                    
                    // Mark reservation as expired
                    reservation.status = 'expired';
                    await reservation.save();
                }
                
                this.reservationTimeouts.delete(reservationId);
                
            } catch (error) {
                console.error('Error releasing reservation:', error);
            }
        }, timeoutMs);
        
        this.reservationTimeouts.set(reservationId, timeout);
    }
}

// VULNERABLE: API endpoints without proper concurrency control
const inventoryManager = new InventoryManager();

app.post('/api/products/:id/reserve', async (req, res) => {
    const { id } = req.params;
    const { quantity } = req.body;
    
    try {
        // PROBLEM: No request deduplication
        const reservation = await inventoryManager.reserveStock(
            id,
            parseInt(quantity),
            req.user.id
        );
        
        res.json({
            success: true,
            reservationId: reservation.reservationId,
            expiresAt: reservation.expiresAt
        });
        
    } catch (error) {
        res.status(400).json({ error: error.message });
    }
});

app.post('/api/reservations/:id/confirm', async (req, res) => {
    const { id } = req.params;
    const { paymentId } = req.body;
    
    try {
        // PROBLEM: No idempotency protection
        const result = await inventoryManager.confirmPurchase(id, paymentId);
        
        res.json(result);
        
    } catch (error) {
        res.status(400).json({ error: error.message });
    }
});

// Attack scenarios:
// 1. Multiple simultaneous reservations for limited stock
// 2. Concurrent purchase confirmations
// 3. Race conditions during stock replenishment
// 4. Reservation timeout and confirmation collision
// 5. Bulk purchase operations causing overselling

Account Balance and Transaction Limit Race Conditions

Banking and financial applications that enforce transaction limits or manage account balances through separate read-modify-write operations are vulnerable to race conditions that allow bypassing limits or creating inconsistent account states during concurrent access.

Preview example – JAVASCRIPT
// VULNERABLE: Account balance management with race condition flaws
const express = require('express');
const app = express();

class AccountManager {
    constructor() {
        this.activeTransactions = new Map(); // Insufficient for distributed systems
    }
    
    // VULNERABLE: Balance update without proper locking
    async updateBalance(accountId, amount, transactionType) {
        try {
            const account = await Account.findById(accountId);
            
            if (!account) {
                throw new Error('Account not found');
            }
            
            // PROBLEM 1: Balance validation without atomic update
            if (amount < 0 && Math.abs(amount) > account.balance) {
                throw new Error('Insufficient funds');
            }
            
            // RACE CONDITION: Balance can change between check and update
            const oldBalance = account.balance;
            account.balance += amount;
            
            // PROBLEM 2: Separate transaction limit checks
            if (transactionType === 'withdrawal' || transactionType === 'transfer') {
                await this.checkTransactionLimits(accountId, Math.abs(amount));
            }
            
            // VULNERABLE: Save operation after limit checks
            await account.save();
            
            // PROBLEM 3: Transaction history after balance update
            await TransactionHistory.create({
                accountId,
                amount,
                type: transactionType,
                oldBalance,
                newBalance: account.balance,
                timestamp: new Date()
            });
            
            return {
                success: true,
                newBalance: account.balance
            };
            
        } catch (error) {
            throw error;
        }
    }
    
    // VULNERABLE: Transaction limit checking without atomic operations
    async checkTransactionLimits(accountId, amount) {
        const account = await Account.findById(accountId);
        
        // PROBLEM: Daily limit calculation without locking
        const today = new Date();
        today.setHours(0, 0, 0, 0);
        
        const tomorrow = new Date(today);
        tomorrow.setDate(today.getDate() + 1);
        
        const todayTransactions = await TransactionHistory.aggregate([
            {
                $match: {
                    accountId: accountId,
                    type: { $in: ['withdrawal', 'transfer'] },
                    timestamp: {
                        $gte: today,
                        $lt: tomorrow
                    }
                }
            },
            {
                $group: {
                    _id: null,
                    totalAmount: { $sum: { $abs: '$amount' } },
                    transactionCount: { $sum: 1 }
                }
            }
        ]);
        
        const dailyTotal = todayTransactions[0]?.totalAmount || 0;
        const dailyCount = todayTransactions[0]?.transactionCount || 0;
        
        // RACE CONDITION: Multiple transactions can bypass these limits
        if (dailyTotal + amount > account.dailyLimit) {
            throw new Error('Daily transaction limit exceeded');
        }
        
        if (dailyCount >= account.maxDailyTransactions) {
            throw new Error('Maximum daily transaction count exceeded');
        }
        
        // PROBLEM: Per-transaction limit check without considering concurrent transactions
        if (amount > account.perTransactionLimit) {
            throw new Error('Per-transaction limit exceeded');
        }
        
        return true;
    }
    
    // VULNERABLE: Concurrent transaction processing
    async processTransfer(fromAccountId, toAccountId, amount) {
        const transferId = `${fromAccountId}_${toAccountId}_${amount}_${Date.now()}`;
        
        try {
            // INSUFFICIENT: Simple duplicate prevention
            if (this.activeTransactions.has(transferId)) {
                throw new Error('Transfer already in progress');
            }
            
            this.activeTransactions.set(transferId, true);
            
            // PROBLEM: Non-atomic multi-account operations
            await this.updateBalance(fromAccountId, -amount, 'transfer');
            await this.updateBalance(toAccountId, amount, 'transfer');
            
            // PROBLEM: Transfer record created after balance updates
            const transfer = await Transfer.create({
                fromAccountId,
                toAccountId,
                amount,
                status: 'completed',
                processedAt: new Date()
            });
            
            this.activeTransactions.delete(transferId);
            
            return {
                success: true,
                transferId: transfer.id
            };
            
        } catch (error) {
            this.activeTransactions.delete(transferId);
            
            // PROBLEM: Partial rollback in case of failure
            try {
                await this.updateBalance(fromAccountId, amount, 'rollback');
            } catch (rollbackError) {
                console.error('Rollback failed:', rollbackError);
            }
            
            throw error;
        }
    }
    
    // VULNERABLE: Interest calculation with race conditions
    async calculateAndApplyInterest(accountId) {
        try {
            const account = await Account.findById(accountId);
            
            if (!account || account.accountType !== 'savings') {
                return { success: false, message: 'Not eligible for interest' };
            }
            
            // PROBLEM: Interest calculation without considering pending transactions
            const interestRate = account.interestRate || 0.02; // 2% annual
            const dailyRate = interestRate / 365;
            const interestAmount = account.balance * dailyRate;
            
            // RACE CONDITION: Balance update during interest calculation
            if (interestAmount > 0) {
                account.balance += interestAmount;
                account.lastInterestDate = new Date();
                
                await account.save();
                
                await TransactionHistory.create({
                    accountId,
                    amount: interestAmount,
                    type: 'interest',
                    oldBalance: account.balance - interestAmount,
                    newBalance: account.balance,
                    timestamp: new Date()
                });
            }
            
            return {
                success: true,
                interestApplied: interestAmount
            };
            
        } catch (error) {
            throw error;
        }
    }
}

// VULNERABLE: API endpoints without concurrency protection
const accountManager = new AccountManager();

app.post('/api/accounts/:id/transfer', async (req, res) => {
    const { id } = req.params;
    const { toAccountId, amount } = req.body;
    
    try {
        // PROBLEM: No idempotency key or request deduplication
        const result = await accountManager.processTransfer(
            id,
            toAccountId,
            parseFloat(amount)
        );
        
        res.json(result);
        
    } catch (error) {
        res.status(400).json({ error: error.message });
    }
});

app.post('/api/accounts/:id/withdraw', async (req, res) => {
    const { id } = req.params;
    const { amount } = req.body;
    
    try {
        // PROBLEM: No concurrency control for withdrawals
        const result = await accountManager.updateBalance(
            id,
            -parseFloat(amount),
            'withdrawal'
        );
        
        res.json(result);
        
    } catch (error) {
        res.status(400).json({ error: error.message });
    }
});

// Attack scenarios:
// 1. Simultaneous withdrawals to bypass daily limits
// 2. Concurrent transfers from same account
// 3. Race conditions during interest calculations
// 4. Multiple transaction limit violations
// 5. Balance inconsistencies during high-frequency trading

Fixes

1

Implement Atomic Transactions with Database Locking

Use database transactions with proper isolation levels and locking mechanisms to ensure that financial operations are atomic and prevent race conditions in concurrent transaction processing.

View implementation – JAVASCRIPT
// SECURE: Atomic transaction processing with database locking
const mongoose = require('mongoose');
const redis = require('redis');
const { v4: uuidv4 } = require('uuid');

const redisClient = redis.createClient(process.env.REDIS_URL);

class SecureFinancialProcessor {
    constructor() {
        this.lockTimeout = 30; // seconds
        this.transactionCache = new Map();
    }
    
    // Atomic money transfer with distributed locking
    async processSecureTransfer(transferData, idempotencyKey) {
        const {
            fromAccountId,
            toAccountId,
            amount,
            description,
            userId
        } = transferData;
        
        // Input validation
        if (amount <= 0) {
            throw new Error('Transfer amount must be positive');
        }
        
        if (fromAccountId === toAccountId) {
            throw new Error('Cannot transfer to same account');
        }
        
        // Check for duplicate transaction using idempotency key
        if (idempotencyKey) {
            const existingTransaction = await Transaction.findOne({
                idempotencyKey,
                status: { $in: ['completed', 'processing'] }
            });
            
            if (existingTransaction) {
                return {
                    success: true,
                    transactionId: existingTransaction.id,
                    message: 'Transaction already processed',
                    isIdempotent: true
                };
            }
        }
        
        // Create deterministic lock keys (smaller account ID first to prevent deadlocks)
        const lockKeys = [fromAccountId, toAccountId].sort().map(id => `account_lock:${id}`);
        const lockValues = lockKeys.map(() => uuidv4());
        
        try {
            // Acquire distributed locks in order to prevent deadlocks
            const locksAcquired = await this.acquireMultipleLocks(lockKeys, lockValues);
            if (!locksAcquired) {
                throw new Error('Unable to acquire account locks, please try again');
            }
            
            // Start database transaction with appropriate isolation level
            const session = await mongoose.startSession();
            const result = await session.withTransaction(async () => {
                // Fetch accounts with pessimistic locking
                const fromAccount = await Account.findById(fromAccountId)
                    .session(session)
                    .populate('accountLimits');
                
                const toAccount = await Account.findById(toAccountId)
                    .session(session);
                
                if (!fromAccount || !toAccount) {
                    throw new Error('One or both accounts not found');
                }
                
                if (fromAccount.status !== 'active' || toAccount.status !== 'active') {
                    throw new Error('One or both accounts are not active');
                }
                
                // Validate sufficient funds atomically
                if (fromAccount.balance < amount) {
                    throw new Error(`Insufficient funds. Available: ${fromAccount.balance}, Required: ${amount}`);
                }
                
                // Check transaction limits atomically
                await this.validateTransactionLimits(
                    fromAccount,
                    amount,
                    'transfer',
                    session
                );
                
                // Perform atomic balance updates with version control
                const fromUpdateResult = await Account.findOneAndUpdate(
                    {
                        _id: fromAccountId,
                        version: fromAccount.version,
                        balance: { $gte: amount } // Double-check sufficient funds
                    },
                    {
                        $inc: {
                            balance: -amount,
                            version: 1,
                            totalDebits: amount
                        },
                        $set: {
                            lastTransactionAt: new Date()
                        }
                    },
                    {
                        new: true,
                        session,
                        runValidators: true
                    }
                );
                
                if (!fromUpdateResult) {
                    throw new Error('Transfer failed - account may have been modified or insufficient funds');
                }
                
                const toUpdateResult = await Account.findOneAndUpdate(
                    {
                        _id: toAccountId,
                        version: toAccount.version
                    },
                    {
                        $inc: {
                            balance: amount,
                            version: 1,
                            totalCredits: amount
                        },
                        $set: {
                            lastTransactionAt: new Date()
                        }
                    },
                    {
                        new: true,
                        session,
                        runValidators: true
                    }
                );
                
                if (!toUpdateResult) {
                    throw new Error('Transfer failed - destination account may have been modified');
                }
                
                // Create transaction record atomically
                const transaction = await Transaction.create([{
                    fromAccountId,
                    toAccountId,
                    amount,
                    description,
                    type: 'transfer',
                    status: 'completed',
                    idempotencyKey,
                    userId,
                    fromBalanceBefore: fromAccount.balance,
                    fromBalanceAfter: fromUpdateResult.balance,
                    toBalanceBefore: toAccount.balance,
                    toBalanceAfter: toUpdateResult.balance,
                    processedAt: new Date(),
                    transactionHash: this.generateTransactionHash({
                        fromAccountId,
                        toAccountId,
                        amount,
                        timestamp: new Date()
                    })
                }], { session });
                
                // Update daily transaction tracking
                await this.updateDailyTransactionTracking(
                    fromAccountId,
                    amount,
                    'debit',
                    session
                );
                
                await this.updateDailyTransactionTracking(
                    toAccountId,
                    amount,
                    'credit',
                    session
                );
                
                return {
                    transaction: transaction[0],
                    fromBalance: fromUpdateResult.balance,
                    toBalance: toUpdateResult.balance
                };
                
            }, {
                readConcern: { level: 'snapshot' },
                writeConcern: { w: 'majority', j: true },
                readPreference: 'primary'
            });
            
            // Log successful transaction
            await this.logTransactionEvent({
                type: 'transfer_completed',
                transactionId: result.transaction.id,
                fromAccountId,
                toAccountId,
                amount,
                userId,
                timestamp: new Date()
            });
            
            return {
                success: true,
                transactionId: result.transaction.id,
                fromBalance: result.fromBalance,
                toBalance: result.toBalance
            };
            
        } finally {
            // Always release locks
            await this.releaseMultipleLocks(lockKeys, lockValues);
        }
    }
    
    // Validate transaction limits with atomic operations
    async validateTransactionLimits(account, amount, transactionType, session) {
        const today = new Date();
        today.setHours(0, 0, 0, 0);
        
        const tomorrow = new Date(today);
        tomorrow.setDate(today.getDate() + 1);
        
        // Get daily transaction summary atomically
        const dailySummary = await Transaction.aggregate([
            {
                $match: {
                    $or: [
                        { fromAccountId: account._id },
                        { toAccountId: account._id }
                    ],
                    status: 'completed',
                    processedAt: {
                        $gte: today,
                        $lt: tomorrow
                    }
                }
            },
            {
                $group: {
                    _id: null,
                    totalDebits: {
                        $sum: {
                            $cond: [
                                { $eq: ['$fromAccountId', account._id] },
                                '$amount',
                                0
                            ]
                        }
                    },
                    transactionCount: { $sum: 1 }
                }
            }
        ]).session(session);
        
        const dayStats = dailySummary[0] || { totalDebits: 0, transactionCount: 0 };
        
        // Check per-transaction limit
        if (amount > account.accountLimits.perTransactionLimit) {
            throw new Error(`Transaction amount ${amount} exceeds per-transaction limit of ${account.accountLimits.perTransactionLimit}`);
        }
        
        // Check daily amount limit
        if (transactionType === 'transfer' || transactionType === 'withdrawal') {
            if (dayStats.totalDebits + amount > account.accountLimits.dailyDebitLimit) {
                throw new Error(`Daily debit limit exceeded. Current: ${dayStats.totalDebits}, Limit: ${account.accountLimits.dailyDebitLimit}`);
            }
        }
        
        // Check daily transaction count
        if (dayStats.transactionCount >= account.accountLimits.maxDailyTransactions) {
            throw new Error(`Maximum daily transaction count of ${account.accountLimits.maxDailyTransactions} exceeded`);
        }
        
        return true;
    }
    
    // Distributed locking implementation
    async acquireMultipleLocks(lockKeys, lockValues) {
        const acquiredLocks = [];
        
        try {
            for (let i = 0; i < lockKeys.length; i++) {
                const acquired = await this.acquireSingleLock(lockKeys[i], lockValues[i]);
                if (!acquired) {
                    // Release any locks we've already acquired
                    for (let j = 0; j < acquiredLocks.length; j++) {
                        await this.releaseSingleLock(acquiredLocks[j].key, acquiredLocks[j].value);
                    }
                    return false;
                }
                acquiredLocks.push({ key: lockKeys[i], value: lockValues[i] });
            }
            return true;
        } catch (error) {
            // Release any acquired locks on error
            for (const lock of acquiredLocks) {
                await this.releaseSingleLock(lock.key, lock.value);
            }
            throw error;
        }
    }
    
    async acquireSingleLock(key, value) {
        const result = await redisClient.set(
            key,
            value,
            'PX',
            this.lockTimeout * 1000,
            'NX'
        );
        return result === 'OK';
    }
    
    async releaseMultipleLocks(lockKeys, lockValues) {
        const promises = lockKeys.map((key, index) => 
            this.releaseSingleLock(key, lockValues[index])
        );
        await Promise.all(promises);
    }
    
    async releaseSingleLock(key, value) {
        const script = `
            if redis.call("get", KEYS[1]) == ARGV[1] then
                return redis.call("del", KEYS[1])
            else
                return 0
            end
        `;
        return redisClient.eval(script, 1, key, value);
    }
    
    // Generate cryptographic hash for transaction integrity
    generateTransactionHash(transactionData) {
        const crypto = require('crypto');
        const dataString = JSON.stringify(transactionData, Object.keys(transactionData).sort());
        return crypto.createHash('sha256').update(dataString).digest('hex');
    }
}
2

Implement Idempotency and Request Deduplication

Create a comprehensive idempotency system that prevents duplicate transaction processing and provides consistent responses for repeated requests, even in high-concurrency scenarios.

View implementation – JAVASCRIPT
// SECURE: Idempotency and request deduplication system
const crypto = require('crypto');
const { v4: uuidv4 } = require('uuid');

class IdempotencyManager {
    constructor() {
        this.cacheTimeout = 24 * 60 * 60 * 1000; // 24 hours
    }
    
    // Generate idempotency key from request data
    generateIdempotencyKey(requestData, userId) {
        const keyData = {
            userId,
            operation: requestData.operation,
            amount: requestData.amount,
            fromAccount: requestData.fromAccountId,
            toAccount: requestData.toAccountId,
            timestamp: Math.floor(Date.now() / (5 * 60 * 1000)) // 5-minute window
        };
        
        const dataString = JSON.stringify(keyData, Object.keys(keyData).sort());
        return crypto.createHash('sha256').update(dataString).digest('hex');
    }
    
    // Check for existing operation with same idempotency key
    async checkExistingOperation(idempotencyKey, operationType) {
        // Check Redis cache first for fast response
        const cacheKey = `idempotency:${operationType}:${idempotencyKey}`;
        const cachedResult = await redisClient.get(cacheKey);
        
        if (cachedResult) {
            return {
                exists: true,
                result: JSON.parse(cachedResult),
                source: 'cache'
            };
        }
        
        // Check database for persistent record
        const existingOperation = await IdempotentOperation.findOne({
            idempotencyKey,
            operationType,
            status: { $in: ['completed', 'processing'] },
            createdAt: { $gte: new Date(Date.now() - this.cacheTimeout) }
        });
        
        if (existingOperation) {
            // Cache the result
            await redisClient.setex(
                cacheKey,
                3600, // 1 hour
                JSON.stringify({
                    operationId: existingOperation.id,
                    result: existingOperation.result,
                    status: existingOperation.status,
                    completedAt: existingOperation.completedAt
                })
            );
            
            return {
                exists: true,
                result: existingOperation.result,
                source: 'database'
            };
        }
        
        return { exists: false };
    }
    
    // Create idempotent operation record
    async createIdempotentOperation(idempotencyKey, operationType, requestData, userId) {
        const operation = await IdempotentOperation.create({
            idempotencyKey,
            operationType,
            requestData,
            userId,
            status: 'processing',
            createdAt: new Date(),
            fingerprint: this.generateRequestFingerprint(requestData, userId)
        });
        
        return operation;
    }
    
    // Complete idempotent operation
    async completeIdempotentOperation(operationId, result) {
        const operation = await IdempotentOperation.findByIdAndUpdate(
            operationId,
            {
                status: 'completed',
                result: result,
                completedAt: new Date()
            },
            { new: true }
        );
        
        if (operation) {
            // Cache the completed operation
            const cacheKey = `idempotency:${operation.operationType}:${operation.idempotencyKey}`;
            await redisClient.setex(
                cacheKey,
                3600,
                JSON.stringify({
                    operationId: operation.id,
                    result: operation.result,
                    status: operation.status,
                    completedAt: operation.completedAt
                })
            );
        }
        
        return operation;
    }
    
    // Generate request fingerprint for additional validation
    generateRequestFingerprint(requestData, userId) {
        const fingerprintData = {
            userId,
            ...requestData,
            timestamp: new Date().toISOString().split('T')[0] // Daily fingerprint
        };
        
        const dataString = JSON.stringify(fingerprintData, Object.keys(fingerprintData).sort());
        return crypto.createHash('md5').update(dataString).digest('hex');
    }
}

// Secure payment processing with idempotency
class SecurePaymentProcessor {
    constructor() {
        this.idempotencyManager = new IdempotencyManager();
        this.financialProcessor = new SecureFinancialProcessor();
    }
    
    // Process payment with comprehensive idempotency protection
    async processPayment(paymentData, userId, providedIdempotencyKey = null) {
        try {
            // Generate or validate idempotency key
            const idempotencyKey = providedIdempotencyKey || 
                this.idempotencyManager.generateIdempotencyKey(paymentData, userId);
            
            // Check for existing operation
            const existingOperation = await this.idempotencyManager.checkExistingOperation(
                idempotencyKey,
                'payment'
            );
            
            if (existingOperation.exists) {
                return {
                    success: true,
                    ...existingOperation.result,
                    isIdempotent: true,
                    source: existingOperation.source
                };
            }
            
            // Create idempotent operation record
            const operation = await this.idempotencyManager.createIdempotentOperation(
                idempotencyKey,
                'payment',
                paymentData,
                userId
            );
            
            try {
                // Validate payment data
                await this.validatePaymentData(paymentData, userId);
                
                // Process payment atomically
                const paymentResult = await this.processAtomicPayment(
                    paymentData,
                    userId,
                    idempotencyKey
                );
                
                // Complete idempotent operation
                await this.idempotencyManager.completeIdempotentOperation(
                    operation.id,
                    paymentResult
                );
                
                return {
                    success: true,
                    ...paymentResult,
                    isIdempotent: false
                };
                
            } catch (error) {
                // Mark operation as failed
                await IdempotentOperation.findByIdAndUpdate(operation.id, {
                    status: 'failed',
                    error: error.message,
                    failedAt: new Date()
                });
                
                throw error;
            }
            
        } catch (error) {
            console.error('Payment processing error:', {
                error: error.message,
                paymentData,
                userId,
                timestamp: new Date()
            });
            
            throw error;
        }
    }
    
    // Atomic payment processing with external service integration
    async processAtomicPayment(paymentData, userId, idempotencyKey) {
        const {
            accountId,
            amount,
            paymentMethodId,
            description,
            merchantId
        } = paymentData;
        
        const session = await mongoose.startSession();
        const result = await session.withTransaction(async () => {
            // Reserve funds atomically
            const reservation = await this.reserveAccountFunds(
                accountId,
                amount,
                idempotencyKey,
                session
            );
            
            try {
                // Process with external payment service
                const externalPayment = await this.processExternalPayment({
                    amount,
                    paymentMethodId,
                    idempotencyKey,
                    metadata: {
                        accountId,
                        reservationId: reservation.id,
                        userId
                    }
                });
                
                if (externalPayment.status === 'succeeded') {
                    // Confirm fund reservation
                    await this.confirmFundReservation(
                        reservation.id,
                        externalPayment.id,
                        session
                    );
                    
                    // Create payment record
                    const payment = await Payment.create([{
                        accountId,
                        amount,
                        paymentMethodId,
                        externalPaymentId: externalPayment.id,
                        reservationId: reservation.id,
                        description,
                        merchantId,
                        status: 'completed',
                        idempotencyKey,
                        userId,
                        processedAt: new Date()
                    }], { session });
                    
                    return {
                        paymentId: payment[0].id,
                        externalPaymentId: externalPayment.id,
                        amount,
                        status: 'completed'
                    };
                    
                } else {
                    // Release reservation on payment failure
                    await this.releaseFundReservation(reservation.id, session);
                    
                    throw new Error(`Payment failed: ${externalPayment.failure_reason}`);
                }
                
            } catch (error) {
                // Release reservation on any error
                await this.releaseFundReservation(reservation.id, session);
                throw error;
            }
            
        }, {
            readConcern: { level: 'snapshot' },
            writeConcern: { w: 'majority', j: true }
        });
        
        return result;
    }
    
    // Reserve account funds atomically
    async reserveAccountFunds(accountId, amount, idempotencyKey, session) {
        const account = await Account.findById(accountId).session(session);
        
        if (!account) {
            throw new Error('Account not found');
        }
        
        if (account.balance < amount) {
            throw new Error('Insufficient funds');
        }
        
        // Update account balance atomically
        const updatedAccount = await Account.findOneAndUpdate(
            {
                _id: accountId,
                balance: { $gte: amount },
                version: account.version
            },
            {
                $inc: {
                    balance: -amount,
                    reservedFunds: amount,
                    version: 1
                }
            },
            {
                new: true,
                session
            }
        );
        
        if (!updatedAccount) {
            throw new Error('Unable to reserve funds - account may have been modified');
        }
        
        // Create reservation record
        const reservation = await FundReservation.create([{
            accountId,
            amount,
            status: 'reserved',
            idempotencyKey,
            expiresAt: new Date(Date.now() + 30 * 60 * 1000), // 30 minutes
            createdAt: new Date()
        }], { session });
        
        return reservation[0];
    }
}
3

Implement Optimistic Locking with Version Control

Use optimistic locking mechanisms with version control to detect and handle concurrent modifications, ensuring data consistency while maintaining high performance in financial systems.

View implementation – JAVASCRIPT
// SECURE: Optimistic locking with version control for financial data
const mongoose = require('mongoose');
const EventEmitter = require('events');

class OptimisticLockingManager extends EventEmitter {
    constructor() {
        super();
        this.maxRetries = 3;
        this.retryDelay = 100; // milliseconds
    }
    
    // Perform operation with optimistic locking and automatic retry
    async performWithOptimisticLocking(operation, maxRetries = this.maxRetries) {
        let attempt = 0;
        let lastError;
        
        while (attempt < maxRetries) {
            try {
                return await operation(attempt);
            } catch (error) {
                lastError = error;
                
                // Check if this is a version conflict error
                if (this.isVersionConflictError(error)) {
                    attempt++;
                    
                    if (attempt < maxRetries) {
                        // Exponential backoff with jitter
                        const delay = this.retryDelay * Math.pow(2, attempt) + Math.random() * 100;
                        await this.sleep(delay);
                        
                        this.emit('retry', {
                            attempt,
                            maxRetries,
                            error: error.message,
                            nextDelay: delay
                        });
                        
                        continue;
                    }
                }
                
                throw error;
            }
        }
        
        this.emit('maxRetriesExceeded', {
            attempts: attempt,
            lastError: lastError.message
        });
        
        throw new Error(`Operation failed after ${maxRetries} attempts: ${lastError.message}`);
    }
    
    // Check if error is due to version conflict
    isVersionConflictError(error) {
        return error.message.includes('version') ||
               error.message.includes('modified') ||
               error.code === 11000 || // MongoDB duplicate key
               error.name === 'VersionError';
    }
    
    // Sleep utility for retry delays
    sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
}

// Enhanced account schema with optimistic locking
const accountSchema = new mongoose.Schema({
    accountNumber: { type: String, unique: true, required: true },
    balance: { 
        type: Number, 
        required: true, 
        min: 0,
        set: function(value) {
            // Round to 2 decimal places to avoid floating point issues
            return Math.round(value * 100) / 100;
        }
    },
    version: { type: Number, default: 0 },
    lastModifiedAt: { type: Date, default: Date.now },
    lastModifiedBy: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
    transactionCount: { type: Number, default: 0 },
    dailyLimits: {
        maxAmount: { type: Number, default: 10000 },
        maxTransactions: { type: Number, default: 50 },
        currentDayAmount: { type: Number, default: 0 },
        currentDayTransactions: { type: Number, default: 0 },
        lastResetDate: { type: Date, default: Date.now }
    },
    lockInfo: {
        isLocked: { type: Boolean, default: false },
        lockedBy: { type: String },
        lockedAt: { type: Date },
        lockReason: { type: String }
    }
});

// Pre-save middleware to increment version
accountSchema.pre('save', function(next) {
    if (this.isModified() && !this.isNew) {
        this.version += 1;
        this.lastModifiedAt = new Date();
    }
    next();
});

const Account = mongoose.model('Account', accountSchema);

// Secure account balance manager with optimistic locking
class SecureAccountBalanceManager {
    constructor() {
        this.lockingManager = new OptimisticLockingManager();
    }
    
    // Update account balance with optimistic locking
    async updateAccountBalance(accountId, amount, transactionType, metadata = {}) {
        return await this.lockingManager.performWithOptimisticLocking(async (attempt) => {
            const session = await mongoose.startSession();
            
            try {
                return await session.withTransaction(async () => {
                    // Fetch current account state
                    const account = await Account.findById(accountId).session(session);
                    
                    if (!account) {
                        throw new Error('Account not found');
                    }
                    
                    // Validate account state
                    if (account.lockInfo.isLocked) {
                        throw new Error(`Account is locked: ${account.lockInfo.lockReason}`);
                    }
                    
                    // Reset daily limits if necessary
                    await this.resetDailyLimitsIfNeeded(account);
                    
                    // Validate transaction against limits
                    await this.validateTransactionLimits(account, amount, transactionType);
                    
                    // Calculate new balance
                    const newBalance = account.balance + amount;
                    
                    if (newBalance < 0) {
                        throw new Error(`Insufficient funds. Current balance: ${account.balance}, Requested: ${Math.abs(amount)}`);
                    }
                    
                    // Prepare update data
                    const updateData = {
                        balance: newBalance,
                        lastModifiedBy: metadata.userId,
                        transactionCount: account.transactionCount + 1
                    };
                    
                    // Update daily tracking
                    if (transactionType === 'debit' || transactionType === 'withdrawal') {
                        updateData['dailyLimits.currentDayAmount'] = 
                            account.dailyLimits.currentDayAmount + Math.abs(amount);
                        updateData['dailyLimits.currentDayTransactions'] = 
                            account.dailyLimits.currentDayTransactions + 1;
                    }
                    
                    // Perform atomic update with version check
                    const updatedAccount = await Account.findOneAndUpdate(
                        {
                            _id: accountId,
                            version: account.version, // Optimistic lock check
                            'lockInfo.isLocked': false // Ensure not locked
                        },
                        {
                            $set: updateData,
                            $inc: { version: 1 } // Increment version
                        },
                        {
                            new: true,
                            session,
                            runValidators: true
                        }
                    );
                    
                    if (!updatedAccount) {
                        throw new Error('Account update failed - account may have been modified by another transaction');
                    }
                    
                    // Create audit trail
                    await this.createAuditRecord({
                        accountId,
                        transactionType,
                        amount,
                        balanceBefore: account.balance,
                        balanceAfter: updatedAccount.balance,
                        versionBefore: account.version,
                        versionAfter: updatedAccount.version,
                        metadata,
                        attempt
                    }, session);
                    
                    return {
                        success: true,
                        accountId,
                        newBalance: updatedAccount.balance,
                        transactionCount: updatedAccount.transactionCount,
                        version: updatedAccount.version
                    };
                    
                }, {
                    readConcern: { level: 'snapshot' },
                    writeConcern: { w: 'majority', j: true }
                });
                
            } finally {
                await session.endSession();
            }
        });
    }
    
    // Bulk balance update with optimistic locking
    async updateMultipleBalances(updates, metadata = {}) {
        return await this.lockingManager.performWithOptimisticLocking(async (attempt) => {
            const session = await mongoose.startSession();
            
            try {
                return await session.withTransaction(async () => {
                    const results = [];
                    
                    // Sort account IDs to prevent deadlocks
                    const sortedUpdates = updates.sort((a, b) => 
                        a.accountId.toString().localeCompare(b.accountId.toString())
                    );
                    
                    // Fetch all accounts with current versions
                    const accountIds = sortedUpdates.map(u => u.accountId);
                    const accounts = await Account.find({
                        _id: { $in: accountIds }
                    }).session(session);
                    
                    const accountMap = new Map();
                    accounts.forEach(acc => accountMap.set(acc._id.toString(), acc));
                    
                    // Validate all updates first
                    for (const update of sortedUpdates) {
                        const account = accountMap.get(update.accountId.toString());
                        
                        if (!account) {
                            throw new Error(`Account ${update.accountId} not found`);
                        }
                        
                        if (account.lockInfo.isLocked) {
                            throw new Error(`Account ${update.accountId} is locked`);
                        }
                        
                        const newBalance = account.balance + update.amount;
                        if (newBalance < 0) {
                            throw new Error(`Insufficient funds in account ${update.accountId}`);
                        }
                    }
                    
                    // Perform all updates atomically
                    for (const update of sortedUpdates) {
                        const account = accountMap.get(update.accountId.toString());
                        const newBalance = account.balance + update.amount;
                        
                        const updatedAccount = await Account.findOneAndUpdate(
                            {
                                _id: update.accountId,
                                version: account.version
                            },
                            {
                                $set: {
                                    balance: newBalance,
                                    lastModifiedBy: metadata.userId
                                },
                                $inc: {
                                    version: 1,
                                    transactionCount: 1
                                }
                            },
                            {
                                new: true,
                                session
                            }
                        );
                        
                        if (!updatedAccount) {
                            throw new Error(`Failed to update account ${update.accountId} - version conflict`);
                        }
                        
                        results.push({
                            accountId: update.accountId,
                            oldBalance: account.balance,
                            newBalance: updatedAccount.balance,
                            version: updatedAccount.version
                        });
                        
                        // Update the map for subsequent operations
                        accountMap.set(update.accountId.toString(), updatedAccount);
                    }
                    
                    return {
                        success: true,
                        updatedAccounts: results,
                        attempt
                    };
                    
                }, {
                    readConcern: { level: 'snapshot' },
                    writeConcern: { w: 'majority', j: true }
                });
                
            } finally {
                await session.endSession();
            }
        });
    }
    
    // Create detailed audit record
    async createAuditRecord(auditData, session) {
        await BalanceAuditLog.create([{
            accountId: auditData.accountId,
            transactionType: auditData.transactionType,
            amount: auditData.amount,
            balanceBefore: auditData.balanceBefore,
            balanceAfter: auditData.balanceAfter,
            versionBefore: auditData.versionBefore,
            versionAfter: auditData.versionAfter,
            userId: auditData.metadata.userId,
            attempt: auditData.attempt,
            timestamp: new Date(),
            metadata: auditData.metadata
        }], { session });
    }
}

Detect This Vulnerability in Your Code

Sourcery automatically identifies race condition vulnerabilities in financial transaction processing systems and many other security issues in your codebase.