Skip to content

Commit 990ea83

Browse files
committed
sqlite: validate StatementSync.run() integers
Use the standard SQLite integer conversion for changes and lastInsertRowid. Throw ERR_OUT_OF_RANGE when a value cannot be represented safely as a Number, or return it as a BigInt when BigInt reads are enabled. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: codex:gpt-5.6-sol
1 parent c7b962c commit 990ea83

2 files changed

Lines changed: 42 additions & 16 deletions

File tree

src/node_sqlite.cc

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,24 @@ void BindingData::CreatePerContextProperties(Local<Object> target,
105105
principal->AddBindingData<BindingData>(target);
106106
}
107107

108+
inline MaybeLocal<Value> IntegerToValue(Isolate* isolate,
109+
sqlite3_int64 value,
110+
bool use_big_ints) {
111+
if (use_big_ints) {
112+
return BigInt::New(isolate, value);
113+
}
114+
115+
if (value >= -kMaxSafeJsInteger && value <= kMaxSafeJsInteger) {
116+
return Number::New(isolate, value);
117+
}
118+
119+
THROW_ERR_OUT_OF_RANGE(
120+
isolate,
121+
"Value is too large to be represented as a JavaScript number: %" PRId64,
122+
value);
123+
return MaybeLocal<Value>();
124+
}
125+
108126
#define CHECK_ERROR_OR_THROW(isolate, db, expr, expected, ret) \
109127
do { \
110128
int r_ = (expr); \
@@ -159,16 +177,7 @@ void BindingData::CreatePerContextProperties(Local<Object> target,
159177
switch (sqlite3_##from##_type(__VA_ARGS__)) { \
160178
case SQLITE_INTEGER: { \
161179
sqlite3_int64 val = sqlite3_##from##_int64(__VA_ARGS__); \
162-
if ((use_big_int_args)) { \
163-
(result) = BigInt::New((isolate), val); \
164-
} else if (std::abs(val) <= kMaxSafeJsInteger) { \
165-
(result) = Number::New((isolate), val); \
166-
} else { \
167-
THROW_ERR_OUT_OF_RANGE((isolate), \
168-
"Value is too large to be represented as a " \
169-
"JavaScript number: %" PRId64, \
170-
val); \
171-
} \
180+
(result) = IntegerToValue((isolate), val, (use_big_int_args)); \
172181
break; \
173182
} \
174183
case SQLITE_FLOAT: { \
@@ -3251,12 +3260,10 @@ MaybeLocal<Object> StatementExecutionHelper::Run(Environment* env,
32513260
Local<Value> last_insert_rowid_val;
32523261
Local<Value> changes_val;
32533262

3254-
if (use_big_ints) {
3255-
last_insert_rowid_val = BigInt::New(isolate, last_insert_rowid);
3256-
changes_val = BigInt::New(isolate, changes);
3257-
} else {
3258-
last_insert_rowid_val = Number::New(isolate, last_insert_rowid);
3259-
changes_val = Number::New(isolate, changes);
3263+
if (!IntegerToValue(isolate, last_insert_rowid, use_big_ints)
3264+
.ToLocal(&last_insert_rowid_val) ||
3265+
!IntegerToValue(isolate, changes, use_big_ints).ToLocal(&changes_val)) {
3266+
return MaybeLocal<Object>();
32603267
}
32613268

32623269
auto run_result_template = env->sqlite_run_result_template();

test/parallel/test-sqlite-statement-sync.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,25 @@ suite('StatementSync.prototype.setReadBigInts()', () => {
821821
});
822822
});
823823

824+
test('BigInt is required for reading large last insert row IDs', (t) => {
825+
using db = new DatabaseSync(':memory:');
826+
db.exec('CREATE TABLE data(key INTEGER PRIMARY KEY) STRICT');
827+
const insert = db.prepare('INSERT INTO data VALUES (?)');
828+
829+
t.assert.throws(() => {
830+
insert.run(9007199254740993n);
831+
}, {
832+
code: 'ERR_OUT_OF_RANGE',
833+
message: /^Value is too large to be represented as a JavaScript number: 9007199254740993$/,
834+
});
835+
836+
insert.setReadBigInts(true);
837+
t.assert.deepStrictEqual(insert.run(9007199254740995n), {
838+
changes: 1n,
839+
lastInsertRowid: 9007199254740995n,
840+
});
841+
});
842+
824843
test('throws if the statement is already finalized', (t) => {
825844
using db = new DatabaseSync(':memory:');
826845
const stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');

0 commit comments

Comments
 (0)