Skip to content

Commit e20da72

Browse files
authored
sqlite: validate backup() source is a Database
backup() only checked that the source argument was a BaseObject before casting it to Database, so passing a Statement or Session caused a type confusion. Check against the Database constructor template instead. Signed-off-by: Guilherme Araújo <arauujogui@gmail.com> PR-URL: #66797 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
1 parent c7ae7ab commit e20da72

3 files changed

Lines changed: 73 additions & 45 deletions

File tree

‎src/env_properties.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,7 @@
451451
V(socketaddress_constructor_template, v8::FunctionTemplate) \
452452
V(space_stats_template, v8::DictionaryTemplate) \
453453
V(sqlite_column_template, v8::DictionaryTemplate) \
454+
V(sqlite_database_constructor_template, v8::FunctionTemplate) \
454455
V(sqlite_limits_template, v8::ObjectTemplate) \
455456
V(sqlite_run_result_template, v8::DictionaryTemplate) \
456457
V(sqlite_statement_sync_constructor_template, v8::FunctionTemplate) \

‎src/node_sqlite.cc‎

Lines changed: 55 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2909,9 +2909,11 @@ void Database::CreateSession(const FunctionCallbackInfo<Value>& args) {
29092909

29102910
void Backup(const FunctionCallbackInfo<Value>& args) {
29112911
Environment* env = Environment::GetCurrent(args);
2912-
if (args.Length() < 1 || !args[0]->IsObject()) {
2913-
THROW_ERR_INVALID_ARG_TYPE(env->isolate(),
2914-
"The \"sourceDb\" argument must be an object.");
2912+
if (!args[0]->IsObject() ||
2913+
!env->sqlite_database_constructor_template()->HasInstance(args[0])) {
2914+
THROW_ERR_INVALID_ARG_TYPE(
2915+
env->isolate(),
2916+
"The \"sourceDb\" argument must be an instance of Database.");
29152917
return;
29162918
}
29172919

@@ -5085,51 +5087,60 @@ static void Initialize(Local<Object> target,
50855087
}
50865088
});
50875089
}
5088-
Local<FunctionTemplate> db_tmpl = NewFunctionTemplate(isolate, Database::New);
5089-
db_tmpl->InstanceTemplate()->SetInternalFieldCount(
5090-
Database::kInternalFieldCount);
50915090
Local<Object> constants = Object::New(isolate);
50925091

50935092
DefineConstants(constants);
50945093

