From 7986cb056a15b0a6b8996f8d9c938542ccacef6b Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 25 Aug 2026 09:56:47 +0000 Subject: [PATCH] fix: prevent SQL injection in sqlite execute and query Added support for parameterized queries in `sqlite.execute` and `sqlite.query` functions to prevent SQL injection vulnerabilities. Parameters are parsed from an optional third argument and safely bound to SQL statements using `rusqlite::params_from_iter`. Co-authored-by: Tcode-Motion <188012755+Tcode-Motion@users.noreply.github.com> --- compiler/llvm_backend/src/jit.rs | 4 +-- stdlib/src/sqlite.rs | 62 +++++++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/compiler/llvm_backend/src/jit.rs b/compiler/llvm_backend/src/jit.rs index 9005daa3..e05cbfe8 100644 --- a/compiler/llvm_backend/src/jit.rs +++ b/compiler/llvm_backend/src/jit.rs @@ -5,8 +5,8 @@ #![cfg(feature = "llvm")] use llvm_sys::core::*; -use llvm_sys::orc2::*; use llvm_sys::orc2::lljit::*; +use llvm_sys::orc2::*; use std::collections::HashMap; use std::ffi::CString; use std::ptr; @@ -60,7 +60,7 @@ impl LLVMJitEngine { // 3. Set host target triple let _host_triple = LLVMOrcLLJITGetExecutionSession(self.jit); // session triple fallback - // We can just keep the default LLVM target triple + // We can just keep the default LLVM target triple // 4. Wrap Module in ThreadSafeModule let tsm = LLVMOrcCreateNewThreadSafeModule(ctx.module, self.ts_ctx); diff --git a/stdlib/src/sqlite.rs b/stdlib/src/sqlite.rs index c2b0313f..14e47625 100644 --- a/stdlib/src/sqlite.rs +++ b/stdlib/src/sqlite.rs @@ -12,6 +12,17 @@ thread_local! { static NEXT_ID: AtomicI64 = AtomicI64::new(1); +fn runtime_to_sql_value(v: &RuntimeValue) -> rusqlite::types::Value { + match v { + RuntimeValue::Null => rusqlite::types::Value::Null, + RuntimeValue::Bool(b) => rusqlite::types::Value::Integer(if *b { 1 } else { 0 }), + RuntimeValue::Int(i) => rusqlite::types::Value::Integer(*i), + RuntimeValue::Float(f) => rusqlite::types::Value::Real(*f), + RuntimeValue::Str(s) => rusqlite::types::Value::Text(s.clone()), + _ => rusqlite::types::Value::Text(v.to_string()), + } +} + impl StdlibRegistry { pub fn register_sqlite(&mut self) { let mut exports: HashMap> = @@ -53,16 +64,34 @@ impl StdlibRegistry { })?; let sql = args[1].to_string(); + let params: Vec = if let Some(arg) = args.get(2) { + if let RuntimeValue::List { items, .. } = arg { + items.borrow().iter().map(runtime_to_sql_value).collect() + } else { + return Err(RuntimeError::new( + RuntimeErrorKind::TypeMismatch { + expected: "list".to_string(), + found: arg.runtime_type().to_string(), + }, + None, + None, + )); + } + } else { + Vec::new() + }; + CONNECTIONS.with(|m| { let mut map = m.borrow_mut(); if let Some(conn) = map.get_mut(&id) { - conn.execute(&sql, []).map_err(|e| { - RuntimeError::new( - RuntimeErrorKind::InvalidOperation(e.to_string()), - None, - None, - ) - })?; + conn.execute(&sql, rusqlite::params_from_iter(params)) + .map_err(|e| { + RuntimeError::new( + RuntimeErrorKind::InvalidOperation(e.to_string()), + None, + None, + ) + })?; Ok(()) } else { Err(RuntimeError::new( @@ -94,6 +123,23 @@ impl StdlibRegistry { })?; let sql = args[1].to_string(); + let params: Vec = if let Some(arg) = args.get(2) { + if let RuntimeValue::List { items, .. } = arg { + items.borrow().iter().map(runtime_to_sql_value).collect() + } else { + return Err(RuntimeError::new( + RuntimeErrorKind::TypeMismatch { + expected: "list".to_string(), + found: arg.runtime_type().to_string(), + }, + None, + None, + )); + } + } else { + Vec::new() + }; + let rows = CONNECTIONS.with(|m| { let mut map = m.borrow_mut(); if let Some(conn) = map.get_mut(&id) { @@ -110,7 +156,7 @@ impl StdlibRegistry { .collect(); let mut rows = Vec::new(); let row_iter = stmt - .query_map([], |row| { + .query_map(rusqlite::params_from_iter(params), |row| { let mut map = IndexMap::new(); for i in 0..col_count { let name = col_names[i].clone();