Skip to content

Commit fe07102

Browse files
Fix SQL injection vulnerability in metadata generator
Co-authored-by: thebearwithabite <216692431+thebearwithabite@users.noreply.github.com>
1 parent 613e4ba commit fe07102

2 files changed

Lines changed: 20 additions & 2 deletions

File tree

.jules/sentinel.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,11 @@
1919
**Prevention:**
2020
1. Always convert `pathlib.Path` objects to absolute strings using `str(path.absolute())` before passing them as arguments to `subprocess.run`.
2121
2. Absolute paths always begin with a directory separator (`/` on Unix) or a drive letter (`C:\` on Windows), guaranteeing the command-line tool parses them as file paths rather than flags or options.
22+
23+
## 2024-05-30 - SQL Injection via Unvalidated Dictionary Keys in Dynamic Queries
24+
25+
**Vulnerability:** The `save_file_metadata` method in `metadata_generator.py` constructed an `INSERT OR REPLACE` query dynamically using `metadata.keys()` directly as column names without any validation. Since standard `?` parameterization only protects values and not column names, an attacker could potentially inject malicious SQL by passing a crafted dictionary key.
26+
27+
**Learning:** When constructing dynamic SQL queries (e.g., `INSERT` or `UPDATE` with dynamically generated column names) from dictionary data, parameterization is insufficient to prevent SQL injection. The column keys themselves must be strictly validated against an explicit schema allowlist.
28+
29+
**Prevention:** Use `PRAGMA table_info(table_name)` to dynamically fetch the valid column schema directly from SQLite and filter input dictionary keys against this validated allowlist before generating the dynamic SQL query.

metadata_generator.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -467,9 +467,19 @@ def save_file_metadata(self, metadata: Dict[str, Any]) -> bool:
467467

468468
try:
469469
with sqlite3.connect(self.db_path) as conn:
470+
# Fetch valid column names to prevent SQL injection
471+
cursor = conn.execute("PRAGMA table_info(file_metadata)")
472+
valid_columns = {row[1] for row in cursor.fetchall()}
473+
474+
# Filter metadata to only include valid columns
475+
safe_metadata = {k: v for k, v in metadata.items() if k in valid_columns}
476+
477+
if not safe_metadata:
478+
return False
479+
470480
# Convert to database format
471-
columns = list(metadata.keys())
472-
values = list(metadata.values())
481+
columns = list(safe_metadata.keys())
482+
values = list(safe_metadata.values())
473483
placeholders = ', '.join(['?' for _ in values])
474484
column_names = ', '.join(columns)
475485

0 commit comments

Comments
 (0)