ClickHouse: SQL injection, nil dereference, and wrong context in destination plugin
Bug 1: SQL injection in queries/table.go
Files: plugins/destination/clickhouse/queries/table.go:58-63
Problem: Database and table names are interpolated directly into SQL query strings using fmt.Sprintf with '%s' quoting, which doesn't prevent SQL injection if the names contain single quotes or escape characters.
Vulnerable queries:
func GetPartitionKeyAndSortingKeyQuery(database, table string) string {
return fmt.Sprintf(`SELECT partition_key, sorting_key FROM system.tables WHERE database = '%s' AND name = '%s'`, database, table)
}
func GetTTLQuery(database, table string) string {
return fmt.Sprintf(`SHOW CREATE TABLE "%s"."%s"`, database, table)
}
Fix: Use util.SanitizeID() to properly escape identifiers:
return fmt.Sprintf(`SELECT partition_key, sorting_key FROM system.tables WHERE database = %s AND name = %s`, util.SanitizeID(database), util.SanitizeID(table))
return fmt.Sprintf("SHOW CREATE TABLE %s.%s", util.SanitizeID(database), util.SanitizeID(table))
Bug 2: Nil pointer dereference in equalTTLs (client/table.go)
File: plugins/destination/clickhouse/client/table.go:72
Problem: The *uint8 result from retryQueryRowAndScan is dereferenced (*result == 1) without a nil check. If the query returns NULL (e.g., on a table with no TTL), this panics.
Current code:
var result *uint8
err := retryQueryRowAndScan(ctx, c.logger, c.conn, sql, []any{}, []any{&result})
if err != nil {
return false, err
}
return *result == 1, nil // panic if result is nil
Bug 3: Wrong context in equalTTLs (client/table.go)
File: plugins/destination/clickhouse/client/table.go:72 (pre-fix)
Problem: retryQueryRowAndScan was called with context.Background() instead of the caller's context. This means the TTL comparison query cannot be cancelled or respect timeouts set by the caller.
Current code (pre-fix):
err := retryQueryRowAndScan(context.Background(), c.logger, c.conn, sql, []any{}, []any{&result})
Reproduction
- SQL injection: Create a plugin config with a table named
foo'; DROP TABLE system.tables; -- and run a sync with ClickHouse destination.
- Nil dereference: Sync a table that has no TTL set but the migration code checks TTL. The
equalTTLs query returns NULL → *result panics.
- Wrong context: Timeout or cancel a long sync — the TTL comparison continues with
context.Background() and cannot be cancelled.
ClickHouse: SQL injection, nil dereference, and wrong context in destination plugin
Bug 1: SQL injection in queries/table.go
Files:
plugins/destination/clickhouse/queries/table.go:58-63Problem: Database and table names are interpolated directly into SQL query strings using
fmt.Sprintfwith'%s'quoting, which doesn't prevent SQL injection if the names contain single quotes or escape characters.Vulnerable queries:
Fix: Use
util.SanitizeID()to properly escape identifiers:Bug 2: Nil pointer dereference in
equalTTLs(client/table.go)File:
plugins/destination/clickhouse/client/table.go:72Problem: The
*uint8result fromretryQueryRowAndScanis dereferenced (*result == 1) without a nil check. If the query returns NULL (e.g., on a table with no TTL), this panics.Current code:
Bug 3: Wrong context in
equalTTLs(client/table.go)File:
plugins/destination/clickhouse/client/table.go:72(pre-fix)Problem:
retryQueryRowAndScanwas called withcontext.Background()instead of the caller's context. This means the TTL comparison query cannot be cancelled or respect timeouts set by the caller.Current code (pre-fix):
Reproduction
foo'; DROP TABLE system.tables; --and run a sync with ClickHouse destination.equalTTLsquery returns NULL →*resultpanics.context.Background()and cannot be cancelled.