@nearform/sql
Advanced tools
Comparing version 1.6.0 to 1.7.0
{ | ||
"name": "@nearform/sql", | ||
"version": "1.6.0", | ||
"version": "1.7.0", | ||
"description": "SQL injection protection module", | ||
@@ -5,0 +5,0 @@ "main": "./SQL.js", |
@@ -156,4 +156,8 @@ # SQL | ||
Does a literal interpolation of the provided value, interpreting the provided value as-is and additionally wrapping it in double quotes. It uses `unsafe` internally. | ||
Mimics the native PostgreSQL `quote_ident` and MySQL `quote_identifier` functions. | ||
In PostgreSQL, it wraps the provided value in double quotes `"` and escapes any double quotes existing in the provided value. | ||
In MySQL, it wraps the provided value in backticks `` ` `` and escapes any backticks existing in the provided value. | ||
It's convenient to use when schema, table or field names are dynamic and can't be hardcoded in the SQL query string. | ||
@@ -160,0 +164,0 @@ |
27
SQL.js
'use strict' | ||
const inspect = Symbol.for('nodejs.util.inspect.custom') | ||
const unsafe = Symbol('unsafe') | ||
const wrapped = Symbol('wrapped') | ||
const quoteIdentifier = require('./quoteIdentifier') | ||
class SqlStatement { | ||
@@ -55,4 +57,4 @@ constructor (strings, values) { | ||
if (valueContainer && valueContainer[unsafe]) { | ||
text += `${valueContainer.value}${this.strings[i]}` | ||
if (valueContainer && valueContainer[wrapped]) { | ||
text += `${valueContainer.transform(type)}${this.strings[i]}` | ||
values.splice(valueIndex, 1) | ||
@@ -79,4 +81,4 @@ valueOffset-- | ||
let quote = "'" | ||
if (data && data[unsafe]) { | ||
data = data.value | ||
if (data && data[wrapped]) { | ||
data = data.transform() | ||
quote = '' | ||
@@ -104,3 +106,3 @@ } | ||
get values () { | ||
return this._values.filter(v => !v || !v[unsafe]) | ||
return this._values.filter(v => !v || !v[wrapped]) | ||
} | ||
@@ -154,5 +156,12 @@ | ||
module.exports.unsafe = value => ({ | ||
value, | ||
[unsafe]: true | ||
transform () { | ||
return value | ||
}, | ||
[wrapped]: true | ||
}) | ||
module.exports.quoteIdent = value => module.exports.unsafe(`"${value}"`) | ||
module.exports.quoteIdent = value => ({ | ||
transform (type) { | ||
return quoteIdentifier(value, type) | ||
}, | ||
[wrapped]: true | ||
}) |
@@ -10,3 +10,3 @@ 'use strict' | ||
test('SQL helper - build complex query with append', t => { | ||
test('SQL helper - build complex query with append', async t => { | ||
const name = 'Team 5' | ||
@@ -33,6 +33,5 @@ const description = 'description' | ||
t.same(sql.values, [name, description, teamId, organizationId]) | ||
t.end() | ||
}) | ||
test('SQL helper - multiline', t => { | ||
test('SQL helper - multiline', async t => { | ||
const name = 'Team 5' | ||
@@ -61,7 +60,5 @@ const description = 'description' | ||
t.same(sql.values, [name, description, teamId, organizationId]) | ||
t.end() | ||
}) | ||
test('SQL helper - multiline with emtpy lines', t => { | ||
test('SQL helper - multiline with emtpy lines', async t => { | ||
const name = 'Team 5' | ||
@@ -92,6 +89,5 @@ const description = 'description' | ||
t.same(sql.values, [name, description, teamId, organizationId]) | ||
t.end() | ||
}) | ||
test('SQL helper - build complex query with glue', t => { | ||
test('SQL helper - build complex query with glue', async t => { | ||
const name = 'Team 5' | ||
@@ -124,6 +120,5 @@ const description = 'description' | ||
t.same(sql.values, [name, description, teamId, organizationId]) | ||
t.end() | ||
}) | ||
test('SQL helper - build complex query with glue - regression #13', t => { | ||
test('SQL helper - build complex query with glue - regression #13', async t => { | ||
const name = 'Team 5' | ||
@@ -144,6 +139,5 @@ const ids = [1, 2, 3].map(id => SQL`${id}`) | ||
t.same(sql.values, [name, 1, 2, 3]) | ||
t.end() | ||
}) | ||
test('SQL helper - build complex query with glue - regression #17', t => { | ||
test('SQL helper - build complex query with glue - regression #17', async t => { | ||
const ids = [1, 2, 3].map(id => SQL`(${id})`) | ||
@@ -158,6 +152,5 @@ | ||
t.same(sql.values, [1, 2, 3]) | ||
t.end() | ||
}) | ||
test('SQL helper - build complex query with static glue - regression #17', t => { | ||
test('SQL helper - build complex query with static glue - regression #17', async t => { | ||
const ids = [1, 2, 3].map(id => SQL`(${id})`) | ||
@@ -172,6 +165,5 @@ | ||
t.same(sql.values, [1, 2, 3]) | ||
t.end() | ||
}) | ||
test('SQL helper - build complex query with append and glue', t => { | ||
test('SQL helper - build complex query with append and glue', async t => { | ||
const updates = [] | ||
@@ -210,6 +202,5 @@ const v1 = 'v1' | ||
t.same(sql.values, [v1, v2, v3, v4, v5, v6, v7]) | ||
t.end() | ||
}) | ||
test('SQL helper - build complex query with append', t => { | ||
test('SQL helper - build complex query with append', async t => { | ||
const v1 = 'v1' | ||
@@ -245,6 +236,5 @@ const v2 = 'v2' | ||
t.same(sql.values, [v1, v2, v3, v4, v5, v6, v7]) | ||
t.end() | ||
}) | ||
test('SQL helper - build complex query with append passing simple strings and template strings', t => { | ||
test('SQL helper - build complex query with append passing simple strings and template strings', async t => { | ||
const v1 = 'v1' | ||
@@ -278,6 +268,5 @@ const v2 = 'v2' | ||
t.same(sql.values, [v1, v2, v3, v4, v5, v6, v7]) | ||
t.end() | ||
}) | ||
test('SQL helper - will throw an error if append is called without using SQL', t => { | ||
test('SQL helper - will throw an error if append is called without using SQL', async t => { | ||
const sql = SQL`TEST QUERY glue pieces FROM ` | ||
@@ -293,6 +282,5 @@ try { | ||
} | ||
t.end() | ||
}) | ||
test('SQL helper - build string using append with and without unsafe flag', t => { | ||
test('SQL helper - build string using append with and without unsafe flag', async t => { | ||
const v2 = 'v2' | ||
@@ -316,6 +304,5 @@ const longName = 'whateverThisIs' | ||
t.ok(sql.values.includes(v2)) | ||
t.end() | ||
}) | ||
test('SQL helper - build string using append and only unsafe', t => { | ||
test('SQL helper - build string using append and only unsafe', async t => { | ||
const v2 = 'v2' | ||
@@ -344,7 +331,5 @@ const longName = 'whateverThisIs' | ||
) | ||
t.end() | ||
}) | ||
test('SQL helper - handles js null values as valid `null` sql values', t => { | ||
test('SQL helper - handles js null values as valid `null` sql values', async t => { | ||
const name = null | ||
@@ -359,11 +344,9 @@ const id = 123 | ||
t.same(sql.values, [name, id]) | ||
t.end() | ||
}) | ||
test('SQL helper - throws when building an sql string with an `undefined` value', t => { | ||
test('SQL helper - throws when building an sql string with an `undefined` value', async t => { | ||
t.throws(() => SQL`UPDATE teams SET name = ${undefined}`) | ||
t.end() | ||
}) | ||
test('empty append', t => { | ||
test('empty append', async t => { | ||
const sql = SQL`UPDATE teams SET name = ${'team'}`.append() | ||
@@ -375,30 +358,27 @@ | ||
t.same(sql.values, ['team']) | ||
t.end() | ||
}) | ||
test('inspect', t => { | ||
test('inspect', async t => { | ||
const sql = SQL`UPDATE teams SET name = ${'team'}` | ||
t.equal(util.inspect(sql), "SQL << UPDATE teams SET name = 'team' >>") | ||
t.end() | ||
}) | ||
test('quoteIdent', t => { | ||
const table = 'teams' | ||
const name = 'name' | ||
const id = 123 | ||
test('quoteIdent', async t => { | ||
t.test('simple', async t => { | ||
const table = 'teams' | ||
const name = 'name' | ||
const id = 123 | ||
const sql = SQL`UPDATE ${quoteIdent( | ||
table | ||
)} SET name = ${name} WHERE id = ${id}` | ||
const sql = SQL`UPDATE ${quoteIdent( | ||
table | ||
)} SET name = ${name} WHERE id = ${id}` | ||
t.equal(sql.text, 'UPDATE "teams" SET name = $1 WHERE id = $2') | ||
t.equal(sql.sql, 'UPDATE "teams" SET name = ? WHERE id = ?') | ||
t.equal(sql.debug, `UPDATE "teams" SET name = 'name' WHERE id = ${id}`) | ||
t.same(sql.values, [name, id]) | ||
t.end() | ||
t.equal(sql.text, 'UPDATE "teams" SET name = $1 WHERE id = $2') | ||
t.equal(sql.sql, 'UPDATE `teams` SET name = ? WHERE id = ?') | ||
t.equal(sql.debug, `UPDATE "teams" SET name = 'name' WHERE id = ${id}`) | ||
t.same(sql.values, [name, id]) | ||
}) | ||
}) | ||
test('unsafe', t => { | ||
test('unsafe', async t => { | ||
const name = 'name' | ||
@@ -413,6 +393,5 @@ const id = 123 | ||
t.same(sql.values, [id]) | ||
t.end() | ||
}) | ||
test('should be able to append query that is using "{ unsafe: true }"', t => { | ||
test('should be able to append query that is using "{ unsafe: true }"', async t => { | ||
const table = 'teams' | ||
@@ -444,7 +423,5 @@ const id = 123 | ||
t.same(sql.values, [id]) | ||
t.end() | ||
}) | ||
test('should be able to append query that is using "quoteIdent(...)"', t => { | ||
test('should be able to append query that is using "quoteIdent(...)"', async t => { | ||
const table = 'teams' | ||
@@ -465,3 +442,3 @@ const id = 123 | ||
sql.sql, | ||
'SELECT * FROM "teams" INNER JOIN (SELECT id FROM "teams" WHERE id = ?) as t2 ON t2.id = id' | ||
'SELECT * FROM `teams` INNER JOIN (SELECT id FROM `teams` WHERE id = ?) as t2 ON t2.id = id' | ||
) | ||
@@ -473,3 +450,2 @@ t.equal( | ||
t.same(sql.values, [id]) | ||
t.end() | ||
}) |
@@ -15,8 +15,13 @@ { | ||
"url": "http://localhost:8080/users", | ||
"method": "POST", | ||
"headers": "Content-Type:application/json", | ||
"params": "username,password,email", | ||
"data": "{\"username\":\"user\",\"password\":\"12345\",\"email\":\"user@email.com\"}" | ||
}, | ||
{ | ||
"url": "http://localhost:8080/table", | ||
"headers": "Content-Type:application/json", | ||
"params": "tableName,field1,field2", | ||
"data": "{\"tableName\":\"some_table\",\"field1\":\"id\",\"field2\":\"username\"}" | ||
} | ||
] | ||
} |
const users = require('./users') | ||
const table = require('./table') | ||
@@ -9,2 +10,3 @@ const fastify = require('fastify')({ | ||
fastify.register(users) | ||
fastify.register(table) | ||
@@ -11,0 +13,0 @@ const start = async () => { |
@@ -38,3 +38,2 @@ const jsonfile = require('jsonfile') | ||
`--url=${urlDescription.url}`, | ||
`--method=${urlDescription.method}`, | ||
`--level=${config.level}`, | ||
@@ -50,2 +49,6 @@ `--risk=${config.risk}`, | ||
if (urlDescription.method) { | ||
params.push(`--method=${urlDescription.method}`) | ||
} | ||
if (urlDescription.headers) { | ||
@@ -52,0 +55,0 @@ params.push(`--headers=${urlDescription.headers}`) |
const SQL = require('../SQL') | ||
const quoteIdent = SQL.quoteIdent | ||
@@ -22,3 +23,3 @@ const tableName = 'users' | ||
const result = await fastify.pg.query( | ||
SQL`INSERT INTO ${SQL.quoteIdent( | ||
SQL`INSERT INTO ${quoteIdent( | ||
tableName | ||
@@ -25,0 +26,0 @@ )} (username, email, password) VALUES (${username},${email},${password})` |
48883
26
961
244