CVE-2019-19250 (JavaScript, published 2019-11-25T17:15Z)
Description: value: OpenTrade before 2019-11-23 allows SQL injection, related to server/modules/api/v1.js and server/utils.js.
Severity: HIGH (CVSS v3 base score: 9.8)
Weakness: CWE-89 \u2014 Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.
Repository: https://github.com/3s3s/opentrade
Files changed:
server/modules/api/v1.js
server/utils.js
Commit message: critical bug with SQL injection fixed!


Vulnerable code:
exports.onGetMarketSummary = function(req, res)
    g_constants.dbTables['coins'].selectAll('name, ticker, info, icon', 'ticker="'+data[1]+'"', '', (err, rows) => {
        if (err || !rows)
            return onError(req, res, err && err.message ? err.message : 'unknown database error');
        if (!rows.length)
            return onError(req, res, 'ticker '+data[1]+' not found');
        const COIN = rows[0];
        const coin_icon_src = rows[0].icon;
        const coin_info = JSON.parse(utils.Decrypt(rows[0].info));
        {
            onSuccess(req, res, ret);
            return;
        }
        
    });
function SubmitOrder(req, res, buysell)
        utils.GetCoinFromTicker(currency[1], coin => {
        });    
exports.onMarketGetOpenOrders = function(req, res)
        utils.GetCoinFromTicker(currency[1], coin => {
        });    
exports.onAccountGetBalance = function(req, res)
        utils.GetCoinFromTicker(queryStr.currency, coin => {
        });    
exports.onAccountGetDepositAddress = function(req, res)
    utils.GetCoinFromTicker(queryStr.currency, coin => {
    });    
exports.onAccountWithdraw = function(req, res)
    utils.GetCoinFromTicker(queryStr.currency, coin => {
    });
    g_constants.dbTables['coins'].selectAll('ROWID AS id, *', 'ticker="'+escape(ticker)+'"', '', (err, rows) => {
        if (err || !rows || !rows.length) return callback({});
        callback(rows[0]);
    })

Fixed code:
exports.onGetMarketSummary = async function(req, res)
    try
    {
    //g_constants.dbTables['coins'].selectAll('name, ticker, info, icon', 'ticker="'+data[1]+'"', '', (err, rows) => {
    //    if (err || !rows)
    //        return onError(req, res, err && err.message ? err.message : 'unknown database error');
        //if (!rows.length)
        //    return onError(req, res, 'ticker '+data[1]+' not found');
        //const COIN = rows[0];
        const COIN = await utils.GetCoinFromTicker(data[1]);
        const coin_icon_src = COIN.icon;
        const coin_info = JSON.parse(utils.Decrypt(COIN.info));
            return onSuccess(req, res, ret);

    }
    catch (e)
    {
        return onError(req, res, e.message || 'unknown error');
    }
    //});
async function SubmitOrder(req, res, buysell)
        const coin = await utils.GetCoinFromTicker(currency[1]); //, coin => {
        //});    
exports.onMarketGetOpenOrders = async function(req, res)
        const coin = await utils.GetCoinFromTicker(currency[1]); //, coin => {
        //});    
exports.onAccountGetBalance = async function(req, res)
        const coin = await utils.GetCoinFromTicker(queryStr.currency);//, coin => {
        //});    
exports.onAccountGetDepositAddress = async function(req, res)
    const coin = utils.GetCoinFromTicker(queryStr.currency); //, coin => {
    //});    
exports.onAccountWithdraw = async function(req, res)
    const coin = await utils.GetCoinFromTicker(queryStr.currency); //, coin => {
    //});
    return new Promise(async (ok, cancel) => {
        try {
            const rows = await g_constants.dbTables['coins'].Select('ROWID AS id, *', 'ticker="'+escape(ticker)+'"', '');
            if (!rows || !rows.length || !rows[0].name) throw new Error('Coin ticker ('+escape(ticker)+') not found!');
            return ok(rows[0]);
        }
        catch(e) {
            return cancel(e);
        }
    });