Skip to content

fix(clickhouse): harden read-only query enforcement and centralize WHERE-clause validation#4895

Merged
waleedlatif1 merged 3 commits into
stagingfrom
waleedlatif1/clickhouse-where-or-injection
Jun 6, 2026
Merged

fix(clickhouse): harden read-only query enforcement and centralize WHERE-clause validation#4895
waleedlatif1 merged 3 commits into
stagingfrom
waleedlatif1/clickhouse-where-or-injection

Conversation

@waleedlatif1
Copy link
Copy Markdown
Collaborator

Summary

  • Centralize SQL WHERE-clause validation in lib/core/security/input-validation.server.ts (validateSqlWhereClause); ClickHouse update/delete/count now delegate to it instead of an inline denylist
  • Catch standalone truthy-literal tautologies the old guard missed — e.g. status = 'active' OR 1 (any non-zero literal is truthy in ClickHouse and matches every row) — while masking string/identifier literals so quoted values like name = 'OR 1' aren't false-positives
  • Enforce ClickHouse server-side readonly=1 on the Query (SELECT) operation: writes/DDL are rejected by the server and a query can't re-enable writes via SET readonly=0. This is the real boundary for the read-only op; the SQL-shape checks remain as defense-in-depth

Type of Change

  • Bug fix / hardening

Testing

Tested manually; type-check, lint, and behavioral tests for the WHERE-clause validator pass (blocks OR 1, 1=1, 'a'='a', stacked statements, comments, UNION/sleep; passes legitimate range filters and SQL-ish string values). Follow-up to the merged ClickHouse integration (#4883).

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jun 6, 2026 12:50am

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented Jun 6, 2026

PR Summary

Medium Risk
Changes security-sensitive ClickHouse query and mutation WHERE handling; mis-tuned regex could block legitimate filters or miss edge-case tautologies despite server readonly.

Overview
Hardens the ClickHouse Query operation and shared mutation WHERE validation.

For read-only queries, HTTP requests now pass ClickHouse readonly=1 when enforceReadOnly is set, so the server blocks writes/DDL (existing client-side SQL-shape checks stay as defense-in-depth). ClickHouse update/delete/count WHERE checks no longer use a local denylist; they call new validateSqlWhereClause in input-validation.server.ts.

That helper masks quoted literals before pattern scans (fewer false positives on values like 'OR 1'), blocks standalone OR <truthy> tautologies (e.g. status = 'active' OR 1), and documents that this layer is not a full security boundary.

Reviewed by Cursor Bugbot for commit 327ee96. Configure here.

Comment thread apps/sim/lib/core/security/input-validation.server.ts Outdated
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 6, 2026

Greptile Summary

This PR hardens the ClickHouse integration's read-only boundary and centralizes WHERE-clause validation. Server-side readonly=1 is now passed as a ClickHouse HTTP query parameter for the Query operation, ensuring the server rejects writes or DDL regardless of SQL-shape checks. WHERE-clause guard logic is moved from an inline denylist in utils.ts into a new shared validateSqlWhereClause export in input-validation.server.ts, which adds string-literal masking before pattern matching to prevent false positives on quoted values that look like injection patterns.

  • readonly=1 enforcement: Threaded through clickhouseRequest via a new readOnly option; executeClickHouseQuery passes enforceReadOnlyreadOnly so ClickHouse itself rejects writes — SQL-shape checks become defense-in-depth.
  • Centralized WHERE validation: validateSqlWhereClause replaces the per-call denylist with masked-pattern matching, catching bare truthy-literal tautologies (OR 1, OR 42) while correctly passing BETWEEN … AND <n> range filters and quoted string values that resemble injection patterns.

Confidence Score: 5/5

Safe to merge — the changes add a genuine server-side enforcement layer and centralize an existing guard without regressing any current validation path.

Both changed files make narrowly scoped, additive security improvements. The readonly=1 enforcement is correctly threaded through only the query operation and has no effect on the write/DDL helpers. The new validateSqlWhereClause preserves all previously blocked patterns while fixing the BETWEEN … AND false positive and adding masking to avoid false positives on quoted values. No mutation path loses a guard.

No files require special attention.

Important Files Changed

Filename Overview
apps/sim/app/api/tools/clickhouse/utils.ts Refactors validateWhereClause to delegate to the new shared validateSqlWhereClause, and adds server-side readonly=1 enforcement to the query operation by threading a readOnly option through clickhouseRequest.
apps/sim/lib/core/security/input-validation.server.ts Adds validateSqlWhereClause (and its supporting maskSqlStringLiterals helper) to centralize WHERE-clause injection/tautology detection with improved string-masking to avoid false positives on quoted values.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[executeClickHouseQuery] -->|enforceReadOnly=true| B{SQL shape checks}
    B -->|pass| C[clickhouseRequest\nreadOnly: true]
    B -->|fail| D[throw Error]
    C -->|readonly=1 in URL| E[(ClickHouse Server)]
    E -->|rejects write/DDL| F[Server-enforced boundary]

    G[executeClickHouseUpdate\nexecuteClickHouseDelete\nexecuteClickHouseCountRows] --> H[validateWhereClause]
    H --> I[validateSqlWhereClause\nin input-validation.server.ts]
    I --> J[maskSqlStringLiterals\nreplace string contents with spaces]
    J --> K{SQL_WHERE_MASKED_PATTERNS\nOR 1, stacked stmts, tautologies}
    K -->|match| L[return isValid: false]
    K -->|no match| M{SQL_WHERE_RAW_PATTERNS\n'a'='a' identical strings}
    M -->|match| L
    M -->|no match| N[return isValid: true]
Loading

Reviews (2): Last reviewed commit: "fix(clickhouse): allow BETWEEN bounds in..." | Re-trigger Greptile

Comment thread apps/sim/lib/core/security/input-validation.server.ts Outdated
Comment thread apps/sim/lib/core/security/input-validation.server.ts Outdated
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 327ee96. Configure here.

@waleedlatif1 waleedlatif1 merged commit 800f56f into staging Jun 6, 2026
14 checks passed
@waleedlatif1 waleedlatif1 deleted the waleedlatif1/clickhouse-where-or-injection branch June 6, 2026 01:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant