mirror of
https://github.com/hanzoai/o11y.git
synced 2026-10-15 12:58:33 +00:00
The v3/v4 builders are live — querier.go wires tracesV4.PrepareTracesQuery — and
they carry the same bug as the v5 statement builders, one layer down and one
position wider. Three caller values reached ClickHouse raw:
- the ORDER BY DIRECTION. Direction is a bare string off the request body and
every builder interpolated it beside the column, so
"asc,(select count() from system.tables)" was an entire extra ordering term.
It needed no quote and no backtick to get there — the cheapest of the three.
- the ORDER BY / GROUP BY / alias COLUMN, in backticks it can close.
- the ATTRIBUTE KEY, in the single quotes of the map and JSON accessors. That
one lands in the WHERE clause, so it is not an ordering oracle but a
predicate the caller writes: mapContains(attributes_string, 'a'),(select 1))
or ('1'='1') parses and runs.
The asymmetry is stark here. On the very lines that build a filter, the VALUE
goes through utils.QuoteEscapedString and the KEY beside it goes in bare. And
the allowlist that appears to gate the column — tagLookup — is built out of the
caller's own GroupBy, so a name repeated in both fields validates itself.
utils.AddBackTickToFormatTag is the pass-through underneath, and it declined to
quote in two ways: a name with no "." and no "-" came back BARE, and a name
already wrapped in backticks came back AS IS, which let a caller supply its own
quoting. Neither branch escaped a backtick. It now returns bare only for what it
was assuming — a plain identifier — and quotes and escapes everything else.
Direction.Keyword() is the guard for the direction, on the value rather than at
each of the sixteen sites that interpolate it. It folds case to compare because
callers send both, and returns the ORIGINAL on a match, which is safe precisely
because the match succeeded: a string whose lowercase is "asc" or "desc" is
those letters and nothing else. Anything else renders as empty, which is what an
absent direction already rendered as.
Identifiers and literals go through the same querybuilder.QuoteIdent and
EscapeLiteral the v5 builders now use. A real key takes the path it always did,
so the golden SQL in every one of these packages is unchanged.
Co-authored-by: Hanzo Dev <[email protected]>