Skip to content

security(pipes): a quoted placeholder lets a value break out of its literal #662

Description

@EricAndrechek

Area: pipes · security

Expected: a pipe parameter's value cannot change the structure of the statement, whatever the template looks like.

Actual: formatParamValue (internal/pipes/pipes.go) renders a string value as its own quoted literal ('…', with \ and ' escaped). If the template also puts quotes around the placeholder, e.g. WHERE id = '{{id}}', the value's opening quote closes the author's. The value OR 1=1 OR id = binds to:

WHERE id = '' OR 1=1 OR id = ''

The bind output is verified against BindParams. That ClickHouse then matches every row is inferred.

Impact:

  • In a read pipe, this defeats the template's filter.
  • In a write pipe, e.g. ALTER TABLE t DELETE WHERE id = '{{id}}', it deletes every row.
  • A pipe is authorized only by its allowed_roles, so any role the operator allowed can do this, including default_role.

Mitigation until this is fixed: write each placeholder bare (WHERE id = {{id}}). #634 adds this rule to pipes.mdx.

Scope: make BindParams, or the check that runs when a pipe is loaded or put, refuse a template whose placeholder sits inside a quoted literal, after stripping comments. The mistake then fails when the pipe is defined, instead of binding silently.

Related:

Activity

  1. added
    bugSomething isn't working
    securitySecurity-sensitive issue or fix
    on Sep 26, 2026
  2. EricAndrechek commented on Sep 26, 2026

    @EricAndrechek
    MemberAuthor

    A constraint for whoever picks this up: the mitigation ("write the placeholder bare") has no clean form for a String column compared against a numeric-looking value.

    formatParamValue renders any string that parses as a number without quotes. So WHERE s = {{id}} with ?id=5 binds to WHERE s = 5, which fails with Code: 386 … no supertype for String, UInt8. That was verified on ClickHouse 26.6.3.62. Wrapping it as toString({{id}}) avoids the error but changes the value: 007 becomes 7, and 1.50 becomes 1.5.

    So refusing a quoted placeholder without another change leaves authors with no correct way to write that comparison. The fix probably needs a declared parameter type (type: "string" always renders quoted) at the same time. #32 already notes the numeric-looking-string rendering.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/pipesNamed query pipesbugSomething 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