diff --git a/app/data/allocations-dao.js b/app/data/allocations-dao.js index 24d4718c4..2a86065e7 100644 --- a/app/data/allocations-dao.js +++ b/app/data/allocations-dao.js @@ -1,114 +1,102 @@ const UserDAO = require("./user-dao").UserDAO; /* The AllocationsDAO must be constructed with a connected database object */ -const AllocationsDAO = function(db){ +const AllocationsDAO = function (db) { - "use strict"; + "use strict"; - /* If this constructor is called without the "new" operator, "this" points - * to the global object. Log a warning and call it correctly. */ - if (false === (this instanceof AllocationsDAO)) { - console.log("Warning: AllocationsDAO constructor called without 'new' operator"); - return new AllocationsDAO(db); - } + /* If this constructor is called without the "new" operator, "this" points + * to the global object. Log a warning and call it correctly. */ + if (false === (this instanceof AllocationsDAO)) { + console.log("Warning: AllocationsDAO constructor called without 'new' operator"); + return new AllocationsDAO(db); + } - const allocationsCol = db.collection("allocations"); - const userDAO = new UserDAO(db); + const allocationsCol = db.collection("allocations"); + const userDAO = new UserDAO(db); - this.update = (userId, stocks, funds, bonds, callback) => { - const parsedUserId = parseInt(userId); + this.update = (userId, stocks, funds, bonds, callback) => { + const parsedUserId = parseInt(userId, 10); - // Create allocations document - const allocations = { - userId: userId, - stocks: stocks, - funds: funds, - bonds: bonds - }; + // Create allocations document + const allocations = { + userId: parsedUserId, + stocks: stocks, + funds: funds, + bonds: bonds + }; - allocationsCol.update({ - userId: parsedUserId - }, allocations, { - upsert: true - }, err => { + // Use $set + upsert to avoid replacing the whole document accidentally + allocationsCol.updateOne( + { userId: parsedUserId }, + { $set: allocations }, + { upsert: true }, + (err) => { + if (err) return callback(err, null); - if (!err) { + console.log("Updated allocations"); - console.log("Updated allocations"); + userDAO.getUserById(parsedUserId, (err, user) => { + if (err) return callback(err, null); - userDAO.getUserById(userId, (err, user) => { + // add user details + allocations.userId = parsedUserId; + allocations.userName = user.userName; + allocations.firstName = user.firstName; + allocations.lastName = user.lastName; - if (err) return callback(err, null); + return callback(null, allocations); + }); + } + ); + }; + + this.getByUserIdAndThreshold = (userId, threshold, callback) => { + const parsedUserId = parseInt(userId, 10); + + // build safe criteria (avoid $where / JS execution) + let criteria = { userId: parsedUserId }; + + if (threshold !== undefined && threshold !== null && threshold !== '') { + const parsedThreshold = parseInt(threshold, 10); + if (Number.isNaN(parsedThreshold) || parsedThreshold < 0) { + return callback(`The user supplied threshold: ${threshold} was not valid.`, null); + } + criteria.stocks = { $gt: parsedThreshold }; + } - // add user details - allocations.userId = userId; - allocations.userName = user.userName; - allocations.firstName = user.firstName; - allocations.lastName = user.lastName; + allocationsCol.find(criteria).toArray((err, allocations) => { + if (err) return callback(err, null); + if (!allocations.length) return callback("ERROR: No allocations found for the user", null); - return callback(null, allocations); - }); - } + let doneCounter = 0; + const userAllocations = []; + let called = false; // guard to avoid multiple callbacks + allocations.forEach(alloc => { + userDAO.getUserById(alloc.userId, (err, user) => { + if (called) return; + if (err) { + called = true; return callback(err, null); - }); - }; + } + + alloc.userName = user.userName; + alloc.firstName = user.firstName; + alloc.lastName = user.lastName; - this.getByUserIdAndThreshold = (userId, threshold, callback) => { - const parsedUserId = parseInt(userId); - - const searchCriteria = () => { - - if (threshold) { - /* - // Fix for A1 - 2 NoSQL Injection - escape the threshold parameter properly - // Fix this NoSQL Injection which doesn't sanitze the input parameter 'threshold' and allows attackers - // to inject arbitrary javascript code into the NoSQL query: - // 1. 0';while(true){}' - // 2. 1'; return 1 == '1 - // Also implement fix in allocations.html for UX. - const parsedThreshold = parseInt(threshold, 10); - - if (parsedThreshold >= 0 && parsedThreshold <= 99) { - return {$where: `this.userId == ${parsedUserId} && this.stocks > ${parsedThreshold}`}; - } - throw `The user supplied threshold: ${parsedThreshold} was not valid.`; - */ - return { - $where: `this.userId == ${parsedUserId} && this.stocks > '${threshold}'` - }; - } - return { - userId: parsedUserId - }; - }; - - allocationsCol.find(searchCriteria()).toArray((err, allocations) => { - if (err) return callback(err, null); - if (!allocations.length) return callback("ERROR: No allocations found for the user", null); - - let doneCounter = 0; - const userAllocations = []; - - allocations.forEach( alloc => { - userDAO.getUserById(alloc.userId, (err, user) => { - if (err) return callback(err, null); - - alloc.userName = user.userName; - alloc.firstName = user.firstName; - alloc.lastName = user.lastName; - - doneCounter += 1; - userAllocations.push(alloc); - - if (doneCounter === allocations.length) { - callback(null, userAllocations); - } - }); - }); + doneCounter += 1; + userAllocations.push(alloc); + + if (doneCounter === allocations.length) { + called = true; + callback(null, userAllocations); + } }); - }; + }); + }); + }; }; -module.exports.AllocationsDAO = AllocationsDAO; +module.exports.AllocationsDAO = AllocationsDAO; \ No newline at end of file