Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
## 2026-03-31 - Prevent SQL Injection via Dictionary Unpacking in SQLite Operations\n**Vulnerability:** SQL injection vulnerability in `streamrip/db.py`'s `contains` and `remove` methods where user-provided `**items` keys were unvalidated or validated using `assert` (which is stripped under python -O), allowing arbitrary strings to be interpolated directly into SQL query text (`f"{key}=?"`).\n**Learning:** Dictionary unpacking (`**kwargs`) circumvents typical Python identifier naming constraints, meaning dictionary keys containing SQL fragments could be passed directly into SQL query generation if not rigorously validated against a schema allowlist.\n**Prevention:** Always explicitly validate dynamic keys against an allowed schema structure using a hard `raise ValueError` before incorporating them into SQL text construction, rather than relying on assertions.
11 changes: 8 additions & 3 deletions streamrip/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@ def contains(self, **items) -> bool:
:rtype: bool
"""
allowed_keys = set(self.structure.keys())
assert all(
key in allowed_keys for key in items.keys()
), f"Invalid key. Valid keys: {allowed_keys}"
for key in items.keys():
if key not in allowed_keys:
raise ValueError(f"Invalid key. Valid keys: {allowed_keys}")

items = {k: str(v) for k, v in items.items()}

Expand Down Expand Up @@ -155,6 +155,11 @@ def remove(self, **items):

:param items:
"""
allowed_keys = set(self.structure.keys())
for key in items.keys():
if key not in allowed_keys:
raise ValueError(f"Invalid key. Valid keys: {allowed_keys}")

conditions = " AND ".join(f"{key}=?" for key in items.keys())
command = f"DELETE FROM {self.name} WHERE {conditions}"

Expand Down