Skip to content

security(pipes): non-scalar pipe param values inlined into SQL unescaped + declared type unchecked (SQL injection) #317

Description

@EricAndrechek

Area: pipes · policy — security (SQL injection / escaping bypass) · found via pre-launch audit

Expected: the docs promise a pipe parameter value "can't break out of its literal and inject SQL," and a type: number parameter rejects a non-number.

Actual: internal/pipes/pipes.go formatParamValue — the default: branch (:230, fmt.Sprintf("%v", v)) emits raw, unescaped text. Scalar strings are escaped (\→\\, '→'') and scalar numbers are digits-only, so they're safe — but a JSON array or object value hits default: raw ([evil] / map[k:v]). BindParams reads only Required/Default and never checks ParamDef.Type, returning a nil bind slice (pure string interpolation, no driver ? params). internal/api/pipes.go decodes the POST into map[string]any and passes values verbatim; GET/POST /v1/pipes/{name} are outside RequireAdmin (authz is per-pipe allowed_roles, commonly [public] → unauthenticated). In a string-context pipe (WHERE col = '{{p}}') an array element's inner ' terminates the literal → clean UNION/boolean injection that bypasses the column allowlist.

Impact: latent on Stats today (the only parameterized pipe interpolates {{limit}} in a numeric LIMIT least({{limit}},50) context, so an array is a CH type error — yielding a full-pipe-SQL information disclosure in the echoed 500 rather than exfil), but one string-context public pipe away from a live unauth column-allowlist bypass. The structured-query path is unaffected (positional ? binds + per-column gating), so pipes are the one inlined-SQL surface. Re-verified fmt.Sprintf("%v") default branch on 40619b8a.

Scope: enforce ParamDef.Type in BindParams; escape or reject non-scalar values.

Related: #32 (this is the security core of Native SQL Templating — #32's own injection review only analyzed scalar inputs and missed the non-scalar breakout), the pipe cache-bypass issue.


From WaveHouse-Stats pre-launch security audit (WAVEHOUSE-FEEDBACK.md dogfooding), audited dev 60fed15; re-verified live on 40619b8a (2026-06-10). Full repro (payloads, local CH exfil of denied actor_id, live marker echo): docs/security/pipe-param-injection-poc.md (private). Filed via /pm-triage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area/pipesNamed query pipesarea/policyAccess control policies (Hasura-style)bugSomething isn't workingsecuritySecurity-sensitive issue or fix

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions