Skip to content

fix: SQL injection in downsampler and basic_transformation plugins #79

@caterryan

Description

@caterryan

A security review of the InfluxData plugins flagged SQL injection vulnerabilities in two plugins that build queries from customer-controlled trigger args via Python f-strings instead of parameter binding.

downsampler — Critical

File: influxdata/downsampler/downsampler.py:911-931 (generate_tag_filter_clause)
for key, values in tag_values.items():
if len(values) == 1:
sql_clause += f"AND\n\t"{key}" = '{values[0]}'\n"
else:
quoted_values = ", ".join(f"'{v}'" for v in values)
sql_clause += f'AND\n\t"{key}" IN ({quoted_values})\n'

tag_values comes from the customer's tag_values trigger arg with no quote escaping anywhere. A trigger configured with tag_values="room:Kitchen'; --" produces:

AND "room" = 'Kitchen'; --'

The HTTP-mode parser (parse_tag_values_for_http) also doesn't validate tag-key characters, so key is injectable too (the scheduler-mode parser does enforce a regex on keys — please make both paths
consistent).

basic_transformation — High

File: influxdata/basic_transformation/basic_transformation.py:921-950 (generate_filter_clause)
for field, op, val in filters:
if isinstance(val, (int, float)):
val_str = str(val)
else:
escaped = val.replace("'", "''")
val_str = f"'{escaped}'"
clauses.append(f'AND\n\t"{field}" {op} {val_str}\n')

Values are escaped, but field (identifier) is not. A filter arg like 'foo" OR 1=1 --:=ignored' parses into field='foo" OR 1=1 --'which produces:

AND "foo" OR 1=1 --" = 'ignored'

Same plugin also has unvalidated measurement interpolation as FROM '{measurement}' in generate_query — a single quote in the measurement name escapes the literal.

Recommended fix

Both plugins already use parameter binding correctly elsewhere (e.g., influxdb3_local.query(query, {"measurement": measurement}) for information_schema.columns lookups). Extend that pattern to all
customer-influenced values:

  • Literal values (tag values, filter values): bind via $param parameters, don't string-interpolate.
  • Identifiers (tag keys, field names, measurement names) — these can't be parameterized, so:
    a. Validate against ^[A-Za-z0-9_][A-Za-z0-9_-]*$, AND
    b. Cross-check against the live schema (SHOW TABLES / information_schema.columns) before interpolation.

Threat-model context

In Hosted Enterprise, customer-supplied trigger args are untrusted input. The blast radius is bounded by the trigger's auth-token scope (the customer's own DB), so this isn't cross-tenant — but it does let a customer broaden a trigger's WHERE clause beyond what the trigger config implies, run arbitrary SELECTs against any table the trigger token can read, and potentially probe information_schema / system tables. Worth fixing on the same theory as any tenant-input → SQL path.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions