Skip to content

Commit 80c1ff3

Browse files
committed
fix: add WHERE clause validation and null guards for data-diff
- Add `_validate_where_clause()` to reject injection patterns (semicolons, comments) - Validate all WHERE clause parameters before passing to Rust engine - Remove unused `_SIDE_MAP` constant from `data_diff.py` - Add null guards for `diff_percent` and `match_percent` in TypeScript to prevent NaN display
1 parent 888de66 commit 80c1ff3

2 files changed

Lines changed: 21 additions & 5 deletions

File tree

packages/altimate-engine/src/altimate_engine/sql/data_diff.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,18 @@
2424
except ImportError:
2525
RELADIFF_AVAILABLE = False
2626

27-
# Map TableSide enum values to warehouse names
28-
_SIDE_MAP = {"Table1": "source", "Table2": "target"}
27+
28+
def _validate_where_clause(clause: str | None) -> str | None:
29+
"""Validate a WHERE clause for safety. Rejects injection patterns."""
30+
if clause is None:
31+
return None
32+
if ";" in clause:
33+
raise ValueError("WHERE clause must not contain semicolons")
34+
if "--" in clause:
35+
raise ValueError("WHERE clause must not contain SQL comments (--)")
36+
if "/*" in clause:
37+
raise ValueError("WHERE clause must not contain block comments (/*)")
38+
return clause
2939

3040

3141
def _resolve_dialect(warehouse_name: str) -> str:
@@ -101,6 +111,11 @@ def run_data_diff(
101111

102112
target_warehouse = target_warehouse or source_warehouse
103113

114+
# Validate WHERE clauses
115+
where_clause = _validate_where_clause(where_clause)
116+
source_where_clause = _validate_where_clause(source_where_clause)
117+
target_where_clause = _validate_where_clause(target_where_clause)
118+
104119
# Resolve dialects from connection types
105120
dialect1 = _resolve_dialect(source_warehouse)
106121
dialect2 = _resolve_dialect(target_warehouse)

packages/opencode/src/altimate/tools/data-diff-run.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,16 +123,17 @@ function formatOutcome(outcome: Record<string, unknown>, args: Record<string, un
123123
lines.push(`Exclusive to table1: ${stats.exclusive_table1 ?? 0}`)
124124
lines.push(`Exclusive to table2: ${stats.exclusive_table2 ?? 0}`)
125125
lines.push(`Updated: ${stats.updated ?? 0}`)
126-
lines.push(`Diff %: ${((stats.diff_percent as number) * 100).toFixed(2)}%`)
126+
const diffPct = stats.diff_percent != null ? ((stats.diff_percent as number) * 100).toFixed(2) : "N/A"
127+
lines.push(`Diff %: ${diffPct}%`)
127128

128129
// Per-column match rates
129130
const matchRates = (stats.column_match_rates ?? []) as Record<string, unknown>[]
130131
if (matchRates.length > 0) {
131132
lines.push("")
132133
lines.push("Column Match Rates:")
133134
for (const col of matchRates) {
134-
const pct = (col.match_percent as number).toFixed(1)
135-
lines.push(` ${col.column}: ${pct}% (${col.matched}/${col.total})`)
135+
const pct = col.match_percent != null ? (col.match_percent as number).toFixed(1) : "N/A"
136+
lines.push(` ${col.column}: ${pct}% (${col.matched ?? "?"}/${col.total ?? "?"})`)
136137
}
137138
}
138139

0 commit comments

Comments
 (0)