Skip to content

Commit d435edb

Browse files
authored
fix: ensure correct behavior for Falsy params (#16)
* fix: ensure correct behavior for falsy `stringifyObjects` values * test: remove reduntant tests * docs: update benchmark results * docs: prefer realistic benchmark results * test: add `escape` test cases
1 parent 6ef029a commit d435edb

4 files changed

Lines changed: 145 additions & 7 deletions

File tree

README.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,12 @@ deno add npm:sql-escaper
5555

5656
### [MySQL2](https://github.com/sidorares/node-mysql2)
5757

58-
🚧 For **MySQL2**, it already uses **SQL Escaper** as its default escaping library since version `3.17.0`, so you just need to update it to the latest version:
58+
For **MySQL2**, it already uses **SQL Escaper** as its default escaping library since version `3.17.0`, so you just need to update it to the latest version:
5959

6060
```bash
61-
npm i mysql2@latest # soon
61+
npm i mysql2@latest
6262
```
6363

64-
- Check the progress migration in [sidorares/node-mysql2#4054](https://github.com/sidorares/node-mysql2/pull/4054).
65-
6664
### [mysqljs/mysql](https://github.com/mysqljs/mysql)
6765

6866
You can use an overrides in your _package.json_:
@@ -83,6 +81,8 @@ You can use an overrides in your _package.json_:
8381

8482
## Usage
8583

84+
For _up-to-date_ documentation, always follow the [**README.md**](https://github.com/mysqljs/sql-escaper?tab=readme-ov-file#readme) in the **GitHub** repository.
85+
8686
### Quickstart
8787

8888
```js
@@ -104,8 +104,6 @@ escape(raw('NOW()'));
104104
// => 'NOW()'
105105
```
106106

107-
> For _up-to-date_ documentation, always follow the [**README.md**](https://github.com/mysqljs/sql-escaper?tab=readme-ov-file#readme) in the **GitHub** repository.
108-
109107
### Import
110108

111109
#### ES Modules

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ export const format = (
464464
) {
465465
escapedValue = objectToValues(currentValue, timezone);
466466
setIndex = -1;
467-
} else escapedValue = escape(currentValue, stringifyObjects, timezone);
467+
} else escapedValue = escape(currentValue, true, timezone);
468468
} else escapedValue = escape(currentValue, stringifyObjects, timezone);
469469

470470
result += sql.slice(chunkIndex, placeholderPosition);

test/escape.test.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { assert, describe, it } from 'poku';
2+
import { escape } from '../src/index.ts';
3+
4+
describe("Can't bypass via object injection using escape directly", () => {
5+
const value = { password: 1 };
6+
const expected = "'[object Object]'";
7+
8+
it('should stringify object when stringifyObjects is true', () => {
9+
assert.strictEqual(escape(value, true), expected);
10+
});
11+
12+
it('should stringify object when stringifyObjects is false', () => {
13+
assert.strictEqual(escape(value, false), expected);
14+
});
15+
16+
it('should stringify object when stringifyObjects is 0', () => {
17+
// @ts-expect-error: testing 0 as a falsy runtime value
18+
assert.strictEqual(escape(value, 0), expected);
19+
});
20+
21+
it('should stringify object when stringifyObjects is empty string', () => {
22+
// @ts-expect-error: testing empty string as a falsy runtime value
23+
assert.strictEqual(escape(value, ''), expected);
24+
});
25+
});
26+
27+
describe('Object expansion when stringifyObjects is nullish', () => {
28+
const value = { password: 1 };
29+
const expanded = '`password` = 1';
30+
31+
it('should expand object when stringifyObjects is undefined', () => {
32+
assert.strictEqual(escape(value, undefined), expanded);
33+
});
34+
35+
it('should expand object when stringifyObjects is null', () => {
36+
// @ts-expect-error: testing null as a falsy runtime value
37+
assert.strictEqual(escape(value, null), expanded);
38+
});
39+
40+
it('should expand object when stringifyObjects is omitted', () => {
41+
assert.strictEqual(escape(value), expanded);
42+
});
43+
});
44+
45+
describe('Safe object to key-value expansion for SET clauses', () => {
46+
it('should expand single key-value pair', () => {
47+
assert.strictEqual(escape({ name: 'foo' }), "`name` = 'foo'");
48+
});
49+
50+
it('should expand multiple key-value pairs', () => {
51+
assert.strictEqual(
52+
escape({ name: 'foo', email: 'bar@test.com' }),
53+
"`name` = 'foo', `email` = 'bar@test.com'"
54+
);
55+
});
56+
57+
it('should expand mixed value types', () => {
58+
assert.strictEqual(
59+
escape({ name: 'foo', active: true, age: 30 }),
60+
"`name` = 'foo', `active` = true, `age` = 30"
61+
);
62+
});
63+
64+
it('should skip function values', () => {
65+
assert.strictEqual(escape({ name: 'foo', fn: () => {} }), "`name` = 'foo'");
66+
});
67+
68+
it('should return empty string for empty object', () => {
69+
assert.strictEqual(escape({}), '');
70+
});
71+
});

test/falsy.test.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { assert, describe, it } from 'poku';
2+
import { format } from '../src/index.ts';
3+
4+
describe('Safe SET with object parameter', () => {
5+
const sql = 'UPDATE users SET ?';
6+
const values = [{ name: 'foo', email: 'bar@test.com' }];
7+
const expected = "UPDATE users SET `name` = 'foo', `email` = 'bar@test.com'";
8+
9+
it('should expand object to key-value pairs when stringifyObjects is undefined', () => {
10+
assert.strictEqual(format(sql, values, undefined), expected);
11+
});
12+
13+
it('should expand object to key-value pairs when stringifyObjects is null', () => {
14+
// @ts-expect-error: testing null as a falsy runtime value
15+
assert.strictEqual(format(sql, values, null), expected);
16+
});
17+
18+
it('should expand object to key-value pairs when stringifyObjects is false', () => {
19+
assert.strictEqual(format(sql, values, false), expected);
20+
});
21+
22+
it('should expand object to key-value pairs when stringifyObjects is 0', () => {
23+
// @ts-expect-error: testing 0 as a falsy runtime value
24+
assert.strictEqual(format(sql, values, 0), expected);
25+
});
26+
27+
it('should expand object to key-value pairs when stringifyObjects is empty string', () => {
28+
// @ts-expect-error: testing empty string as a falsy runtime value
29+
assert.strictEqual(format(sql, values, ''), expected);
30+
});
31+
32+
it('should expand object to key-value pairs when stringifyObjects is omitted', () => {
33+
assert.strictEqual(format(sql, values), expected);
34+
});
35+
});
36+
37+
describe("Can't bypass via object password injection", () => {
38+
const sql = 'SELECT * FROM `users` WHERE `username` = ? AND `password` = ?';
39+
const values: [string, { password: boolean }] = ['admin', { password: true }];
40+
const expected =
41+
"SELECT * FROM `users` WHERE `username` = 'admin' AND `password` = '[object Object]'";
42+
43+
it('should not generate a SQL fragment when stringifyObjects is undefined', () => {
44+
assert.strictEqual(format(sql, values, undefined), expected);
45+
});
46+
47+
it('should not generate a SQL fragment when stringifyObjects is null', () => {
48+
// @ts-expect-error: testing null as a falsy runtime value
49+
assert.strictEqual(format(sql, values, null), expected);
50+
});
51+
52+
it('should not generate a SQL fragment when stringifyObjects is false', () => {
53+
assert.strictEqual(format(sql, values, false), expected);
54+
});
55+
56+
it('should not generate a SQL fragment when stringifyObjects is 0', () => {
57+
// @ts-expect-error: testing 0 as a falsy runtime value
58+
assert.strictEqual(format(sql, values, 0), expected);
59+
});
60+
61+
it('should not generate a SQL fragment when stringifyObjects is empty string', () => {
62+
// @ts-expect-error: testing empty string as a falsy runtime value
63+
assert.strictEqual(format(sql, values, ''), expected);
64+
});
65+
66+
it('should not generate a SQL fragment when stringifyObjects is omitted', () => {
67+
assert.strictEqual(format(sql, values), expected);
68+
});
69+
});

0 commit comments

Comments
 (0)