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: https://github.com/nodejs/node/pull/66797
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
This commit is contained in:
Guilherme Araújo authored and GitHub committed 2026-10-25 21:35:13 +00:00
1 parent c7ae7ab5b9
commit e20da720c8
3 files changed
+73 -45

No files matched your search

+1
View File
@@ -451,6 +451,7 @@
V(socketaddress_constructor_template, v8::FunctionTemplate) \
V(space_stats_template, v8::DictionaryTemplate) \
V(sqlite_column_template, v8::DictionaryTemplate) \
V(sqlite_database_constructor_template, v8::FunctionTemplate) \
V(sqlite_limits_template, v8::ObjectTemplate) \
V(sqlite_run_result_template, v8::DictionaryTemplate) \
V(sqlite_statement_sync_constructor_template, v8::FunctionTemplate) \
+55 -44
View File
@@ -2909,9 +2909,11 @@ void Database::CreateSession(const FunctionCallbackInfo<Value>& args) {
void Backup(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
if (args.Length() < 1 || !args[0]->IsObject()) {
THROW_ERR_INVALID_ARG_TYPE(env->isolate(),
"The \"sourceDb\" argument must be an object.");
if (!args[0]->IsObject() ||
!env->sqlite_database_constructor_template()->HasInstance(args[0])) {
THROW_ERR_INVALID_ARG_TYPE(
env->isolate(),
"The \"sourceDb\" argument must be an instance of Database.");
return;
}
@@ -5085,51 +5087,60 @@ static void Initialize(Local<Object> target,
}
});
}
Local<FunctionTemplate> db_tmpl = NewFunctionTemplate(isolate, Database::New);
db_tmpl->InstanceTemplate()->SetInternalFieldCount(
Database::kInternalFieldCount);
Local<Object> constants = Object::New(isolate);
DefineConstants(constants);
SetProtoMethod(isolate, db_tmpl, "open", Database::Open);
SetProtoMethod(isolate, db_tmpl, "close", Database::Close);
SetProtoDispose(isolate, db_tmpl, Database::Dispose);
SetProtoMethod(isolate, db_tmpl, "prepare", Database::Prepare);
SetProtoMethod(isolate, db_tmpl, "exec", Database::Exec);
SetProtoMethod(isolate, db_tmpl, "function", Database::CustomFunction);
SetProtoMethod(isolate, db_tmpl, "createTagStore", Database::CreateTagStore);
SetProtoMethodNoSideEffect(isolate, db_tmpl, "location", Database::Location);
SetProtoMethod(isolate, db_tmpl, "aggregate", Database::AggregateFunction);
SetProtoMethod(isolate, db_tmpl, "createSession", Database::CreateSession);
SetProtoMethod(isolate, db_tmpl, "applyChangeset", Database::ApplyChangeset);
SetProtoMethod(
isolate, db_tmpl, "enableLoadExtension", Database::EnableLoadExtension);
SetProtoMethod(
isolate, db_tmpl, "enableDefensive", Database::EnableDefensive);
SetProtoMethod(isolate, db_tmpl, "loadExtension", Database::LoadExtension);
SetProtoMethod(isolate, db_tmpl, "serialize", Database::Serialize);
SetProtoMethod(isolate, db_tmpl, "deserialize", Database::Deserialize);
SetProtoMethod(isolate, db_tmpl, "setAuthorizer", Database::SetAuthorizer);
SetProtoMethod(isolate, db_tmpl, "createModule", Database::CreateModule);
SetSideEffectFreeGetter(isolate,
db_tmpl,
FIXED_ONE_BYTE_STRING(isolate, "isOpen"),
Database::IsOpenGetter);
SetSideEffectFreeGetter(isolate,
db_tmpl,
FIXED_ONE_BYTE_STRING(isolate, "isTransaction"),
Database::IsTransactionGetter);
SetSideEffectFreeGetter(
isolate, db_tmpl, env->limits_string(), Database::LimitsGetter);
Local<String> sqlite_type_key = FIXED_ONE_BYTE_STRING(isolate, "sqlite-type");
Local<v8::Symbol> sqlite_type_symbol =
v8::Symbol::For(isolate, sqlite_type_key);
Local<String> database_sync_string =
FIXED_ONE_BYTE_STRING(isolate, "node:sqlite");
db_tmpl->InstanceTemplate()->Set(sqlite_type_symbol, database_sync_string);
SetConstructorFunction(context, target, "Database", db_tmpl);
Local<FunctionTemplate> db_tmpl = env->sqlite_database_constructor_template();
if (db_tmpl.IsEmpty()) {
db_tmpl = NewFunctionTemplate(isolate, Database::New);
db_tmpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "Database"));
db_tmpl->InstanceTemplate()->SetInternalFieldCount(
Database::kInternalFieldCount);
SetProtoMethod(isolate, db_tmpl, "open", Database::Open);
SetProtoMethod(isolate, db_tmpl, "close", Database::Close);
SetProtoDispose(isolate, db_tmpl, Database::Dispose);
SetProtoMethod(isolate, db_tmpl, "prepare", Database::Prepare);
SetProtoMethod(isolate, db_tmpl, "exec", Database::Exec);
SetProtoMethod(isolate, db_tmpl, "function", Database::CustomFunction);
SetProtoMethod(
isolate, db_tmpl, "createTagStore", Database::CreateTagStore);
SetProtoMethodNoSideEffect(
isolate, db_tmpl, "location", Database::Location);
SetProtoMethod(isolate, db_tmpl, "aggregate", Database::AggregateFunction);
SetProtoMethod(isolate, db_tmpl, "createSession", Database::CreateSession);
SetProtoMethod(
isolate, db_tmpl, "applyChangeset", Database::ApplyChangeset);
SetProtoMethod(
isolate, db_tmpl, "enableLoadExtension", Database::EnableLoadExtension);
SetProtoMethod(
isolate, db_tmpl, "enableDefensive", Database::EnableDefensive);
SetProtoMethod(isolate, db_tmpl, "loadExtension", Database::LoadExtension);
SetProtoMethod(isolate, db_tmpl, "serialize", Database::Serialize);
SetProtoMethod(isolate, db_tmpl, "deserialize", Database::Deserialize);
SetProtoMethod(isolate, db_tmpl, "setAuthorizer", Database::SetAuthorizer);
SetProtoMethod(isolate, db_tmpl, "createModule", Database::CreateModule);
SetSideEffectFreeGetter(isolate,
db_tmpl,
FIXED_ONE_BYTE_STRING(isolate, "isOpen"),
Database::IsOpenGetter);
SetSideEffectFreeGetter(isolate,
db_tmpl,
FIXED_ONE_BYTE_STRING(isolate, "isTransaction"),
Database::IsTransactionGetter);
SetSideEffectFreeGetter(
isolate, db_tmpl, env->limits_string(), Database::LimitsGetter);
Local<String> sqlite_type_key =
FIXED_ONE_BYTE_STRING(isolate, "sqlite-type");
Local<v8::Symbol> sqlite_type_symbol =
v8::Symbol::For(isolate, sqlite_type_key);
Local<String> database_sync_string =
FIXED_ONE_BYTE_STRING(isolate, "node:sqlite");
db_tmpl->InstanceTemplate()->Set(sqlite_type_symbol, database_sync_string);
env->set_sqlite_database_constructor_template(db_tmpl);
}
SetConstructorFunction(
context, target, "Database", db_tmpl, SetConstructorFunctionFlag::NONE);
SetConstructorFunction(context,
target,
"Statement",
+17 -1
View File
@@ -48,10 +48,26 @@ describe('backup()', () => {
backup();
}, {
code: 'ERR_INVALID_ARG_TYPE',
message: 'The "sourceDb" argument must be an object.'
message: 'The "sourceDb" argument must be an instance of Database.'
});
});
test('throws if the source is not an instance of Database', (t) => {
const database = makeSourceDb();
for (const source of [
{},
database.prepare('SELECT 1'),
database.createSession(),
]) {
t.assert.throws(() => {
backup(source, 'out.db');
}, {
code: 'ERR_INVALID_ARG_TYPE',
message: 'The "sourceDb" argument must be an instance of Database.'
});
}
});
test('throws if path is not a string, URL, or Buffer', (t) => {
const database = makeSourceDb();