Home > Blockchain >  Is it possible to create MongoDB query dynamically on the basis of input vaiables or can we inject s
Is it possible to create MongoDB query dynamically on the basis of input vaiables or can we inject s

Time:01-19

I want to create a dynamic MongoDB query on the basis of input variables I try to put query as a string but it's not working.

let carsPromise = Car.find({
   carsFetchQuery(company, model, minPrice, maxPrice),
  })
    .skip(offSet)
    .limit(rowsPerPage)
    .lean();

Function to create fetch query dynamically if input parameters are not null

const createCarFetchquery = (company, model, minPrice, maxPrice) => {
  if (
    company === null &&
    model === null &&
    minPrice === null &&
    maxPrice === null
  )
    return '';

  let query = `$and: [
    {
      $or: [{
        ${getQuery('$eq', 'company', company)} ${model ? ',' : ''}
        ${getQuery('$eq', 'model', model)}  ${minPrice ? ',' : ''}
        ${getQuery('$gte', 'minPrice', minPrice)}  ${maxPrice ? ',' : ''}
        ${getQuery('$eq', 'maxPrice', maxPrice)}
     }],
    },
  ]
  `;

Helper function to get query expression

const getQuery = (opertor, key, value) => {
  if (value) {
    return `${key}: {${opertor}: '${value}'}`;
  }
  return '';
};

But it is not working any help

CodePudding user response:

You can not inject string in to query, but you can pass an object in query, prepare an array of objects and use it in query,

For understanding i have updated your method as below, you can change it as per your requirement,

const createCarFetchquery = (company, model, minPrice, maxPrice) => {
    
    let query = [];
    if (company) query.push(getQuery('$eq', 'company', company));
    if (model) query.push(getQuery('$eq', 'model', model));
    if (minPrice) query.push(getQuery('$gte', 'minPrice', minPrice));
    if (maxPrice) query.push(getQuery('$eq', 'maxPrice', maxPrice));

    if (query.length) return { $or: query };
    return {};

}

const getQuery = (opertor, key, value) => {
    return { [key]: { [opertor]: value } };
};

console.log(createCarFetchquery("ABC", "A", 10, 12));

and use it in your query, but make sure you have wrapped your query by async/await method,

let carsPromise = await Car.find(
   carsFetchQuery(company, model, minPrice, maxPrice)
)
.skip(offSet)
.limit(rowsPerPage)
.lean();

CodePudding user response:

find requires a JSON object, thus it may work with

let carsPromise = Car.find(
   JSON.parse(carsFetchQuery(company, model, minPrice, maxPrice))
)

That's highly vulnerable for NoSQL injection.

A better approach would be like this one:

var query = []

var field = {}
field['company'] = {}
field['company']['$eq'] = company
query.push(field)

var field = {}
field['minPrice'] = {}
field['minPrice']['$gte'] = minPrice
query.push(field)

// or a  bit shorter (thanks to turivishal) 
query.push({ ['maxPrice']: { ['$eq']: maxPrice} })

let carsPromise = Car.find({$and: [ { $or: query } ] })

You an simply push as many conditions as you like with a loop. However, it is still possible to inject NoSQL code, for example when the URL looks like this:

...?model['$ne']=null

then the query would return all documents where model is not null - most likely all, see https://blog.websecurify.com/2014/08/hacking-nodejs-and-mongodb.html

Thus you should protect input values with mongo-sanitize or similar:

field['company']['$eq'] = sanitize(company)
  •  Tags:  
  • Related