diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 0000000..d5d74ce --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2024-06-04 - SQLite Dynamic Column Name Injection +**Vulnerability:** SQL injection vulnerability in dynamic column name interpolation (`format!("SELECT {field_name} FROM books ...")`) in `get_book_field`, `get_book_i64_field`, and `get_book_field_value_for_lock`. +**Learning:** Rust's `format!` macro for SQL queries allows injection if the interpolated variable (`field_name`) isn't validated. Column names can't be parameterized in standard SQLite, leading to developers occasionally interpolating them directly. +**Prevention:** Implement strict string validation (e.g., `field_name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')`) before injecting dynamic values like column names or table names into raw SQL strings. diff --git a/src-tauri/src/library/db.rs b/src-tauri/src/library/db.rs index ebc369f..c236c1d 100644 --- a/src-tauri/src/library/db.rs +++ b/src-tauri/src/library/db.rs @@ -2196,6 +2196,9 @@ impl Repository { } fn get_book_field(&self, book_id: &str, field_name: &str) -> anyhow::Result> { + if !field_name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { + return Err(anyhow!("Invalid field name")); + } let conn = self.conn()?; let query = format!("SELECT {field_name} FROM books WHERE id = ?1"); Ok( @@ -2207,6 +2210,9 @@ impl Repository { } fn get_book_i64_field(&self, book_id: &str, field_name: &str) -> anyhow::Result> { + if !field_name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { + return Err(anyhow!("Invalid field name")); + } let conn = self.conn()?; let query = format!("SELECT {field_name} FROM books WHERE id = ?1"); Ok( @@ -2315,6 +2321,9 @@ fn get_book_field_value_for_lock( book_id: &str, field_name: &str, ) -> anyhow::Result> { + if !field_name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { + return Err(anyhow!("Invalid field name")); + } let query = format!("SELECT {field_name} FROM books WHERE id = ?1 LIMIT 1"); if matches!(field_name, "page_count" | "series_index") { let numeric = tx