Skip to content

Commit a2a0ac2

Browse files
authored
protect against unsafe sql.fragment usage (#602)
* in typescript: protects using types * in javascript: protects by checking for strings.raw Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#raw_strings Closes #589 Co-authored-by: alxndrsn <alxndrsn>
1 parent 53c3258 commit a2a0ac2

File tree

2 files changed

+19
-3
lines changed

2 files changed

+19
-3
lines changed

packages/sql-tag/src/factories/createSqlTag.test/sql.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { FragmentToken } from '../../tokens';
22
import { createSqlTag } from '../createSqlTag';
3+
import { InvalidInputError } from '@slonik/errors';
34
import anyTest, { type TestFn } from 'ava';
45
import { ROARR } from 'roarr';
56

@@ -17,6 +18,15 @@ test.beforeEach((t) => {
1718
};
1819
});
1920

21+
test('throws error if called as a function', (t) => {
22+
const error = t.throws(() => {
23+
// @ts-expect-error - intentional
24+
sql.fragment([`SELECT 1`]);
25+
});
26+
27+
t.true(error instanceof InvalidInputError);
28+
});
29+
2030
test('creates an object describing a query', (t) => {
2131
const query = sql.fragment`SELECT 1`;
2232

packages/sql-tag/src/factories/createSqlTag.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,15 @@ const log = Logger.child({
3232
});
3333

3434
const createFragment = (
35-
parts: readonly string[],
35+
parts: TemplateStringsArray,
3636
values: readonly ValueExpression[],
3737
) => {
38+
if (!Array.isArray(parts.raw) || !Object.isFrozen(parts.raw)) {
39+
throw new InvalidInputError(
40+
'Function must be called as a template literal.',
41+
);
42+
}
43+
3844
let rawSql = '';
3945

4046
const parameterValues: PrimitiveValueExpression[] = [];
@@ -181,7 +187,7 @@ export const createSqlTag = <
181187
},
182188
type: (parser) => {
183189
return (
184-
parts: readonly string[],
190+
parts: TemplateStringsArray,
185191
...args: readonly ValueExpression[]
186192
) => {
187193
return Object.freeze({
@@ -199,7 +205,7 @@ export const createSqlTag = <
199205
}
200206

201207
return (
202-
parts: readonly string[],
208+
parts: TemplateStringsArray,
203209
...args: readonly ValueExpression[]
204210
) => {
205211
return Object.freeze({

0 commit comments

Comments
 (0)