Version in base suite: 1.19-1 Base version: pgextwlist_1.19-1 Target version: pgextwlist_1.19-1+deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/p/pgextwlist/pgextwlist_1.19-1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/p/pgextwlist/pgextwlist_1.19-1+deb13u1.dsc changelog | 15 + patches/0ae3d2dfdbfe38aa32b7acb02784df4ba9c6708b | 241 +++++++++++++++++++++++ patches/series | 1 3 files changed, 257 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp6w5gge8g/pgextwlist_1.19-1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp6w5gge8g/pgextwlist_1.19-1+deb13u1.dsc: no acceptable signature found diff -Nru pgextwlist-1.19/debian/changelog pgextwlist-1.19/debian/changelog --- pgextwlist-1.19/debian/changelog 2025-06-12 12:38:58.000000000 +0000 +++ pgextwlist-1.19/debian/changelog 2026-07-08 16:12:26.000000000 +0000 @@ -1,3 +1,18 @@ +pgextwlist (1.19-1+deb13u1) trixie-security; urgency=medium + + * Reject substituting extension schemas or owners matching ["$'\]. + + The extwlist.custom_path script mechanism was vulnerable to SQL + injection via crafted schema and user names. The same problem was fixed + in August 2023 in PostgreSQL (cd5f2a3570), and is now fixed in + pgextwlist as well. Substitutions that attempt to insert any of "$'\ are + now simply rejected. (We don't try to quote the values as we don't know + which quoting context we are in.) + + CVE-2023-39417 + + -- Christoph Berg Wed, 08 Jul 2026 18:12:26 +0200 + pgextwlist (1.19-1) unstable; urgency=medium * PG18 Support, take 2: Revert CreateQueryDesc change. diff -Nru pgextwlist-1.19/debian/patches/0ae3d2dfdbfe38aa32b7acb02784df4ba9c6708b pgextwlist-1.19/debian/patches/0ae3d2dfdbfe38aa32b7acb02784df4ba9c6708b --- pgextwlist-1.19/debian/patches/0ae3d2dfdbfe38aa32b7acb02784df4ba9c6708b 1970-01-01 00:00:00.000000000 +0000 +++ pgextwlist-1.19/debian/patches/0ae3d2dfdbfe38aa32b7acb02784df4ba9c6708b 2026-07-08 16:12:26.000000000 +0000 @@ -0,0 +1,241 @@ +commit 0ae3d2dfdbfe38aa32b7acb02784df4ba9c6708b +Author: Christoph Berg +Date: Wed Jul 8 16:34:33 2026 +0200 + + Reject substituting extension schemas or owners matching ["$'\]. + + The extwlist.custom_path script mechanism was vulnerable to SQL + injection via crafted schema and user names. The same problem was fixed + in August 2023 in PostgreSQL (cd5f2a3570), and is now fixed in + pgextwlist as well. Substitutions that attempt to insert any of "$'\ are + now simply rejected. (We don't try to quote the values as we don't know + which quoting context we are in.) + + Reported by Guillaume Winter. + + Security: CVE-2023-39417 + +--- a/Makefile ++++ b/Makefile +@@ -4,7 +4,7 @@ long_ver = $(shell (git describe --tags + MODULE_big = pgextwlist + OBJS = utils.o pgextwlist.o + DOCS = README.md +-REGRESS = setup pgextwlist errors crossuser hooks ++REGRESS = setup pgextwlist errors crossuser hooks substitution_safety + RPM_MINOR_VERSION_SUFFIX ?= + + PG_CONFIG = pg_config +--- /dev/null ++++ b/expected/substitution_safety.out +@@ -0,0 +1,56 @@ ++-- CVE-2023-39417 mitigation: @-substitution values containing characters ++-- that could enable SQL injection (" $ ' \) must be rejected before the ++-- replacement is inserted into the script. Verify each forbidden char. ++-- pg_stat_statements is whitelisted in setup.sql and has a custom after-create ++-- script under test-scripts/, so CREATE EXTENSION always reaches ++-- execute_custom_script and the substitution check. ++-- 1. Single quote in schema name. ++CREATE SCHEMA "weird'name"; ++SET ROLE mere_mortal; ++CREATE EXTENSION pg_stat_statements WITH SCHEMA "weird'name"; ++ERROR: invalid value for "@extschema@" substitution ++DETAIL: Value must not contain any of: "$'\ ++HINT: Rename the object referenced by @extschema@. ++RESET ROLE; ++DROP SCHEMA "weird'name"; ++-- 2. Dollar sign in schema name. ++CREATE SCHEMA "weird$name"; ++SET ROLE mere_mortal; ++CREATE EXTENSION pg_stat_statements WITH SCHEMA "weird$name"; ++ERROR: invalid value for "@extschema@" substitution ++DETAIL: Value must not contain any of: "$'\ ++HINT: Rename the object referenced by @extschema@. ++RESET ROLE; ++DROP SCHEMA "weird$name"; ++-- 3. Backslash in schema name. ++CREATE SCHEMA "weird\name"; ++SET ROLE mere_mortal; ++CREATE EXTENSION pg_stat_statements WITH SCHEMA "weird\name"; ++ERROR: invalid value for "@extschema@" substitution ++DETAIL: Value must not contain any of: "$'\ ++HINT: Rename the object referenced by @extschema@. ++RESET ROLE; ++DROP SCHEMA "weird\name"; ++-- 4. Double quote in schema name (embedded via SQL "" doubling). ++CREATE SCHEMA "weird""name"; ++SET ROLE mere_mortal; ++CREATE EXTENSION pg_stat_statements WITH SCHEMA "weird""name"; ++ERROR: invalid value for "@extschema@" substitution ++DETAIL: Value must not contain any of: "$'\ ++HINT: Rename the object referenced by @extschema@. ++RESET ROLE; ++DROP SCHEMA "weird""name"; ++-- Sanity: a plain schema name should not trip the CVE check. Clean up any ++-- leftovers from earlier regressions in the same cluster first so the ++-- after-create script can run cleanly. ++DROP EXTENSION IF EXISTS pg_stat_statements; ++NOTICE: extension "pg_stat_statements" does not exist, skipping ++DROP ROLE IF EXISTS stat_resetters; ++NOTICE: role "stat_resetters" does not exist, skipping ++CREATE SCHEMA ok_schema; ++SET ROLE mere_mortal; ++CREATE EXTENSION pg_stat_statements WITH SCHEMA ok_schema; ++RESET ROLE; ++DROP EXTENSION pg_stat_statements; ++DROP ROLE stat_resetters; ++DROP SCHEMA ok_schema; +--- /dev/null ++++ b/sql/substitution_safety.sql +@@ -0,0 +1,48 @@ ++-- CVE-2023-39417 mitigation: @-substitution values containing characters ++-- that could enable SQL injection (" $ ' \) must be rejected before the ++-- replacement is inserted into the script. Verify each forbidden char. ++ ++-- pg_stat_statements is whitelisted in setup.sql and has a custom after-create ++-- script under test-scripts/, so CREATE EXTENSION always reaches ++-- execute_custom_script and the substitution check. ++ ++-- 1. Single quote in schema name. ++CREATE SCHEMA "weird'name"; ++SET ROLE mere_mortal; ++CREATE EXTENSION pg_stat_statements WITH SCHEMA "weird'name"; ++RESET ROLE; ++DROP SCHEMA "weird'name"; ++ ++-- 2. Dollar sign in schema name. ++CREATE SCHEMA "weird$name"; ++SET ROLE mere_mortal; ++CREATE EXTENSION pg_stat_statements WITH SCHEMA "weird$name"; ++RESET ROLE; ++DROP SCHEMA "weird$name"; ++ ++-- 3. Backslash in schema name. ++CREATE SCHEMA "weird\name"; ++SET ROLE mere_mortal; ++CREATE EXTENSION pg_stat_statements WITH SCHEMA "weird\name"; ++RESET ROLE; ++DROP SCHEMA "weird\name"; ++ ++-- 4. Double quote in schema name (embedded via SQL "" doubling). ++CREATE SCHEMA "weird""name"; ++SET ROLE mere_mortal; ++CREATE EXTENSION pg_stat_statements WITH SCHEMA "weird""name"; ++RESET ROLE; ++DROP SCHEMA "weird""name"; ++ ++-- Sanity: a plain schema name should not trip the CVE check. Clean up any ++-- leftovers from earlier regressions in the same cluster first so the ++-- after-create script can run cleanly. ++DROP EXTENSION IF EXISTS pg_stat_statements; ++DROP ROLE IF EXISTS stat_resetters; ++CREATE SCHEMA ok_schema; ++SET ROLE mere_mortal; ++CREATE EXTENSION pg_stat_statements WITH SCHEMA ok_schema; ++RESET ROLE; ++DROP EXTENSION pg_stat_statements; ++DROP ROLE stat_resetters; ++DROP SCHEMA ok_schema; +--- a/utils.c ++++ b/utils.c +@@ -539,6 +539,34 @@ get_current_database_owner_name() + } + + /* ++ * Reject @-substitution values containing characters that could be used to ++ * break out of quoting in the resulting script (CVE-2023-39417; upstream ++ * fix cd5f2a3570). The check is applied to the raw, unquoted name because ++ * quote_identifier() output always contains '"', which would trip the ++ * check spuriously. ++ */ ++static void ++check_substitution_value(const char *placeholder, const char *value) ++{ ++ static const char forbidden[] = "\"$'\\"; ++ ++ if (value == NULL) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), ++ errmsg("invalid value for \"%s\" substitution", ++ placeholder))); ++ ++ if (strpbrk(value, forbidden) != NULL) ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), ++ errmsg("invalid value for \"%s\" substitution", ++ placeholder), ++ errdetail("Value must not contain any of: %s", forbidden), ++ errhint("Rename the object referenced by %s.", ++ placeholder))); ++} ++ ++/* + * Execute given script + */ + void +@@ -546,7 +574,6 @@ execute_custom_script(const char *filena + { + int save_nestlevel; + StringInfoData pathbuf; +- const char *qSchemaName = quote_identifier(schemaName); + + elog(DEBUG1, "Executing custom script \"%s\"", filename); + +@@ -631,35 +658,40 @@ execute_custom_script(const char *filena + /* + * substitute the target schema name for occurrences of @extschema@. + */ ++ check_substitution_value("@extschema@", schemaName); + t_sql = DirectFunctionCall3Coll(replace_text, + C_COLLATION_OID, + t_sql, + CStringGetTextDatum("@extschema@"), +- CStringGetTextDatum(qSchemaName)); ++ CStringGetTextDatum(schemaName)); + + /* + * substitute the current user name for occurrences of @current_user@ + */ +- t_sql = DirectFunctionCall3Coll(replace_text, +- C_COLLATION_OID, +- t_sql, +- CStringGetTextDatum("@current_user@"), +- CStringGetTextDatum( +- GetUserNameFromId(GetUserId() +-#if PG_MAJOR_VERSION >= 905 +- , false +-#endif +- ))); ++ { ++ char *cur_user = GetUserNameFromId(GetUserId(), false); ++ ++ check_substitution_value("@current_user@", cur_user); ++ t_sql = DirectFunctionCall3Coll(replace_text, ++ C_COLLATION_OID, ++ t_sql, ++ CStringGetTextDatum("@current_user@"), ++ CStringGetTextDatum(cur_user)); ++ } + + /* + * substitute the database owner for occurrences of @database_owner@ + */ +- t_sql = DirectFunctionCall3Coll(replace_text, +- C_COLLATION_OID, +- t_sql, +- CStringGetTextDatum("@database_owner@"), +- CStringGetTextDatum( +- get_current_database_owner_name())); ++ { ++ char *db_owner = get_current_database_owner_name(); ++ ++ check_substitution_value("@database_owner@", db_owner); ++ t_sql = DirectFunctionCall3Coll(replace_text, ++ C_COLLATION_OID, ++ t_sql, ++ CStringGetTextDatum("@database_owner@"), ++ CStringGetTextDatum(db_owner)); ++ } + + /* And now back to C string */ + c_sql = text_to_cstring(DatumGetTextPP(t_sql)); diff -Nru pgextwlist-1.19/debian/patches/series pgextwlist-1.19/debian/patches/series --- pgextwlist-1.19/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 +++ pgextwlist-1.19/debian/patches/series 2026-07-08 16:09:07.000000000 +0000 @@ -0,0 +1 @@ +0ae3d2dfdbfe38aa32b7acb02784df4ba9c6708b