From 76df88c30edaaec60f81bee0c18ac215a3a93794 Mon Sep 17 00:00:00 2001 From: "Kamat, Trivikram" <16024985+trivikr@users.noreply.github.com> Date: Tue, 28 Jul 2026 20:12:17 -0700 Subject: [PATCH] sqlite: check database state before calling SQLite Check whether the database is open in enableLoadExtension() and setAuthorizer() before passing the connection to SQLite. This prevents calls after close() from terminating the process and makes them throw ERR_INVALID_STATE instead. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: codex:gpt-5.6-sol --- src/node_sqlite.cc | 9 +++++++-- test/parallel/test-sqlite-authz.js | 12 ++++++++++++ test/parallel/test-sqlite-config.js | 12 ++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc index 8e2f13cd633439..8044eec6237177 100644 --- a/src/node_sqlite.cc +++ b/src/node_sqlite.cc @@ -2396,7 +2396,10 @@ void DatabaseSync::EnableLoadExtension( const FunctionCallbackInfo& args) { DatabaseSync* db; ASSIGN_OR_RETURN_UNWRAP(&db, args.This()); - auto isolate = args.GetIsolate(); + Environment* env = Environment::GetCurrent(args); + THROW_AND_RETURN_ON_BAD_STATE(env, !db->IsOpen(), "database is not open"); + + Isolate* isolate = env->isolate(); if (!args[0]->IsBoolean()) { THROW_ERR_INVALID_ARG_TYPE(isolate, "The \"allow\" argument must be a boolean."); @@ -2424,7 +2427,7 @@ void DatabaseSync::EnableDefensive(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); THROW_AND_RETURN_ON_BAD_STATE(env, !db->IsOpen(), "database is not open"); - auto isolate = args.GetIsolate(); + Isolate* isolate = env->isolate(); if (!args[0]->IsBoolean()) { THROW_ERR_INVALID_ARG_TYPE(isolate, "The \"active\" argument must be a boolean."); @@ -2475,6 +2478,8 @@ void DatabaseSync::SetAuthorizer(const FunctionCallbackInfo& args) { DatabaseSync* db; ASSIGN_OR_RETURN_UNWRAP(&db, args.This()); Environment* env = Environment::GetCurrent(args); + THROW_AND_RETURN_ON_BAD_STATE(env, !db->IsOpen(), "database is not open"); + Isolate* isolate = env->isolate(); if (args[0]->IsNull()) { diff --git a/test/parallel/test-sqlite-authz.js b/test/parallel/test-sqlite-authz.js index 2bf268847cd370..69c075a57e2e87 100644 --- a/test/parallel/test-sqlite-authz.js +++ b/test/parallel/test-sqlite-authz.js @@ -275,4 +275,16 @@ suite('DatabaseSync.prototype.setAuthorizer()', () => { message: /The "callback" argument must be a function/ }); }); + + it('throws if database is not open', () => { + const db = new DatabaseSync(':memory:'); + db.close(); + + assert.throws(() => { + db.setAuthorizer(() => constants.SQLITE_OK); + }, { + code: 'ERR_INVALID_STATE', + message: 'database is not open', + }); + }); }); diff --git a/test/parallel/test-sqlite-config.js b/test/parallel/test-sqlite-config.js index 411b9d1cfaeace..d1a6cae2d7443f 100644 --- a/test/parallel/test-sqlite-config.js +++ b/test/parallel/test-sqlite-config.js @@ -61,3 +61,15 @@ test('throws if options.defensive is provided but is not a boolean', (t) => { message: 'The "options.defensive" argument must be a boolean.', }); }); + +test('enableLoadExtension() throws if database is not open', (t) => { + const db = new DatabaseSync(':memory:', { allowExtension: true }); + db.close(); + + t.assert.throws(() => { + db.enableLoadExtension(false); + }, { + code: 'ERR_INVALID_STATE', + message: 'database is not open', + }); +});