5095-
SetProtoMethod(isolate, db_tmpl, "open", Database::Open);
5096-
SetProtoMethod(isolate, db_tmpl, "close", Database::Close);
5097-
SetProtoDispose(isolate, db_tmpl, Database::Dispose);
5098-
SetProtoMethod(isolate, db_tmpl, "prepare", Database::Prepare);
5099-
SetProtoMethod(isolate, db_tmpl, "exec", Database::Exec);
5100-
SetProtoMethod(isolate, db_tmpl, "function", Database::CustomFunction);
5101-
SetProtoMethod(isolate, db_tmpl, "createTagStore", Database::CreateTagStore);
5102-
SetProtoMethodNoSideEffect(isolate, db_tmpl, "location", Database::Location);
5103-
SetProtoMethod(isolate, db_tmpl, "aggregate", Database::AggregateFunction);
5104-
SetProtoMethod(isolate, db_tmpl, "createSession", Database::CreateSession);
5105-
SetProtoMethod(isolate, db_tmpl, "applyChangeset", Database::ApplyChangeset);
5106-
SetProtoMethod(
5107-
isolate, db_tmpl, "enableLoadExtension", Database::EnableLoadExtension);
5108-
SetProtoMethod(
5109-
isolate, db_tmpl, "enableDefensive", Database::EnableDefensive);
5110-
SetProtoMethod(isolate, db_tmpl, "loadExtension", Database::LoadExtension);
5111-
SetProtoMethod(isolate, db_tmpl, "serialize", Database::Serialize);
5112-
SetProtoMethod(isolate, db_tmpl, "deserialize", Database::Deserialize);
5113-
SetProtoMethod(isolate, db_tmpl, "setAuthorizer", Database::SetAuthorizer);
5114-
SetProtoMethod(isolate, db_tmpl, "createModule", Database::CreateModule);
5115-
SetSideEffectFreeGetter(isolate,
5116-
db_tmpl,
5117-
FIXED_ONE_BYTE_STRING(isolate, "isOpen"),
5118-
Database::IsOpenGetter);
5119-
SetSideEffectFreeGetter(isolate,
5120-
db_tmpl,
5121-
FIXED_ONE_BYTE_STRING(isolate, "isTransaction"),
5122-
Database::IsTransactionGetter);
5123-
SetSideEffectFreeGetter(
5124-
isolate, db_tmpl, env->limits_string(), Database::LimitsGetter);
5125-
Local<String> sqlite_type_key = FIXED_ONE_BYTE_STRING(isolate, "sqlite-type");
5126-
Local<v8::Symbol> sqlite_type_symbol =
5127-
v8::Symbol::For(isolate, sqlite_type_key);
5128-
Local<String> database_sync_string =
5129-
FIXED_ONE_BYTE_STRING(isolate, "node:sqlite");
5130-
db_tmpl->InstanceTemplate()->Set(sqlite_type_symbol, database_sync_string);
5131-
5132-
SetConstructorFunction(context, target, "Database", db_tmpl);
5094+
Local<FunctionTemplate> db_tmpl = env->sqlite_database_constructor_template();
5095+
if (db_tmpl.IsEmpty()) {
5096+
db_tmpl = NewFunctionTemplate(isolate, Database::New);
5097+
db_tmpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "Database"));
5098+
db_tmpl->InstanceTemplate()->SetInternalFieldCount(
5099+
Database::kInternalFieldCount);
5100+
SetProtoMethod(isolate, db_tmpl, "open", Database::Open);
5101+
SetProtoMethod(isolate, db_tmpl, "close", Database::Close);
5102+
SetProtoDispose(isolate, db_tmpl, Database::Dispose);
5103+
SetProtoMethod(isolate, db_tmpl, "prepare", Database::Prepare);
5104+
SetProtoMethod(isolate, db_tmpl, "exec", Database::Exec);
5105+
SetProtoMethod(isolate, db_tmpl, "function", Database::CustomFunction);
5106+
SetProtoMethod(
5107+
isolate, db_tmpl, "createTagStore", Database::CreateTagStore);
5108+
SetProtoMethodNoSideEffect(
5109+
isolate, db_tmpl, "location", Database::Location);
5110+
SetProtoMethod(isolate, db_tmpl, "aggregate", Database::AggregateFunction);
5111+
SetProtoMethod(isolate, db_tmpl, "createSession", Database::CreateSession);
5112+
SetProtoMethod(
5113+
isolate, db_tmpl, "applyChangeset", Database::ApplyChangeset);
5114+
SetProtoMethod(
5115+
isolate, db_tmpl, "enableLoadExtension", Database::EnableLoadExtension);
5116+
SetProtoMethod(
5117+
isolate, db_tmpl, "enableDefensive", Database::EnableDefensive);
5118+
SetProtoMethod(isolate, db_tmpl, "loadExtension", Database::LoadExtension);
5119+
SetProtoMethod(isolate, db_tmpl, "serialize", Database::Serialize);
5120+
SetProtoMethod(isolate, db_tmpl, "deserialize", Database::Deserialize);
5121+
SetProtoMethod(isolate, db_tmpl, "setAuthorizer", Database::SetAuthorizer);
5122+
SetProtoMethod(isolate, db_tmpl, "createModule", Database::CreateModule);
5123+
SetSideEffectFreeGetter(isolate,
5124+
db_tmpl,
5125+
FIXED_ONE_BYTE_STRING(isolate, "isOpen"),
5126+
Database::IsOpenGetter);
5127+
SetSideEffectFreeGetter(isolate,
5128+
db_tmpl,
5129+
FIXED_ONE_BYTE_STRING(isolate, "isTransaction"),
5130+
Database::IsTransactionGetter);
5131+
SetSideEffectFreeGetter(
5132+
isolate, db_tmpl, env->limits_string(), Database::LimitsGetter);
5133+
Local<String> sqlite_type_key =
5134+
FIXED_ONE_BYTE_STRING(isolate, "sqlite-type");
5135+
Local<v8::Symbol> sqlite_type_symbol =
5136+
v8::Symbol::For(isolate, sqlite_type_key);
5137+
Local<String> database_sync_string =
5138+
FIXED_ONE_BYTE_STRING(isolate, "node:sqlite");
5139+
db_tmpl->InstanceTemplate()->Set(sqlite_type_symbol, database_sync_string);
5140+
env->set_sqlite_database_constructor_template(db_tmpl);
5141+
}
5142+
SetConstructorFunction(
5143+
context, target, "Database", db_tmpl, SetConstructorFunctionFlag::NONE);
51335144
SetConstructorFunction(context,
51345145
target,
51355146
"Statement",

‎test/parallel/test-sqlite-backup.mjs‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,26 @@ describe('backup()', () => {
4848
backup();
4949
}, {
5050
code: 'ERR_INVALID_ARG_TYPE',
51-
message: 'The "sourceDb" argument must be an object.'
51+
message: 'The "sourceDb" argument must be an instance of Database.'
5252
});
5353
});
5454

55+
test('throws if the source is not an instance of Database', (t) => {
56+
const database = makeSourceDb();
57+
for (const source of [
58+
{},
59+
database.prepare('SELECT 1'),
60+
database.createSession(),
61+
]) {
62+
t.assert.throws(() => {
63+
backup(source, 'out.db');
64+
}, {
65+
code: 'ERR_INVALID_ARG_TYPE',
66+
message: 'The "sourceDb" argument must be an instance of Database.'
67+
});
68+
}
69+
});
70+
5571
test('throws if path is not a string, URL, or Buffer', (t) => {
5672
const database = makeSourceDb();
5773

0 commit comments

Comments
 (0)