Description
The validate_sql function in the skill endpoints uses re.captures() / re.replace() which only operate on the first LIMIT clause found. A query with a low inner LIMIT (below MAX_ROWS) followed by a high outer LIMIT passes validation with the outer limit intact.
File: crates/daemon/src/server/skill_endpoints.rs (lines ~183-195)
Example
sql -- Inner LIMIT 50 is found first (< MAX_ROWS=100) → no replacement triggered -- Outer LIMIT 5000 is never inspected → survives unchanged SELECT * FROM (SELECT * FROM frames LIMIT 50) AS t LIMIT 5000
An LLM-generated query like this would return up to 5000 rows, scanning the entire database and consuming large amounts of memory.
Impact
The MAX_ROWS = 100 cap can be bypassed by any nested/subquery pattern, potentially causing the daemon to load and return unbounded result sets.
Suggested Fix
Use captures_iter() to inspect and cap all LIMIT clauses, or wrap the entire query unconditionally:
ust let capped = format!( "SELECT * FROM ({}) AS _capped LIMIT {}", normalized, MAX_ROWS );
This guarantees the cap regardless of query structure.
Description
The
validate_sqlfunction in the skill endpoints usesre.captures()/re.replace()which only operate on the firstLIMITclause found. A query with a low innerLIMIT(belowMAX_ROWS) followed by a high outerLIMITpasses validation with the outer limit intact.File:
crates/daemon/src/server/skill_endpoints.rs(lines ~183-195)Example
sql -- Inner LIMIT 50 is found first (< MAX_ROWS=100) → no replacement triggered -- Outer LIMIT 5000 is never inspected → survives unchanged SELECT * FROM (SELECT * FROM frames LIMIT 50) AS t LIMIT 5000An LLM-generated query like this would return up to 5000 rows, scanning the entire database and consuming large amounts of memory.
Impact
The
MAX_ROWS = 100cap can be bypassed by any nested/subquery pattern, potentially causing the daemon to load and return unbounded result sets.Suggested Fix
Use
captures_iter()to inspect and cap allLIMITclauses, or wrap the entire query unconditionally:ust let capped = format!( "SELECT * FROM ({}) AS _capped LIMIT {}", normalized, MAX_ROWS );This guarantees the cap regardless of query structure.