Prepared statement in an array and bind() for X DevAPI - node.js

I want the statement to search a number of Ids. Like so.
const idsStr = "41, 42, 43";
const sqlStr = `SELECT * FROM table where id IN (${idsStr})`;
session.sql(sqlStr).execute()
But if I use bind method, it only captures the first instance of the string, the remaining values are ignored.
const idsStr = "41, 42, 43";
const sqlStr = `SELECT * FROM table where id IN (?)`;
session.sql(sqlStr).bind(idsStr).execute()
I want to make prepared statement according to the API currently support so as to avoid SQL injection.

This is a limitation of the API (and the X Plugin itself) and a byproduct of the fact that CRUD expressions support an alternative syntax such as IN [41, 42, 43]. Right now, the only way to do what you want is for the SQL statement itself to contain placeholders for all those ids:
const sqlStr = `SELECT * FROM table where id IN (?, ?, ?)
await session.sql(sqlStr).bind(41, 42, 43).execute()
Of course this does not work if you need a dynamic number of elements in the filtering criteria. In that case, you can resort to something like:
const ids = [41, 42, 43]
const sqlStr = `SELECT * FROM table where id IN (${ids.map(() => '?').join(',')})`
await session.sql(sqlStr).bind(ids).execute()
This is probably a bit convoluted but it's the smartest workaround I can think of at the moment.
In the meantime, maybe you can open a bug report at https://bugs.mysql.com/ using the Connector for Node.js category.
Disclaimer: I'm the lead dev of the MySQL X DevAPI Connector for Node.js

Related

Node and SQL Injection by using ${variable} on query string

I was told on a question that Im having a SQL Injection problem.
Here is the question
Node with SQL Server - response with for json path query not responding as expected
and here is my code
let sqlString = `
SELECT codeid, code, validFrom, validTo,
(SELECT dbo.PLprospectAgentCodesComp.productIdentifier, dbo.masterGroupsProducts.productName, dbo.PLprospectAgentCodesComp.compensation
FROM dbo.PLprospectAgentCodesComp INNER JOIN
dbo.masterGroupsProducts ON dbo.PLprospectAgentCodesComp.productIdentifier = dbo.masterGroupsProducts.productIdentifier
WHERE (dbo.PLprospectAgentCodesComp.codeid = dbo.PLprospectAgentCodes.codeid) for json path ) as products
FROM dbo.PLprospectAgentCodes
WHERE (plid = ${userData.plid}) for json path`
let conn = await sql.connect(process.env.DB_CONNSTRING)
let recordset = await conn.query(sqlString)
But I've read at Microsoft, and even on a question on this site, that that format prevents SQL injection.
From MS:
"All values are automatically sanitized against sql injection. This is
because it is rendered as prepared statement, and thus all limitations
imposed in MS SQL on parameters apply. e.g. Column names cannot be
passed/set in statements using variables."
I was trying to use the declare #parameter for the above code, but since my code has several queries that depend one of another, Im using await for each Query... and #parameter is not working. After I process the recordset, other queries will execute.
If my code actually is dangerous for SQL injection, is it possible to sanitize sqlString before the following two lines? The reason I ask is not to change the method in about 50 routes.
let sqlString = `select * from table where userid=${userId}`
Sanitizing code here
let conn = await sql.connect(process.env.DB_CONNSTRING)
let recordset = await conn.query(sqlString)
Thanks.
According to https://tediousjs.github.io/node-mssql/ , "All values are automatically sanitized against sql injection." applies only when you use the ES6 Tagged template literals. You should add the tag sql.query before the template string.
let sqlString = sql.query`select * from mytable where id = ${value}`
For more information on tagged template literals: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates

In clause Nodejs snowflake not getting result set

I am working on snowflake with nodejs. I have used snowflake-sdk.
My raw query is
select * from xyz where x in ('1','2','3').
For this, in node.js, i had written query as connection.execute({ sqlText: select * from xyz where x in (:1), binds: [] })
what should I pass in binds and in which format, I am not getting an idea for it?
Please review the node.js driver documentation which provides a sample for the bind operations : https://docs.snowflake.com/en/user-guide/nodejs-driver-use.html#binding-statement-parameters
Note: not compiled or tested the below, but it's based on a technique we've used
It isn't possible to directly bind the array of values, but the following works:
var params = ['1', '2', '3'];
var statement = select * from xyz where id in (${params.map(x => '?').join()});
// statement will now be:
// select * from xyz where id in (?, ?, ?)
connection.execute({ sqlText: statements, binds: params })

How to construct a `select ... in` SQL query in nim?

I'm using nim and db_sqlite to fetch some rows with certain _ids from a database table. For example:
for row in db.fastRows("SELECT * FROM t WHERE _id IN (?, ?)", #["1", "2"]):
echo row
This works as expected, however, the sequence at the end is constructed dynamically at runtime, which means I need a variable amount of ? in the query. I end up creating a sequence with question marks, joining them, interpolating the string and turning it into a database query:
var qs : seq[string]
for id in ids:
qs.add("?")
let query_string = """SELECT * FROM t WHERE _id IN ($1)""" % join(qs, ",")
let query = SqlQuery(query_string)
for row in db.fastRows(query, ids):
echo row
Is there a better way to construct a select ... in query in nim? Ideally one with just one ? in the SqlQuery.
(For what it's worth, the current behavior is similar to other languages I've used)
you could do the replacement manually, here's one way using strformat and map
import strformat,db_sqlite,sequtils,strutils
#assuming ids is a seq[string] here
let query = sql(&"SELECT * FROM t WHERE _id IN ({ids.map(dbQuote).join(\",\")})")
for row in db.fastRows(query):
echo row

Pass column name as argument - Postgres and Node JS

I have a query (Update statement) wrapped in a function and will need to perform the same statement on multiple columns during the course of my script
async function update_percentage_value(value, id){
(async () => {
const client = await pool.connect();
try {
const res = await client.query('UPDATE fixtures SET column_1_percentage = ($1) WHERE id = ($2) RETURNING *', [value, id]);
} finally {
client.release();
}
})().catch(e => console.log(e.stack))
}
I then call this function
update_percentage_value(50, 2);
I have many columns to update at various points of my script, each one needs to be done at the time. I would like to be able to just call the one function, passing the column name, value and id.
My table looks like below
CREATE TABLE fixtures (
ID SERIAL PRIMARY KEY,
home_team VARCHAR,
away_team VARCHAR,
column_1_percentage INTEGER,
column_2_percentage INTEGER,
column_3_percentage INTEGER,
column_4_percentage INTEGER
);
Is it at all possible to do this?
I'm going to post the solution that was advised by Sehrope Sarkuni via the node-postgres GitHub repo. This helped me a lot and works for what I require:
No column names are identifiers and they can't be specified as parameters. They have to be included in the text of the SQL command.
It is possible but you have to build the SQL text with the column names. If you're going to dynamically build SQL you should make sure to escape the components using something like pg-format or use an ORM that handles this type of thing.
So something like:
const format = require('pg-format');
async function updateFixtures(id, column, value) {
const sql = format('UPDATE fixtures SET %I = $1 WHERE id = $2', column);
await pool.query(sql, [value, id]);
}
Also if you're doing multiple updates to the same row back-to-back then you're likely better off with a single UPDATE statement that modifies all the columns rather than separate statements as they'd be both slower and generate more WAL on the server.
To get the column names of the table, you can query the information_schema.columns table which stores the details of column structure of your table, this would help you in framing a dynamic query for updating a specific column based on a specific result.
You can get the column names of the table with the help of following query:
select column_name from information_schema.columns where table_name='fixtures' and table_schema='public';
The above query would give you the list of columns in the table.
Now to update each one for a specific purpose, You can store the result set of column name to a variable and pass that variable to the function to perform the required action.

"non-integer constant in ORDER BY" when using pg-promise with named parameters

I am trying to write a simple query using the pgp-promise library. My original implementation looks like:
var bar = function(orderBy){
var qs = 'select * from mytable order by ${orderBy};';
return db.many(qs,{orderBy:orderBy});
}
...
bar('id').then(...)
But this gives an error of non-integer constant in ORDER BY
I have also tried adding quotes aroung ${orderBy} and adding double quotes to the orderBy paramater to no avail. I have a working solution by doing var qs = 'select * from mytable order by "' + orderBy + '";' though it should be obvious why I don't want code like that in the project.
My question: Is there a way to get pg-promise to build a query with an order by clause that isn't vulnerable to sql injection?
Is there a way to get pg-promise to build a query with an order by clause that isn't vulnerable to sql injection?
The value for ORDER BY clause is an SQL name, and it is to be formatted using SQL Names:
const bar = function(orderBy) {
const qs = 'select * from mytable order by ${orderBy:name}';
return db.many(qs, {orderBy});
}
whereas :raw / ^ is injecting raw text, which is vulnerable to SQL injections when it comes from outside, and to be used only for strings that have been created and pre-formatted inside the server.

Resources