Files
supabase/apps/studio/data/logs/execute-analytics-sql.ts
Charis 9bdb757b6a feat(logs): brand Observability/EdgeFunctions SQL with SafeLogSqlFragment (#8) (#46466)
## 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?

Refactor / security hardening — continues the analytics SQL
provenance-tracking series (PR 8).

## What is the current behavior?

- `generateRegexpWhere` (unsafe: interpolates user-controlled filter
keys/values without escaping) still exists alongside
`generateRegexpWhereSafe` and its tests only cover the old function.
- `usePostgrestOverviewMetrics` builds a SQL query string with plain
string interpolation and calls the analytics endpoint directly via
`get()`.
- `edge-functions-last-hour-stats-query` builds a SQL query with
`functionIds` escaped via Postgres-only `quoteLiteral` and calls the
analytics endpoint directly via `post()`.
- `executeAnalyticsSql` has no way to pass a `key` query-string param
for network-tool identification.
- `rawSql('minute')` / `rawSql('hour')` / `rawSql('day')` and
`rawSql(value ? 'true' : 'false')` are used for static strings that
could be expressed with the `safeSql` template tag.

## What is the new behavior?

- `generateRegexpWhere` is deleted; its tests are replaced with
`generateRegexpWhereSafe` coverage including injection-attempt cases
(`level OR id IS NOT NULL`, `request.method); DROP TABLE edge_logs; --`)
that verify predicates are silently dropped rather than emitted.
- `usePostgrestOverviewMetrics` returns `SafeLogSqlFragment` from its
SQL builder and routes through `executeAnalyticsSql`.
- `edge-functions-last-hour-stats-query` uses `analyticsLiteral`
(BigQuery/ClickHouse-correct escaping) instead of `quoteLiteral`
(Postgres-only) and routes through `executeAnalyticsSql`.
- `executeAnalyticsSql` accepts an optional `key?: string` forwarded as
a query-string param on both GET and POST requests; `key:
'last-hour-stats'` is restored on the edge-functions query.
- Static `rawSql('...')` calls replaced with `safeSql\`...\`` template
literals throughout.

## Additional context

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

## Summary by CodeRabbit

## Bug Fixes
- Removed legacy unsafe SQL-filter utility from Reports

## Chores
- Enhanced analytics SQL execution infrastructure with improved error
handling
- Added optional request identification parameter to analytics query
execution
- Refined SQL filtering mechanisms in reporting features

<!-- 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/46466?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-28 10:30:57 -04:00

77 lines
2.5 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 { get, handleError, post } from '@/data/fetchers'
/**
* Analytics endpoints that accept `{ sql, iso_timestamp_start, iso_timestamp_end }`
* either as a POST body or GET query string. 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
/** Defaults to 'post'. Use 'get' to preserve wire behavior when migrating legacy GET callers. */
method?: 'get' | 'post'
/**
* Optional query-string key for network-tool identification.
* Not part of the OpenAPI schema; accepted by the server and visible in DevTools.
*/
key?: string
signal?: AbortSignal
headers?: HeadersInit
}
export async function executeAnalyticsSql({
projectRef,
endpoint,
sql,
iso_timestamp_start,
iso_timestamp_end,
method = 'post',
key,
signal,
headers: headersInit,
}: ExecuteAnalyticsSqlVariables) {
const headers = headersInit !== undefined ? new Headers(headersInit) : undefined
if (method === 'get') {
const { data, error } = await get(endpoint, {
params: {
path: { ref: projectRef },
query: { sql, iso_timestamp_start, iso_timestamp_end, ...(key ? { key } : {}) },
},
signal,
headers,
})
if (error) handleError(error)
return data
}
const { data, error } = await post(endpoint, {
// @ts-ignore key is not in the OpenAPI schema; included only for network-tool identification
params: { path: { ref: projectRef }, ...(key ? { query: { key } } : {}) },
body: { sql, iso_timestamp_start, iso_timestamp_end },
signal,
headers,
})
if (error) handleError(error)
return data
}