| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 95644a4 commit 2e55c6a
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -98,6 +98,10 @@ exposed by this class execute synchronously. | |||
| 98 | 98 | <!-- YAML | |
| 99 | 99 | added: v22.5.0 | |
| 100 | 100 | changes: | |
| 101 | + - version: | ||
| 102 | + - REPLACEME | ||
| 103 | + pr-url: https://github.com/nodejs/node/pull/60217 | ||
| 104 | + description: Add `defensive` option. | ||
| 101 | 105 | - version: | |
| 102 | 106 | - v24.4.0 | |
| 103 | 107 | - v22.18.0 | |
@@ -140,6 +144,10 @@ changes: | |||
| 140 | 144 | character (e.g., `foo` instead of `:foo`). **Default:** `true`. | |
| 141 | 145 | * `allowUnknownNamedParameters` {boolean} If `true`, unknown named parameters are ignored when binding. | |
| 142 | 146 | If `false`, an exception is thrown for unknown named parameters. **Default:** `false`. | |
| 147 | + * `defensive` {boolean} If `true`, enables the defensive flag. When the defensive flag is enabled, | ||
| 148 | + language features that allow ordinary SQL to deliberately corrupt the database file are disabled. | ||
| 149 | + The defensive flag can also be set using `enableDefensive()`. | ||
| 150 | + **Default:** `false`. | ||
| 143 | 151 | ||
| 144 | 152 | Constructs a new `DatabaseSync` instance. | |
| 145 | 153 | ||
@@ -261,6 +269,19 @@ Enables or disables the `loadExtension` SQL function, and the `loadExtension()` | |||
| 261 | 269 | method. When `allowExtension` is `false` when constructing, you cannot enable | |
| 262 | 270 | loading extensions for security reasons. | |
| 263 | 271 | ||
| 272 | + ### `database.enableDefensive(active)` | ||
| 273 | + | ||
| 274 | + <!-- YAML | ||
| 275 | + added: | ||
| 276 | + - REPLACEME | ||
| 277 | + --> | ||
| 278 | + | ||
| 279 | + * `active` {boolean} Whether to set the defensive flag. | ||
| 280 | + | ||
| 281 | + Enables or disables the defensive flag. When the defensive flag is active, | ||
| 282 | + language features that allow ordinary SQL to deliberately corrupt the database file are disabled. | ||
| 283 | + See [`SQLITE_DBCONFIG_DEFENSIVE`][] in the SQLite documentation for details. | ||
| 284 | + | ||
| 264 | 285 | ### `database.location([dbName])` | |
| 265 | 286 | ||
| 266 | 287 | <!-- YAML | |
@@ -1306,6 +1327,7 @@ callback function to indicate what type of operation is being authorized. | |||
| 1306 | 1327 | [Type conversion between JavaScript and SQLite]: #type-conversion-between-javascript-and-sqlite | |
| 1307 | 1328 | [`ATTACH DATABASE`]: https://www.sqlite.org/lang_attach.html | |
| 1308 | 1329 | [`PRAGMA foreign_keys`]: https://www.sqlite.org/pragma.html#pragma_foreign_keys | |
| 1330 | + [`SQLITE_DBCONFIG_DEFENSIVE`]: https://www.sqlite.org/c3ref/c_dbconfig_defensive.html#sqlitedbconfigdefensive | ||
| 1309 | 1331 | [`SQLITE_DETERMINISTIC`]: https://www.sqlite.org/c3ref/c_deterministic.html | |
| 1310 | 1332 | [`SQLITE_DIRECTONLY`]: https://www.sqlite.org/c3ref/c_deterministic.html | |
| 1311 | 1333 | [`SQLITE_MAX_FUNCTION_ARG`]: https://www.sqlite.org/limits.html#max_function_arg | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -130,6 +130,7 @@ | |||
| 130 | 130 | V(cwd_string, "cwd") \ | |
| 131 | 131 | V(data_string, "data") \ | |
| 132 | 132 | V(default_is_true_string, "defaultIsTrue") \ | |
| 133 | + V(defensive_string, "defensive") \ | ||
| 133 | 134 | V(deserialize_info_string, "deserializeInfo") \ | |
| 134 | 135 | V(dest_string, "dest") \ | |
| 135 | 136 | V(destroyed_string, "destroyed") \ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -753,6 +753,14 @@ bool DatabaseSync::Open() { | |||
| 753 | 753 | CHECK_ERROR_OR_THROW(env()->isolate(), this, r, SQLITE_OK, false); | |
| 754 | 754 | CHECK_EQ(foreign_keys_enabled, open_config_.get_enable_foreign_keys()); | |
| 755 | 755 | ||
| 756 | + int defensive_enabled; | ||
| 757 | + r = sqlite3_db_config(connection_, | ||
| 758 | + SQLITE_DBCONFIG_DEFENSIVE, | ||
| 759 | + static_cast<int>(open_config_.get_enable_defensive()), | ||
| 760 | + &defensive_enabled); | ||
| 761 | + CHECK_ERROR_OR_THROW(env()->isolate(), this, r, SQLITE_OK, false); | ||
| 762 | + CHECK_EQ(defensive_enabled, open_config_.get_enable_defensive()); | ||
| 763 | + | ||
| 756 | 764 | sqlite3_busy_timeout(connection_, open_config_.get_timeout()); | |
| 757 | 765 | ||
| 758 | 766 | if (allow_load_extension_) { | |
@@ -1065,6 +1073,21 @@ void DatabaseSync::New(const FunctionCallbackInfo<Value>& args) { | |||
| 1065 | 1073 | allow_unknown_named_params_v.As<Boolean>()->Value()); | |
| 1066 | 1074 | } | |
| 1067 | 1075 | } | |
| 1076 | + | ||
| 1077 | + Local<Value> defensive_v; | ||
| 1078 | + if (!options->Get(env->context(), env->defensive_string()) | ||
| 1079 | + .ToLocal(&defensive_v)) { | ||
| 1080 | + return; | ||
| 1081 | + } | ||
| 1082 | + if (!defensive_v->IsUndefined()) { | ||
| 1083 | + if (!defensive_v->IsBoolean()) { | ||
| 1084 | + THROW_ERR_INVALID_ARG_TYPE( | ||
| 1085 | + env->isolate(), | ||
| 1086 | + "The \"options.defensive\" argument must be a boolean."); | ||
| 1087 | + return; | ||
| 1088 | + } | ||
| 1089 | + open_config.set_enable_defensive(defensive_v.As<Boolean>()->Value()); | ||
| 1090 | + } | ||
| 1068 | 1091 | } | |
| 1069 | 1092 | ||
| 1070 | 1093 | new DatabaseSync( | |
@@ -1835,6 +1858,26 @@ void DatabaseSync::EnableLoadExtension( | |||
| 1835 | 1858 | CHECK_ERROR_OR_THROW(isolate, db, load_extension_ret, SQLITE_OK, void()); | |
| 1836 | 1859 | } | |
| 1837 | 1860 | ||
| 1861 | + void DatabaseSync::EnableDefensive(const FunctionCallbackInfo<Value>& args) { | ||
| 1862 | + DatabaseSync* db; | ||
| 1863 | + ASSIGN_OR_RETURN_UNWRAP(&db, args.This()); | ||
| 1864 | + Environment* env = Environment::GetCurrent(args); | ||
| 1865 | + THROW_AND_RETURN_ON_BAD_STATE(env, !db->IsOpen(), "database is not open"); | ||
| 1866 | + | ||
| 1867 | + auto isolate = args.GetIsolate(); | ||
| 1868 | + if (!args[0]->IsBoolean()) { | ||
| 1869 | + THROW_ERR_INVALID_ARG_TYPE(isolate, | ||
| 1870 | + "The \"active\" argument must be a boolean."); | ||
| 1871 | + return; | ||
| 1872 | + } | ||
| 1873 | + | ||
| 1874 | + const int enable = args[0].As<Boolean>()->Value(); | ||
| 1875 | + int defensive_enabled; | ||
| 1876 | + const int defensive_ret = sqlite3_db_config( | ||
| 1877 | + db->connection_, SQLITE_DBCONFIG_DEFENSIVE, enable, &defensive_enabled); | ||
| 1878 | + CHECK_ERROR_OR_THROW(isolate, db, defensive_ret, SQLITE_OK, void()); | ||
| 1879 | + } | ||
| 1880 | + | ||
| 1838 | 1881 | void DatabaseSync::LoadExtension(const FunctionCallbackInfo<Value>& args) { | |
| 1839 | 1882 | DatabaseSync* db; | |
| 1840 | 1883 | ASSIGN_OR_RETURN_UNWRAP(&db, args.This()); | |
@@ -3316,6 +3359,8 @@ static void Initialize(Local<Object> target, | |||
| 3316 | 3359 | db_tmpl, | |
| 3317 | 3360 | "enableLoadExtension", | |
| 3318 | 3361 | DatabaseSync::EnableLoadExtension); | |
| 3362 | + SetProtoMethod( | ||
| 3363 | + isolate, db_tmpl, "enableDefensive", DatabaseSync::EnableDefensive); | ||
| 3319 | 3364 | SetProtoMethod( | |
| 3320 | 3365 | isolate, db_tmpl, "loadExtension", DatabaseSync::LoadExtension); | |
| 3321 | 3366 | SetProtoMethod( | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -65,6 +65,10 @@ class DatabaseOpenConfiguration { | |||
| 65 | 65 | return allow_unknown_named_params_; | |
| 66 | 66 | } | |
| 67 | 67 | ||
| 68 | + inline void set_enable_defensive(bool flag) { defensive_ = flag; } | ||
| 69 | + | ||
| 70 | + inline bool get_enable_defensive() const { return defensive_; } | ||
| 71 | + | ||
| 68 | 72 | private: | |
| 69 | 73 | std::string location_; | |
| 70 | 74 | bool read_only_ = false; | |
@@ -75,6 +79,7 @@ class DatabaseOpenConfiguration { | |||
| 75 | 79 | bool return_arrays_ = false; | |
| 76 | 80 | bool allow_bare_named_params_ = true; | |
| 77 | 81 | bool allow_unknown_named_params_ = false; | |
| 82 | + bool defensive_ = false; | ||
| 78 | 83 | }; | |
| 79 | 84 | ||
| 80 | 85 | class DatabaseSync; | |
@@ -140,6 +145,7 @@ class DatabaseSync : public BaseObject { | |||
| 140 | 145 | static void ApplyChangeset(const v8::FunctionCallbackInfo<v8::Value>& args); | |
| 141 | 146 | static void EnableLoadExtension( | |
| 142 | 147 | const v8::FunctionCallbackInfo<v8::Value>& args); | |
| 148 | + static void EnableDefensive(const v8::FunctionCallbackInfo<v8::Value>& args); | ||
| 143 | 149 | static void LoadExtension(const v8::FunctionCallbackInfo<v8::Value>& args); | |
| 144 | 150 | static void SetAuthorizer(const v8::FunctionCallbackInfo<v8::Value>& args); | |
| 145 | 151 | static int AuthorizerCallback(void* user_data, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,56 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + const { skipIfSQLiteMissing } = require('../common/index.mjs'); | ||
| 3 | + const { test } = require('node:test'); | ||
| 4 | + const assert = require('node:assert'); | ||
| 5 | + const { DatabaseSync } = require('node:sqlite'); | ||
| 6 | + skipIfSQLiteMissing(); | ||
| 7 | + | ||
| 8 | + function checkDefensiveMode(db) { | ||
| 9 | + function journalMode() { | ||
| 10 | + return db.prepare('PRAGMA journal_mode').get().journal_mode; | ||
| 11 | + } | ||
| 12 | + | ||
| 13 | + assert.strictEqual(journalMode(), 'memory'); | ||
| 14 | + db.exec('PRAGMA journal_mode=OFF'); | ||
| 15 | + | ||
| 16 | + switch (journalMode()) { | ||
| 17 | + case 'memory': return true; // journal_mode unchanged, defensive mode must be active | ||
| 18 | + case 'off': return false; // journal_mode now 'off', so defensive mode not active | ||
| 19 | + default: throw new Error('unexpected journal_mode'); | ||
| 20 | + } | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + test('by default, defensive mode is off', (t) => { | ||
| 24 | + const db = new DatabaseSync(':memory:'); | ||
| 25 | + t.assert.strictEqual(checkDefensiveMode(db), false); | ||
| 26 | + }); | ||
| 27 | + | ||
| 28 | + test('when passing { defensive: true } as config, defensive mode is on', (t) => { | ||
| 29 | + const db = new DatabaseSync(':memory:', { | ||
| 30 | + defensive: true | ||
| 31 | + }); | ||
| 32 | + t.assert.strictEqual(checkDefensiveMode(db), true); | ||
| 33 | + }); | ||
| 34 | + | ||
| 35 | + test('defensive mode on after calling db.enableDefensive(true)', (t) => { | ||
| 36 | + const db = new DatabaseSync(':memory:'); | ||
| 37 | + db.enableDefensive(true); | ||
| 38 | + t.assert.strictEqual(checkDefensiveMode(db), true); | ||
| 39 | + }); | ||
| 40 | + | ||
| 41 | + test('defensive mode should be off after calling db.enableDefensive(false)', (t) => { | ||
| 42 | + const db = new DatabaseSync(':memory:', { | ||
| 43 | + defensive: true | ||
| 44 | + }); | ||
| 45 | + db.enableDefensive(false); | ||
| 46 | + t.assert.strictEqual(checkDefensiveMode(db), false); | ||
| 47 | + }); | ||
| 48 | + | ||
| 49 | + test('throws if options.defensive is provided but is not a boolean', (t) => { | ||
| 50 | + t.assert.throws(() => { | ||
| 51 | + new DatabaseSync(':memory:', { defensive: 42 }); | ||
| 52 | + }, { | ||
| 53 | + code: 'ERR_INVALID_ARG_TYPE', | ||
| 54 | + message: 'The "options.defensive" argument must be a boolean.', | ||
| 55 | + }); | ||
| 56 | + }); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments