@@ -488,6 +488,7 @@ class BackupJob : public ThreadPoolWork {
488488 std::string source_db,
489489 std::string destination_name,
490490 std::string dest_db,
491+ std::shared_ptr<SQLitePermissionVFS> permission_vfs,
491492 int pages,
492493 Local<Function> progressFunc)
493494 : ThreadPoolWork(env, " node_sqlite3.BackupJob" ),
@@ -496,7 +497,8 @@ class BackupJob : public ThreadPoolWork {
496497 pages_(pages),
497498 source_db_(std::move(source_db)),
498499 destination_name_(std::move(destination_name)),
499- dest_db_(std::move(dest_db)) {
500+ dest_db_(std::move(dest_db)),
501+ permission_vfs_(std::move(permission_vfs)) {
500502 resolver_.Reset (env->isolate (), resolver);
501503 progressFunc_.Reset (env->isolate (), progressFunc);
502504 }
@@ -508,7 +510,8 @@ class BackupJob : public ThreadPoolWork {
508510 destination_name_.c_str (),
509511 &dest_,
510512 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_URI ,
511- nullptr );
513+ permission_vfs_ == nullptr ? nullptr
514+ : permission_vfs_->name ().c_str ());
512515 Local<Promise::Resolver> resolver =
513516 Local<Promise::Resolver>::New (env ()->isolate (), resolver_);
514517 if (backup_status_ != SQLITE_OK ) {
@@ -609,6 +612,8 @@ class BackupJob : public ThreadPoolWork {
609612 sqlite3_close_v2 (dest_);
610613 dest_ = nullptr ;
611614 }
615+
616+ permission_vfs_.reset ();
612617 }
613618
614619 private:
@@ -651,6 +656,7 @@ class BackupJob : public ThreadPoolWork {
651656 std::string source_db_;
652657 std::string destination_name_;
653658 std::string dest_db_;
659+ std::shared_ptr<SQLitePermissionVFS> permission_vfs_;
654660};
655661
656662UserDefinedFunction::UserDefinedFunction (Environment* env,
@@ -935,22 +941,58 @@ void DatabaseSync::MemoryInfo(MemoryTracker* tracker) const {
935941 " open_config" , sizeof (open_config_), " DatabaseOpenConfiguration" );
936942}
937943
944+ namespace {
945+
946+ bool IsSQLiteMemoryLocation (std::string_view location) {
947+ return location == " :memory:" ||
948+ (location.starts_with (" file:" ) &&
949+ (SQLitePathForPermission (location) == " :memory:" ||
950+ SQLiteUriParameterEquals (location, " mode" , " memory" )));
951+ }
952+
953+ bool IsSQLiteReadOnlyLocation (std::string_view location, bool read_only) {
954+ return read_only ||
955+ (location.starts_with (" file:" ) &&
956+ SQLiteUriParameterEquals (location, " mode" , " ro" ));
957+ }
958+
959+ } // namespace
960+
938961bool DatabaseSync::Open () {
939962 if (IsOpen ()) {
940963 THROW_ERR_INVALID_STATE (env (), " database is already open" );
941964 return false ;
942965 }
943966
944- // Permission checks: skip for in-memory databases, enforce FS permissions
945- // for file-backed databases.
946- std::string_view db_path = open_config_.location ();
947- if (db_path != " :memory:" && !db_path.empty ()) {
948- if (open_config_.get_read_only ()) {
967+ // Permission checks: in-memory databases do not access the filesystem.
968+ // SQLite URI modes are handled before the VFS is created so that the
969+ // user-facing error remains Node's permission error.
970+ std::string_view db_location = open_config_.location ();
971+ if (!IsSQLiteMemoryLocation (db_location)) {
972+ const std::string db_path = SQLitePathForPermission (db_location);
973+ const bool read_only =
974+ IsSQLiteReadOnlyLocation (db_location, open_config_.get_read_only ());
975+ THROW_IF_INSUFFICIENT_PERMISSIONS (
976+ env (),
977+ permission::PermissionScope::kFileSystemRead ,
978+ db_path,
979+ false );
980+ if (!read_only) {
949981 THROW_IF_INSUFFICIENT_PERMISSIONS (
950- env (), permission::PermissionScope::kFileSystemRead , db_path, false );
951- } else {
952- THROW_IF_INSUFFICIENT_PERMISSIONS (
953- env (), permission::PermissionScope::kFileSystemWrite , db_path, false );
982+ env (),
983+ permission::PermissionScope::kFileSystemWrite ,
984+ db_path,
985+ false );
986+ }
987+ }
988+
989+ if (env ()->permission ()->enabled ()) {
990+ permission_vfs_ = std::make_shared<SQLitePermissionVFS>(env ());
991+ if (!permission_vfs_->Register ()) {
992+ THROW_ERR_SQLITE_ERROR (env ()->isolate (),
993+ " Unable to register the SQLite permission VFS" );
994+ permission_vfs_.reset ();
995+ return false ;
954996 }
955997 }
956998
@@ -972,12 +1014,19 @@ bool DatabaseSync::Open() {
9721014 int flags = open_config_.get_read_only ()
9731015 ? SQLITE_OPEN_READONLY
9741016 : SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE ;
975- int r = sqlite3_open_v2 (open_config_.location ().c_str (),
976- &connection_,
977- flags | default_flags,
978- nullptr );
1017+ int r = sqlite3_open_v2 (
1018+ open_config_.location ().c_str (),
1019+ &connection_,
1020+ flags | default_flags,
1021+ permission_vfs_ == nullptr ? nullptr : permission_vfs_->name ().c_str ());
9791022 CHECK_ERROR_OR_THROW (env ()->isolate (), this , r, SQLITE_OK , false );
9801023
1024+ if (permission_vfs_ != nullptr ) {
1025+ r = sqlite3_set_authorizer (
1026+ connection_, DatabaseSync::AuthorizerCallback, this );
1027+ CHECK_ERROR_OR_THROW (env ()->isolate (), this , r, SQLITE_OK , false );
1028+ }
1029+
9811030 r = sqlite3_db_config (connection_,
9821031 SQLITE_DBCONFIG_DQS_DML ,
9831032 static_cast <int >(open_config_.get_enable_dqs ()),
@@ -1101,10 +1150,21 @@ std::optional<std::string> ValidateDatabasePath(Environment* env,
11011150 constexpr auto has_null_bytes = [](std::string_view str) {
11021151 return str.find (' \0 ' ) != std::string_view::npos;
11031152 };
1153+ const auto validate_uri_vfs = [&](std::string value)
1154+ -> std::optional<std::string> {
1155+ if (env->permission ()->enabled () && HasSQLiteVfsUriParameter (value)) {
1156+ THROW_ERR_INVALID_ARG_VALUE (
1157+ env->isolate (),
1158+ " The \" %s\" argument must not specify a SQLite VFS." ,
1159+ field_name);
1160+ return std::nullopt ;
1161+ }
1162+ return value;
1163+ };
11041164 if (path->IsString ()) {
11051165 Utf8Value location (env->isolate (), path.As <String>());
11061166 if (!has_null_bytes (location.ToStringView ())) {
1107- return location.ToString ();
1167+ return validate_uri_vfs ( location.ToString () );
11081168 }
11091169 } else if (path->IsUint8Array ()) {
11101170 Local<Uint8Array> buffer = path.As <Uint8Array>();
@@ -1113,7 +1173,8 @@ std::optional<std::string> ValidateDatabasePath(Environment* env,
11131173 auto data =
11141174 static_cast <const uint8_t *>(buffer->Buffer ()->Data ()) + byteOffset;
11151175 if (std::find (data, data + byteLength, 0 ) == data + byteLength) {
1116- return std::string (reinterpret_cast <const char *>(data), byteLength);
1176+ return validate_uri_vfs (
1177+ std::string (reinterpret_cast <const char *>(data), byteLength));
11171178 }
11181179 } else if (path->IsObject ()) { // When is URL
11191180 auto url = path.As <Object>();
@@ -1129,7 +1190,7 @@ std::optional<std::string> ValidateDatabasePath(Environment* env,
11291190 return std::nullopt ;
11301191 }
11311192
1132- return location_value.ToString ();
1193+ return validate_uri_vfs ( location_value.ToString () );
11331194 }
11341195 }
11351196 }
@@ -1452,6 +1513,7 @@ void DatabaseSync::Close(const FunctionCallbackInfo<Value>& args) {
14521513 int r = sqlite3_close_v2 (db->connection_ );
14531514 CHECK_ERROR_OR_THROW (env->isolate (), db, r, SQLITE_OK , void ());
14541515 db->connection_ = nullptr ;
1516+ db->permission_vfs_ .reset ();
14551517}
14561518
14571519void DatabaseSync::Dispose (const v8::FunctionCallbackInfo<v8::Value>& args) {
@@ -2242,6 +2304,7 @@ void Backup(const FunctionCallbackInfo<Value>& args) {
22422304 std::move (source_db),
22432305 dest_path.value (),
22442306 std::move (dest_db),
2307+ db->PermissionVFS (),
22452308 rate,
22462309 progressFunc);
22472310 db->AddBackup (job);
@@ -2477,9 +2540,13 @@ void DatabaseSync::SetAuthorizer(const FunctionCallbackInfo<Value>& args) {
24772540 Isolate* isolate = env->isolate ();
24782541
24792542 if (args[0 ]->IsNull ()) {
2480- // Clear the authorizer
2481- sqlite3_set_authorizer (db->connection_ , nullptr , nullptr );
24822543 db->object ()->SetInternalField (kAuthorizerCallback , Null (isolate));
2544+ if (db->permission_vfs_ == nullptr ) {
2545+ sqlite3_set_authorizer (db->connection_ , nullptr , nullptr );
2546+ } else {
2547+ sqlite3_set_authorizer (
2548+ db->connection_ , DatabaseSync::AuthorizerCallback, db);
2549+ }
24832550 return ;
24842551 }
24852552
@@ -2508,16 +2575,19 @@ int DatabaseSync::AuthorizerCallback(void* user_data,
25082575 const char * param3,
25092576 const char * param4) {
25102577 DatabaseSync* db = static_cast <DatabaseSync*>(user_data);
2511- Environment* env = db->env ();
2512- Isolate* isolate = env-> isolate ();
2513- HandleScope handle_scope (isolate) ;
2514- Local<Context> context = env-> context ();
2578+ if ( db->permission_vfs_ != nullptr && action_code == SQLITE_ATTACH &&
2579+ param1 != nullptr && HasSQLiteVfsUriParameter (param1)) {
2580+ return SQLITE_DENY ;
2581+ }
25152582
25162583 Local<Value> cb =
25172584 db->object ()->GetInternalField (kAuthorizerCallback ).template As <Value>();
2585+ if (!cb->IsFunction ()) return SQLITE_OK ;
25182586
2519- CHECK (cb->IsFunction ());
2520-
2587+ Environment* env = db->env ();
2588+ Isolate* isolate = env->isolate ();
2589+ HandleScope handle_scope (isolate);
2590+ Local<Context> context = env->context ();
25212591 Local<Function> callback = cb.As <Function>();
25222592
25232593 LocalVector<Value> js_argv (
0 commit comments