Files
supabase/apps/studio/data/logs/execute-analytics-sql.ts
T
Charis d117e70f6c feat: add safe SQL execution for analytics queries (BigQuery/ClickHouse) (#46287)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Feature - Security infrastructure

## What is the current behavior?

Analytics queries (BigQuery for legacy cloud, ClickHouse for self-hosted
OTEL) lack a compile-time safety model to prevent SQL injection from
untrusted input sources like URL parameters, UI inputs, or LLM output.

## What is the new behavior?

Implement a security model with a branded type `SafeLogSqlFragment` that
ensures all SQL fragments originate from either static code or
sanitization helpers. This includes:

- `analyticsLiteral()` for escaping string/number/boolean values
- `bqIdent()` and `clickhouseIdent()` for quoting identifiers with
engine-specific syntax
- `safeSql` template tag for composing fragments safely
- `executeAnalyticsSql()` wire boundary that rejects plain strings at
compile time

The pattern prevents cross-engine confusion by keeping
`SafeLogSqlFragment` (analytics) distinct from pg-meta's
`SafeSqlFragment` (Postgres).

## Additional context

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Introduced analytics SQL execution capabilities with built-in safety
validation for queries.
* Enhanced query robustness through keyword and identifier validation
mechanisms.
  * Improved error handling and reporting for analytics operations.

* **Tests**
* Added comprehensive test suite for analytics SQL safety and validation
utilities.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46287?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-25 08:40:18 -04:00

51 lines
1.7 KiB
TypeScript

/**
* Wire-boundary for analytics SQL execution.
*
* This is the analytics-path analog of pg-meta's `executeSql`. It accepts only
* `SafeLogSqlFragment` — plain strings are rejected at compile time — so any
* value flowing from URL parameters, UI inputs, or LLM output must pass through
* a sanitization helper in safe-analytics-sql.ts before reaching the wire.
*
* See .claude/skills/safe-sql-execution/SKILL.md for the full security model.
*/
import type { SafeLogSqlFragment } from './safe-analytics-sql'
import { handleError, post } from '@/data/fetchers'
/**
* Analytics endpoints that accept a POST body with `{ sql, iso_timestamp_start, iso_timestamp_end }`.
* Extend this union as additional endpoints are migrated to the safe-analytics-sql pattern.
*/
export type AnalyticsSqlEndpoint =
| '/platform/projects/{ref}/analytics/endpoints/logs.all'
| '/platform/projects/{ref}/analytics/endpoints/logs.all.otel'
export interface ExecuteAnalyticsSqlVariables {
projectRef: string
endpoint: AnalyticsSqlEndpoint
/** Must carry the `SafeLogSqlFragment` brand — plain strings are rejected at compile time. */
sql: SafeLogSqlFragment
iso_timestamp_start: string
iso_timestamp_end: string
signal?: AbortSignal
headers?: HeadersInit
}
export async function executeAnalyticsSql({
projectRef,
endpoint,
sql,
iso_timestamp_start,
iso_timestamp_end,
signal,
headers: headersInit,
}: ExecuteAnalyticsSqlVariables) {
const { data, error } = await post(endpoint, {
params: { path: { ref: projectRef } },
body: { sql, iso_timestamp_start, iso_timestamp_end },
signal,
headers: headersInit !== undefined ? new Headers(headersInit) : undefined,
})
if (error) handleError(error)
return data
}