| 
1 | 1 | #include "node_sqlite.h"  | 
 | 2 | +#include <path.h>  | 
2 | 3 | #include "base_object-inl.h"  | 
3 | 4 | #include "debug_utils-inl.h"  | 
4 | 5 | #include "env-inl.h"  | 
@@ -114,10 +115,13 @@ inline void THROW_ERR_SQLITE_ERROR(Isolate* isolate, const char* message) {  | 
114 | 115 | DatabaseSync::DatabaseSync(Environment* env,  | 
115 | 116 |                            Local<Object> object,  | 
116 | 117 |                            DatabaseOpenConfiguration&& open_config,  | 
117 |  | -                           bool open)  | 
 | 118 | +                           bool open,  | 
 | 119 | +                           bool allow_load_extension)  | 
118 | 120 |     : BaseObject(env, object), open_config_(std::move(open_config)) {  | 
119 | 121 |   MakeWeak();  | 
120 | 122 |   connection_ = nullptr;  | 
 | 123 | +  allow_load_extension_ = allow_load_extension;  | 
 | 124 | +  enable_load_extension_ = allow_load_extension;  | 
121 | 125 | 
 
  | 
122 | 126 |   if (open) {  | 
123 | 127 |     Open();  | 
@@ -182,6 +186,19 @@ bool DatabaseSync::Open() {  | 
182 | 186 |   CHECK_ERROR_OR_THROW(env()->isolate(), connection_, r, SQLITE_OK, false);  | 
183 | 187 |   CHECK_EQ(foreign_keys_enabled, open_config_.get_enable_foreign_keys());  | 
184 | 188 | 
 
  | 
 | 189 | +  if (allow_load_extension_) {  | 
 | 190 | +    if (env()->permission()->enabled()) [[unlikely]] {  | 
 | 191 | +      THROW_ERR_LOAD_SQLITE_EXTENSION(env(),  | 
 | 192 | +                                      "Cannot load SQLite extensions when the "  | 
 | 193 | +                                      "permission model is enabled.");  | 
 | 194 | +      return false;  | 
 | 195 | +    }  | 
 | 196 | +    const int load_extension_ret = sqlite3_db_config(  | 
 | 197 | +        connection_, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, nullptr);  | 
 | 198 | +    CHECK_ERROR_OR_THROW(  | 
 | 199 | +        env()->isolate(), connection_, load_extension_ret, SQLITE_OK, false);  | 
 | 200 | +  }  | 
 | 201 | + | 
185 | 202 |   return true;  | 
186 | 203 | }  | 
187 | 204 | 
 
  | 
@@ -227,6 +244,7 @@ void DatabaseSync::New(const FunctionCallbackInfo<Value>& args) {  | 
227 | 244 |   DatabaseOpenConfiguration open_config(std::move(location));  | 
228 | 245 | 
 
  | 
229 | 246 |   bool open = true;  | 
 | 247 | +  bool allow_load_extension = false;  | 
230 | 248 | 
 
  | 
231 | 249 |   if (args.Length() > 1) {  | 
232 | 250 |     if (!args[1]->IsObject()) {  | 
@@ -302,9 +320,28 @@ void DatabaseSync::New(const FunctionCallbackInfo<Value>& args) {  | 
302 | 320 |       }  | 
303 | 321 |       open_config.set_enable_dqs(enable_dqs_v.As<Boolean>()->Value());  | 
304 | 322 |     }  | 
 | 323 | + | 
 | 324 | +    Local<String> allow_extension_string =  | 
 | 325 | +        FIXED_ONE_BYTE_STRING(env->isolate(), "allowExtension");  | 
 | 326 | +    Local<Value> allow_extension_v;  | 
 | 327 | +    if (!options->Get(env->context(), allow_extension_string)  | 
 | 328 | +             .ToLocal(&allow_extension_v)) {  | 
 | 329 | +      return;  | 
 | 330 | +    }  | 
 | 331 | + | 
 | 332 | +    if (!allow_extension_v->IsUndefined()) {  | 
 | 333 | +      if (!allow_extension_v->IsBoolean()) {  | 
 | 334 | +        THROW_ERR_INVALID_ARG_TYPE(  | 
 | 335 | +            env->isolate(),  | 
 | 336 | +            "The \"options.allowExtension\" argument must be a boolean.");  | 
 | 337 | +        return;  | 
 | 338 | +      }  | 
 | 339 | +      allow_load_extension = allow_extension_v.As<Boolean>()->Value();  | 
 | 340 | +    }  | 
305 | 341 |   }  | 
306 | 342 | 
 
  | 
307 |  | -  new DatabaseSync(env, args.This(), std::move(open_config), open);  | 
 | 343 | +  new DatabaseSync(  | 
 | 344 | +      env, args.This(), std::move(open_config), open, allow_load_extension);  | 
308 | 345 | }  | 
309 | 346 | 
 
  | 
310 | 347 | void DatabaseSync::Open(const FunctionCallbackInfo<Value>& args) {  | 
@@ -526,6 +563,70 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo<Value>& args) {  | 
526 | 563 |   args.GetReturnValue().Set(true);  | 
527 | 564 | }  | 
528 | 565 | 
 
  | 
 | 566 | +void DatabaseSync::EnableLoadExtension(  | 
 | 567 | +    const FunctionCallbackInfo<Value>& args) {  | 
 | 568 | +  DatabaseSync* db;  | 
 | 569 | +  ASSIGN_OR_RETURN_UNWRAP(&db, args.This());  | 
 | 570 | +  Environment* env = Environment::GetCurrent(args);  | 
 | 571 | +  if (!args[0]->IsBoolean()) {  | 
 | 572 | +    THROW_ERR_INVALID_ARG_TYPE(env->isolate(),  | 
 | 573 | +                               "The \"allow\" argument must be a boolean.");  | 
 | 574 | +    return;  | 
 | 575 | +  }  | 
 | 576 | + | 
 | 577 | +  const int enable = args[0].As<Boolean>()->Value();  | 
 | 578 | +  auto isolate = env->isolate();  | 
 | 579 | + | 
 | 580 | +  if (db->allow_load_extension_ == false && enable == true) {  | 
 | 581 | +    THROW_ERR_INVALID_STATE(  | 
 | 582 | +        isolate,  | 
 | 583 | +        "Cannot enable extension loading because it was disabled at database "  | 
 | 584 | +        "creation.");  | 
 | 585 | +    return;  | 
 | 586 | +  }  | 
 | 587 | +  db->enable_load_extension_ = enable;  | 
 | 588 | +  const int load_extension_ret = sqlite3_db_config(  | 
 | 589 | +      db->connection_, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, enable, nullptr);  | 
 | 590 | +  CHECK_ERROR_OR_THROW(  | 
 | 591 | +      isolate, db->connection_, load_extension_ret, SQLITE_OK, void());  | 
 | 592 | +}  | 
 | 593 | + | 
 | 594 | +void DatabaseSync::LoadExtension(const FunctionCallbackInfo<Value>& args) {  | 
 | 595 | +  DatabaseSync* db;  | 
 | 596 | +  ASSIGN_OR_RETURN_UNWRAP(&db, args.This());  | 
 | 597 | +  Environment* env = Environment::GetCurrent(args);  | 
 | 598 | +  THROW_AND_RETURN_ON_BAD_STATE(  | 
 | 599 | +      env, db->connection_ == nullptr, "database is not open");  | 
 | 600 | +  THROW_AND_RETURN_ON_BAD_STATE(  | 
 | 601 | +      env, !db->allow_load_extension_, "extension loading is not allowed");  | 
 | 602 | +  THROW_AND_RETURN_ON_BAD_STATE(  | 
 | 603 | +      env, !db->enable_load_extension_, "extension loading is not allowed");  | 
 | 604 | + | 
 | 605 | +  if (!args[0]->IsString()) {  | 
 | 606 | +    THROW_ERR_INVALID_ARG_TYPE(env->isolate(),  | 
 | 607 | +                               "The \"path\" argument must be a string.");  | 
 | 608 | +    return;  | 
 | 609 | +  }  | 
 | 610 | + | 
 | 611 | +  auto isolate = env->isolate();  | 
 | 612 | + | 
 | 613 | +  BufferValue path(isolate, args[0]);  | 
 | 614 | +  BufferValue entryPoint(isolate, args[1]);  | 
 | 615 | +  CHECK_NOT_NULL(*path);  | 
 | 616 | +  ToNamespacedPath(env, &path);  | 
 | 617 | +  if (*entryPoint == nullptr) {  | 
 | 618 | +    ToNamespacedPath(env, &entryPoint);  | 
 | 619 | +  }  | 
 | 620 | +  THROW_IF_INSUFFICIENT_PERMISSIONS(  | 
 | 621 | +      env, permission::PermissionScope::kFileSystemRead, path.ToStringView());  | 
 | 622 | +  char* errmsg = nullptr;  | 
 | 623 | +  const int r =  | 
 | 624 | +      sqlite3_load_extension(db->connection_, *path, *entryPoint, &errmsg);  | 
 | 625 | +  if (r != SQLITE_OK) {  | 
 | 626 | +    isolate->ThrowException(ERR_LOAD_SQLITE_EXTENSION(isolate, errmsg));  | 
 | 627 | +  }  | 
 | 628 | +}  | 
 | 629 | + | 
529 | 630 | StatementSync::StatementSync(Environment* env,  | 
530 | 631 |                              Local<Object> object,  | 
531 | 632 |                              DatabaseSync* db,  | 
@@ -1312,6 +1413,12 @@ static void Initialize(Local<Object> target,  | 
1312 | 1413 |       isolate, db_tmpl, "createSession", DatabaseSync::CreateSession);  | 
1313 | 1414 |   SetProtoMethod(  | 
1314 | 1415 |       isolate, db_tmpl, "applyChangeset", DatabaseSync::ApplyChangeset);  | 
 | 1416 | +  SetProtoMethod(isolate,  | 
 | 1417 | +                 db_tmpl,  | 
 | 1418 | +                 "enableLoadExtension",  | 
 | 1419 | +                 DatabaseSync::EnableLoadExtension);  | 
 | 1420 | +  SetProtoMethod(  | 
 | 1421 | +      isolate, db_tmpl, "loadExtension", DatabaseSync::LoadExtension);  | 
1315 | 1422 |   SetConstructorFunction(context, target, "DatabaseSync", db_tmpl);  | 
1316 | 1423 |   SetConstructorFunction(context,  | 
1317 | 1424 |                          target,  | 
 | 
0 commit comments