Skip to content

Commit 528d65a

Browse files
committed
sqlite: validate connection after reading options
Signed-off-by: Lazizbek Ergashev <lazerg2@gmail.com>
1 parent 29c517f commit 528d65a

2 files changed

Lines changed: 96 additions & 0 deletions

File tree

src/node_sqlite.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1908,6 +1908,9 @@ void DatabaseSync::CustomFunction(const FunctionCallbackInfo<Value>& args) {
19081908
argc = js_len.As<Int32>()->Value();
19091909
}
19101910

1911+
THROW_AND_RETURN_ON_BAD_STATE(env, !db->IsOpen(), "database is not open");
1912+
THROW_AND_RETURN_IF_IN_AUTHORIZER(env, db);
1913+
19111914
UserDefinedFunction* user_data = new UserDefinedFunction(
19121915
env, fn, BaseObjectWeakPtr<DatabaseSync>(db), use_bigint_args);
19131916
int text_rep = SQLITE_UTF8;
@@ -2070,6 +2073,12 @@ void DatabaseSync::Deserialize(const FunctionCallbackInfo<Value>& args) {
20702073
}
20712074
}
20722075

2076+
THROW_AND_RETURN_ON_BAD_STATE(env, !db->IsOpen(), "database is not open");
2077+
THROW_AND_RETURN_ON_BAD_STATE(
2078+
env,
2079+
db->IsInCallback(),
2080+
"database cannot be deserialized while in a callback");
2081+
20732082
// sqlite3_malloc64 is required because SQLITE_DESERIALIZE_FREEONCLOSE
20742083
// transfers ownership to SQLite, which calls sqlite3_free() on close.
20752084
// See: https://www.sqlite.org/c3ref/deserialize.html
@@ -2229,6 +2238,9 @@ void DatabaseSync::AggregateFunction(const FunctionCallbackInfo<Value>& args) {
22292238
argc = std::max({argc, js_len.As<Int32>()->Value() - 1, 0});
22302239
}
22312240

2241+
THROW_AND_RETURN_ON_BAD_STATE(env, !db->IsOpen(), "database is not open");
2242+
THROW_AND_RETURN_IF_IN_AUTHORIZER(env, db);
2243+
22322244
int text_rep = SQLITE_UTF8;
22332245
if (direct_only) {
22342246
text_rep |= SQLITE_DIRECTONLY;
@@ -2441,6 +2453,8 @@ void Backup(const FunctionCallbackInfo<Value>& args) {
24412453
}
24422454
}
24432455

2456+
THROW_AND_RETURN_ON_BAD_STATE(env, !db->IsOpen(), "database is not open");
2457+
24442458
Local<Promise::Resolver> resolver;
24452459
if (!Promise::Resolver::New(env->context()).ToLocal(&resolver)) {
24462460
return;
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
'use strict';
2+
3+
const { skipIfSQLiteMissing } = require('../common');
4+
skipIfSQLiteMissing();
5+
const tmpdir = require('../common/tmpdir');
6+
const assert = require('node:assert');
7+
const { join } = require('node:path');
8+
const { test } = require('node:test');
9+
const { backup, DatabaseSync } = require('node:sqlite');
10+
11+
tmpdir.refresh();
12+
13+
const closedError = {
14+
code: 'ERR_INVALID_STATE',
15+
message: 'database is not open',
16+
};
17+
18+
// Reading the options bag runs a user getter, so the connection validated on
19+
// entry can already be closed by the time the method reaches SQLite.
20+
test('function() with a getter that closes the database', () => {
21+
const db = new DatabaseSync(':memory:');
22+
const options = {
23+
get useBigIntArguments() {
24+
db.close();
25+
return false;
26+
},
27+
};
28+
29+
assert.throws(() => {
30+
db.function('custom', options, () => 1);
31+
}, closedError);
32+
});
33+
34+
test('aggregate() with a getter that closes the database', () => {
35+
const db = new DatabaseSync(':memory:');
36+
const options = {
37+
start: 0,
38+
step: (acc, value) => acc + value,
39+
get useBigIntArguments() {
40+
db.close();
41+
return false;
42+
},
43+
};
44+
45+
assert.throws(() => {
46+
db.aggregate('custom', options);
47+
}, closedError);
48+
});
49+
50+
test('deserialize() with a getter that closes the database', () => {
51+
const source = new DatabaseSync(':memory:');
52+
source.exec('CREATE TABLE data (value INTEGER)');
53+
const serialized = source.serialize();
54+
source.close();
55+
56+
const db = new DatabaseSync(':memory:');
57+
const options = {
58+
get dbName() {
59+
db.close();
60+
return 'main';
61+
},
62+
};
63+
64+
assert.throws(() => {
65+
db.deserialize(serialized, options);
66+
}, closedError);
67+
});
68+
69+
test('backup() with a getter that closes the database', () => {
70+
const db = new DatabaseSync(':memory:');
71+
db.exec('CREATE TABLE data (value INTEGER)');
72+
const options = {
73+
get rate() {
74+
db.close();
75+
return 1;
76+
},
77+
};
78+
79+
assert.throws(() => {
80+
backup(db, join(tmpdir.path, 'backup.db'), options);
81+
}, closedError);
82+
});

0 commit comments

Comments
 (0)