Validates query is a single SELECT, SHOW, or EXPLAIN statement.
(sql: &str)
| 1173 | |
| 1174 | /// Validates query is a single SELECT, SHOW, or EXPLAIN statement. |
| 1175 | fn validate_readonly_query(sql: &str) -> Result<(), McpRequestError> { |
| 1176 | let sql = sql.trim(); |
| 1177 | if sql.is_empty() { |
| 1178 | return Err(McpRequestError::QueryValidationFailed( |
| 1179 | "Empty query".to_string(), |
| 1180 | )); |
| 1181 | } |
| 1182 | |
| 1183 | // Parse the SQL to get AST |
| 1184 | let stmts = parse(sql).map_err(|e| { |
| 1185 | McpRequestError::QueryValidationFailed(format!("Failed to parse SQL: {}", e)) |
| 1186 | })?; |
| 1187 | |
| 1188 | // Only allow a single statement |
| 1189 | if stmts.len() != 1 { |
| 1190 | return Err(McpRequestError::QueryValidationFailed(format!( |
| 1191 | "Only one query allowed at a time. Found {} statements.", |
| 1192 | stmts.len() |
| 1193 | ))); |
| 1194 | } |
| 1195 | |
| 1196 | // Allowlist: SELECT, SHOW, and every read-only EXPLAIN variant. EXPLAIN |
| 1197 | // expands to six distinct Statement variants in the parser (ExplainPlan |
| 1198 | // covers only the most common one). Listing them out exhaustively beats |
| 1199 | // matching by string prefix so a new write-capable EXPLAIN variant — were |
| 1200 | // one ever added — would have to be considered here. |
| 1201 | let stmt = &stmts[0]; |
| 1202 | use mz_sql_parser::ast::Statement; |
| 1203 | |
| 1204 | match &stmt.ast { |
| 1205 | Statement::Select(_) |
| 1206 | | Statement::Show(_) |
| 1207 | | Statement::ExplainPlan(_) |
| 1208 | | Statement::ExplainPushdown(_) |
| 1209 | | Statement::ExplainTimestamp(_) |
| 1210 | | Statement::ExplainSinkSchema(_) |
| 1211 | | Statement::ExplainAnalyzeObject(_) |
| 1212 | | Statement::ExplainAnalyzeCluster(_) => Ok(()), |
| 1213 | _ => Err(McpRequestError::QueryValidationFailed( |
| 1214 | "Only SELECT, SHOW, and EXPLAIN statements are allowed".to_string(), |
| 1215 | )), |
| 1216 | } |
| 1217 | } |
| 1218 | |
| 1219 | async fn execute_query( |
| 1220 | client: &mut AuthedClient, |