Skip to content

Commit 19e2e3d

Browse files
committed
fix(clickhouse): block WITH-led writes/DDL in read-only query operation
1 parent c11527f commit 19e2e3d

1 file changed

Lines changed: 31 additions & 0 deletions

File tree

  • apps/sim/app/api/tools/clickhouse

apps/sim/app/api/tools/clickhouse/utils.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,32 @@ function hasChainedStatement(sql: string): boolean {
207207
return /;\s*\S/.test(maskSqlNoise(sql))
208208
}
209209

210+
/**
211+
* Write/DDL statement shapes that must never run under the read-only query
212+
* operation, even when wrapped by a leading `WITH` CTE (e.g. `WITH … INSERT INTO …`).
213+
* Patterns require the keyword's statement context (e.g. `insert into`, `alter table`)
214+
* so SQL functions/columns like `truncate(x)` or `created_at` are not false-positives.
215+
*/
216+
const MUTATING_STATEMENT = [
217+
/\binsert\s+into\b/i,
218+
/\bdelete\s+from\b/i,
219+
/\bupdate\s+[\w.`"]+\s+set\b/i,
220+
/\balter\s+table\b/i,
221+
/\b(?:create|attach)\s+(?:or\s+replace\s+)?(?:temporary\s+)?(?:table|database|dictionary|view|materialized\s+view|live\s+view|function|user|role)\b/i,
222+
/\bdrop\s+(?:table|database|dictionary|view|column|partition|index|function|user|role)\b/i,
223+
/\btruncate\s+table\b/i,
224+
/\brename\s+(?:table|database|dictionary)\b/i,
225+
/\bdetach\s+(?:table|database|dictionary|view|permanently)\b/i,
226+
/\b(?:grant|revoke)\b/i,
227+
/\boptimize\s+table\b/i,
228+
]
229+
230+
/** Whether a statement performs a write/DDL anywhere (comments and strings masked out). */
231+
function isMutatingStatement(sql: string): boolean {
232+
const masked = maskSqlNoise(sql)
233+
return MUTATING_STATEMENT.some((pattern) => pattern.test(masked))
234+
}
235+
210236
export async function executeClickHouseQuery(
211237
config: ClickHouseConnectionConfig,
212238
query: string,
@@ -225,6 +251,11 @@ export async function executeClickHouseQuery(
225251
'The query operation only allows a single statement; chained statements separated by ";" are not allowed. Use the Execute Raw SQL operation to run multiple statements.'
226252
)
227253
}
254+
if (isMutatingStatement(query)) {
255+
throw new Error(
256+
'The query operation only allows read-only statements; a write or DDL statement (e.g. INSERT/ALTER/DROP, including after a WITH clause) was detected. Use the Execute Raw SQL operation instead.'
257+
)
258+
}
228259
}
229260
const result = await clickhouseRequest(config, ensureJsonFormat(query))
230261
return parseRowsResult(result)

0 commit comments

Comments
 (0)