0

Code example

// Creates an Objection query. 
// I have no control over the creation of the query. I can only modify the query after it has been created.
// Example: "select `todos`.* from `todos` where `text` = ?"
const objectionQuery = thirdPartyService.createQuery(userControlledInput);
// Adds an access check. Example "select `todos`.* from `todos` where `text` = ? and `userId` = ?"
objectionQuery.andWhere("userId", currentUser.id);

The above example has a security bug. If the thirdPartyService generates a query like this:

select `todos`.* from `todos` where `text` = ? or `id` = ?

Then after adding the access check we will get the following query:

select `todos`.* from `todos` where `text` = ? or `id` = ? and `userId` = ?

And this query can return data that doesn't belong to the current user. To fix this bug, we need to enclose the user-controlled conditions in parentheses:

select `todos`.* from `todos` where (`text` = ? or `id` = ?) and `userId` = ?

But how do I do this with the Objection query builder? I imagine something like this:

const objectionQuery = thirdPartyService.createQuery(userControlledInput);
wrapWhereClauses(objectionQuery);
objectionQuery.andWhere("userId", currentUser.id);
Mike
  • 613
  • 1
  • 10
  • 20

2 Answers2

3

From docs: You can add parentheses to queries by passing a function to any of the where* methods:

await Todo.query()
  .where('userId', 1)
  .where(builder => {
    builder.where('text', 2).orWhere('id', 3);
  });

will result in

select * from "todos" where "userId" = 1 and ("text" = 2 or "id" = 3)
Rashomon
  • 5,962
  • 4
  • 29
  • 67
  • Thanks for the answer, but I have no control over the creation of the query. I can only modify the query after it has been created. So I cannot use your solution. – Mike Sep 30 '20 at 14:23
2

One way could be to wrap the original query to be subquery / temporary table:

MyModel.query().from(thirdPartyService.createQuery(userControlledInput)).where(...)

(please let me know if this works at all, I haven't tested it)

Mikael Lepistö
  • 18,909
  • 3
  • 68
  • 70
  • 1
    Yeah, it works and creates a query like this: `select * from (select `todos`.* from `todos` where `text` = ?) where `userId` = ?`. Thank you. – Mike Oct 01 '20 at 16:38