I have a search query that its parameters changes depending on the client input.
await prisma.$queryRaw(`SELECT column FROM table ${condition ? `WHERE column = '${condition}'` :' ' } `)
how can I write this query using prepared statement and avoiding duplicate queries. The only solution I came up with is the following:
const result = condition ? await prisma.$queryRaw(`SELECT column FROM table WHERE column = $1`,condition) : await prisma.$queryRaw(`SELECT column FROM table`)
The goal from this is to avoid sql injections from the first query.
EDIT
after trying the solution suggested by #Ryan I got the following error:
Raw query failed. Code: `22P03`. Message: `db error: ERROR: incorrect binary data format in bind parameter 1`
here's my implementation:
const where = Prisma.sql`WHERE ${searchConditions.join(' AND ')}`;
const fetchCount = await prisma.$queryRaw`
SELECT
COUNT(id)
FROM
table
${searchConditions.length > 0 ? where : Prisma.empty}
`;
that will translate to the following in the prisma logs:
Query:
SELECT
COUNT(id)
FROM
table
WHERE $1
["column = something"]
SOLUTION
I had to do a lot of rework to achieve what I want. Here's the idea behind it:
for every search condition you need to do the following:
let queryCondition = Prisma.empty;
if (searchFilter) {
const searchFilterCondition = Prisma.sql`column = ${searchFilter}`;
queryCondition.sql.length > 0
? (queryCondition = Prisma.sql`${queryCondition} AND ${streamingUnitCondition}`)
: (queryCondition = searchFilterCondition);
}
afterwards in the final search query you can do something of this sort:
SELECT COUNT(*) FROM table ${queryCondition.sql.length > 0 ? Prisma.sql`WHERE ${queryCondition}` : Prisma.empty}
You can do it like this:
import { Prisma } from '#prisma/client'
const where = Prisma.sql`where column = ${condition}`
const result = await prisma.$queryRaw`SELECT column FROM table ${condition ? where : Prisma.empty}`
Here is my working version, using Prima.join :
import { Prisma } from '#prisma/client'
const searchConditions: Prisma.Sql[] = []
if (q) {
searchConditions.push(Prisma.sql`column = ${q}`)
}
const where = searchConditions.length ?
Prisma.sql`where ${Prisma.join(searchConditions, ' and ')}` :
Prisma.empty
await prisma.$queryRaw(
Prisma.sql`
select *
from table
${where}
`
)
Related
I'm making a query like this with Knex.
const query = knex
.select('ue.username', 'ue.permission', 'ue.status')
.from('user_entity as ue')
.join('device_entity as de', function () {
this
.on('de.username', '=', 'ue.username')
.andOn({'de.deleted': ''})
.andOn({'de.token': token})
.andOn({'de.uid': uid});
})
.where({'ue.deleted': ''})
Node.js Console shows just that.
in token condition for some reason generates with (´) and replacing (´ ´) with (" ") the query works normally. But I don't understand why he generates the SQL with (´ ´)
select `ue`.`username`, `ue`.`permission`, `ue`.`status`
from `user_entity` as `ue`
inner join `device_entity` as `de`
on `de`.`username` = `ue`.`username`
and `de`.`deleted` = ``
and `de`.`token` = `C0AbKXv2ffWS7w`
and `de`.`uid` = `F8F9C9A8-06CC-40E4-AE8E-75FE7FA6BB57`
where `ue`.`deleted` = ''
How SQL should be...
select `ue`.`username`, `ue`.`permission`, `ue`.`status`
from `user_entity` as `ue`
inner join `device_entity` as `de`
on `de`.`username` = `ue`.`username`
and `de`.`deleted` = ''
and `de`.`token` = 'C0AbKXv2ffWS7w'
and `de`.`uid` = 'F8F9C9A8-06CC-40E4-AE8E-75FE7FA6BB57'
where `ue`.`deleted` = ''
a query with inner join but Knex generates in "Inner Join in condition (and)" with (´ ´) instead of (" ").
on clause basically parses everything into column names, so all your values wrapped with `, which is for mysql identifier such as table name or column name.
If you use typescript, or javascript IDE that supports parsing typescript syntax like vscode, you can check out type definitions for .join().
interface JoinClause {
on(raw: Raw): JoinClause;
on(callback: JoinCallback): JoinClause;
on(columns: { [key: string]: string | Raw }): JoinClause; // <--------
on(column1: string, column2: string): JoinClause;
on(column1: string, raw: Raw): JoinClause; // <-----------------------
...
}
As you can see, string parameters are column*. If you want to add condition according to non-column value, use should pass Raw value via knex.raw().
knex("user_entity as ue")
.select('ue.username', 'ue.permission', 'ue.status')
.join('device_entity as de', (j)=> (
j
.on('de.username', '=', 'ue.username')
.andOn({'de.deleted': knex.raw("?","")})
.andOn({'de.token': knex.raw("?","iMoENdVC5hSUwr")})
.andOn({'de.uid': knex.raw("?","TPB4.220624.004")})
))
.where({'ue.deleted': ''})
Knex Playground
I'm looking for a way to dynamically build a SQL query for an unknown number of API parameters that may come back. As a simple example, consider the following query:
router.get("/someEndpoint", authorizationFunction, async (req, res) => {
let sql = `
SELECT *
FROM some_table
WHERE username = $1
${req.query.since !== undefined ? "AND hire_date >= $2" : ""}
`
const results = await pool.query(sql, [req.query.user, req.query.since]);
}
where pool is defined as
const Pool = require("pg").Pool;
const pool = new Pool({<connection parameters>});
The problem I'm having is that if req.query.since is not provided, then the SQL query only requires a single bound parameter ($1). This is presented as an error that says bind message supplies 2 parameters, but prepared statement "" requires 1. Since I don't know which parameters a user will provide until the time of the query, I'm under the impression that I need to provide all possible value, and let the query figure it out.
I've seen a lot of posts that point to pg-promise as a solution, but I'm wondering if that's necessary. Is there a way that I can solve this with my current setup? Perhaps I'm thinking about the problem incorrectly?
Thanks in advance!
Add a trivial expression text that contains $2 and evaluates to true instead of "", for example
SELECT * FROM some_table WHERE username = $1 AND
${req.query.since !== undefined ? " hire_date >= $2": " (true or $2 = $2)"}
The planner will remove it anyway. Added true or just in case $2 is null.
Still it would be cleaner like this
if (req.query.since !== undefined)
{
let sql = `SELECT * FROM some_table WHERE username = $1 AND hire_date >= $2`;
const results = await pool.query(sql, [req.query.user, req.query.since]);
}
else
{
let sql = `SELECT * FROM some_table WHERE username = $1`;
const results = await pool.query(sql, [req.query.user]);
}
What about SQLi risk BTW?
I am writing code using nestjs and typeorm.
However, the way I write is vulnerable to SQL injection, so I am changing the code.
//Before
.where(`userId = ${userId}`)
//After
.where(`userId = :userId` , {userId:userId})
I am writing a question because I was changing the code and couldn't find a way to change it for a few cases.
//CASE1
const query1 = `select id, 'normal' as type from user where id = ${userId}`;
const query2 = `select id, 'doctor' as type from doctor where id = ${userId}`;
const finalQuery = await getConnection().query(`select id, type from (${query1} union ${query2}) as f limit ${limit} offset ${offset};`);
//CASE2
...
.addSelect(`CASE WHEN userRole = '${userRole}' THEN ...`, 'userType')
...
//CASE3 -> to find search results in order of accuracy
...
.orderBy(`((LENGTH(user.name) - LENGTH((REPLACE(user.name, '${keyword.replace( / /g, '', )}', '')))) / LENGTH('${keyword.replace(/ /g, '')}'))`,
'ASC')
...
//CASE4
let query = 'xxxx';
let whereQuery = '';
for(const i=0;i<5;i++)
whereQuery += ' or ' + `user.name like '%${keyword}%'`
query.where(whereQuery)
I cannot use parameter in the select function.
In the above case, I am wondering how to change it.
Is it ok to not have to modify the select code?
I am new to node.js and it seems like a simple problem but I have a parametrised variable which I need to put in quotes for the value to appear as it is appering in oracle server.
How do I add this?
Here is the nodejs code:
export const orderItemCRQry = async (coil: any) => {
try {
const sql = `SELECT ccl_id_order, ccl_id_order_item FROM v_cold_coil WHERE
ccl_id_coil = :coil_id`;
const binds = [`${coil}`];
return await query(sql, binds);
} catch (error) {
console.log(error);
throw new Error.InternalServerError("Error!");
}
};
This is the query section. I need to put :coil_id in quotes to get a value when I put a static variable like '001300' in quotes it gives the correct value however if I want to put coil id like this ':coil_id' it gives bad request . How do I put quotes there for the parametrised query to work
In oraclesql:
when I run the query:
SELECT ccl_id_order, ccl_id_order_item FROM v_cold_coil WHERE
ccl_id_coil = '001300'
like this it works
and If I remove the quotes I get no value. There fore I need to add quotes to the default 001300 value which I am getting . How do I do that?
You don't need quotes when using bind variables. The following snippet:
const coil = 'abc';
const result = await connection.execute(
`SELECT * FROM DUAL where 'abc' = :coil_id`,
[`${coil}`]);
console.dir(result.rows, { depth: null });
gives:
[ [ 'X' ] ]
showing that the WHERE condition was satisfied.
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);
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)
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)