Fix the metrics query with a caller-chosen sort column
Your task
An analytics service fetches a tenant's metrics, sorted by a column the caller picks. The first version built the whole query by joining strings, so both the tenant and the column could inject SQL.
An assistant wrote build_metrics_query(tenant, sort_col, allowed_cols). It is in the editor. It has a bug. Find it and fix it.
Input and output:
tenant: the tenant id. It becomes a bound value, so any text is fine.sort_col: the column name the caller asked to sort by.allowed_cols: the real column names, spelled as in the table.- Return
[sql, tenant], or[]to refuse the request.
Rules the tests check:
- The SQL text is
SELECT * FROM metrics WHERE tenant_id = $1 ORDER BYfollowed by the quoted column. The tenant never appears in it. - Return
tenantexactly as given, as the second item. - Trim whitespace at both ends of
sort_coland lowercase it. If the result is empty, return[]. - Trim each entry of
allowed_colsand skip empty entries. The column is the first entry whose lowercase form equals the trimmed, lowercasedsort_col. If none does, return[]. A near match such asvaluesforvalue, orcreated_at ASC, is refused. - Write the column as the trimmed entry spells it, never as the caller typed it.
- Quote the column the way Postgres quotes an identifier: wrap it in double quotes and double any double quote inside it.
Examples:
build_metrics_query("acme", "created_at", ["created_at", "value"])returns["SELECT * FROM metrics WHERE tenant_id = $1 ORDER BY \"created_at\"", "acme"].build_metrics_query("acme", "id; DROP TABLE metrics; --", ["created_at", "value"])returns[].build_metrics_query("x' OR '1'='1", "value", ["created_at", "value"])returns["SELECT * FROM metrics WHERE tenant_id = $1 ORDER BY \"value\"", "x' OR '1'='1"]. The tenant is a bound value, so the quotes do nothing.
Then, in a sentence or two: how would you catch this kind of bug in review?
Examples
build_metrics_query(tenant: str, sort_col: str, allowed_cols: list[str]) -> list[str]Example 1
Input
build_metrics_query("acme", "created_at", ["created_at", "value"])Output
["SELECT * FROM metrics WHERE tenant_id = $1 ORDER BY \"created_at\"", "acme"]Example 2
Input
build_metrics_query("acme", "id; DROP TABLE metrics; --", ["created_at", "value"])Output
[]Example 3
Input
build_metrics_query("x' OR '1'='1", "value", ["created_at", "value"])Output
["SELECT * FROM metrics WHERE tenant_id = $1 ORDER BY \"value\"", "x' OR '1'='1"]