Objection.js: Can all where clauses be enclosed in parentheses after they have been added?

2.8k Views Asked by At
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);
2

There are 2 best solutions below

1
On BEST ANSWER

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)

1
On

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)