v09_0001_don-t-allow-newline-or-carriage-return-in-db-user-role-names.patch

text/x-patch

Filename: v09_0001_don-t-allow-newline-or-carriage-return-in-db-user-role-names.patch
Type: text/x-patch
Part: 0
Message: Re: getting "shell command argument contains a newline or carriage return:" error with pg_dumpall when db name have new line in double quote

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: format-patch
Series: patch v9-0001
Subject: don't allow newline or carriage return character in name for database/user/role/tablespace
File+
src/backend/commands/dbcommands.c 12 0
src/backend/commands/tablespace.c 12 0
src/backend/commands/user.c 12 0
src/bin/pg_dump/t/010_dump_connstr.pl 0 14
src/bin/scripts/t/020_createdb.pl 12 0
src/fe_utils/string_utils.c 0 6
src/include/commands/defrem.h 1 0
src/test/modules/unsafe_tests/expected/alter_system_table.out 5 0
src/test/modules/unsafe_tests/expected/rolenames.out 4 0
src/test/modules/unsafe_tests/sql/alter_system_table.sql 4 0
src/test/modules/unsafe_tests/sql/rolenames.sql 2 0
src/test/regress/expected/tablespace.out 5 0
src/test/regress/sql/tablespace.sql 4 0
From 29c2da33a521094d9969d2cacd3ef4152f5da933 Mon Sep 17 00:00:00 2001
From: Mahendra Singh Thalor <mahi6run@gmail.com>
Date: Wed, 4 Feb 2026 00:12:48 +0530
Subject: [PATCH 1/2] don't allow newline or carriage return character in name 
 for  database/user/role/tablespace

While creating database, if database name has any newline or carriage
return character in name, then through error becuase these special
character are not allowed in dbname when dump command is executed.

do same for "CREATE ROLE", "CREATE USER" also.

This will add check for:
"CREATE DATABASE", "CREATE ROLE", "CREATE USER", "CREATE TABLESPACE"
"RENAME DATABASE/USER/ROLE/TABLESPACE"

-------------------------
As we will not allow these \n\r in names, then we will never get these
names in dump also.

If we are dumping from older branch, then we will fail with same old error.
(dump will fail in older branches so no need to add extra handling for dump.)

Also remove comment added by 142c24c23447f212e642a0ffac9af878b93f490d commit.

Remove one test of 8b845520fb0aa50fea7aae44a45cee1b6d87845d commit.
---
 src/backend/commands/dbcommands.c                  | 12 ++++++++++++
 src/backend/commands/tablespace.c                  | 12 ++++++++++++
 src/backend/commands/user.c                        | 12 ++++++++++++
 src/bin/pg_dump/t/010_dump_connstr.pl              | 14 --------------
 src/bin/scripts/t/020_createdb.pl                  | 12 ++++++++++++
 src/fe_utils/string_utils.c                        |  6 ------
 src/include/commands/defrem.h                      |  1 +
 .../unsafe_tests/expected/alter_system_table.out   |  5 +++++
 .../modules/unsafe_tests/expected/rolenames.out    |  4 ++++
 .../unsafe_tests/sql/alter_system_table.sql        |  4 ++++
 src/test/modules/unsafe_tests/sql/rolenames.sql    |  2 ++
 src/test/regress/expected/tablespace.out           |  5 +++++
 src/test/regress/sql/tablespace.sql                |  4 ++++
 13 files changed, 73 insertions(+), 20 deletions(-)
 mode change 100644 => 100755 src/bin/pg_dump/t/010_dump_connstr.pl
 mode change 100644 => 100755 src/bin/scripts/t/020_createdb.pl

diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 87949054f26..6396d3e7caf 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -742,6 +742,12 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
 	CreateDBStrategy dbstrategy = CREATEDB_WAL_LOG;
 	createdb_failure_params fparms;
 
+	/* Report error if name has \n or \r character. */
+	if (strpbrk(dbname, "\n\r"))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE)),
+				errmsg("database name \"%s\" contains a newline or carriage return character",dbname));
+
 	/* Extract options from the statement node tree */
 	foreach(option, stmt->options)
 	{
@@ -1910,6 +1916,12 @@ RenameDatabase(const char *oldname, const char *newname)
 	int			npreparedxacts;
 	ObjectAddress address;
 
+	/* Report error if name has \n or \r character. */
+	if (strpbrk(newname, "\n\r"))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE)),
+				errmsg("database name \"%s\" contains a newline or carriage return character",newname));
+
 	/*
 	 * Look up the target database's OID, and get exclusive lock on it. We
 	 * need this for the same reasons as DROP DATABASE.
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 0b064891932..f6dba30da55 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -241,6 +241,12 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
 				(errcode(ERRCODE_INVALID_NAME),
 				 errmsg("tablespace location cannot contain single quotes")));
 
+	/* Report error if name has \n or \r character. */
+	if (strpbrk(stmt->tablespacename, "\n\r"))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE)),
+				errmsg("tablespace name \"%s\" contains a newline or carriage return character",stmt->tablespacename));
+
 	in_place = allow_in_place_tablespaces && strlen(location) == 0;
 
 	/*
@@ -970,6 +976,12 @@ RenameTableSpace(const char *oldname, const char *newname)
 				 errmsg("unacceptable tablespace name \"%s\"", newname),
 				 errdetail("The prefix \"pg_\" is reserved for system tablespaces.")));
 
+	/* Report error if name has \n or \r character. */
+	if (strpbrk(newname, "\n\r"))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE)),
+				errmsg("tablespace name \"%s\" contains a newline or carriage return character",newname));
+
 	/*
 	 * If built with appropriate switch, whine when regression-testing
 	 * conventions for tablespace names are violated.
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 8fb9ea25db0..5b9d6634e87 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -171,6 +171,12 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
 	DefElem    *dbypassRLS = NULL;
 	GrantRoleOptions popt;
 
+	/* Report error if name has \n or \r character. */
+	if (strpbrk(stmt->role, "\n\r"))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE)),
+				errmsg("role name \"%s\" contains a newline or carriage return character",stmt->role));
+
 	/* The defaults can vary depending on the original statement type */
 	switch (stmt->stmt_type)
 	{
@@ -1348,6 +1354,12 @@ RenameRole(const char *oldname, const char *newname)
 	ObjectAddress address;
 	Form_pg_authid authform;
 
+	/* Report error if name has \n or \r character. */
+	if (strpbrk(newname, "\n\r"))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE)),
+				errmsg("role name \"%s\" contains a newline or carriage return character",newname));
+
 	rel = table_open(AuthIdRelationId, RowExclusiveLock);
 	dsc = RelationGetDescr(rel);
 
diff --git a/src/bin/pg_dump/t/010_dump_connstr.pl b/src/bin/pg_dump/t/010_dump_connstr.pl
old mode 100644
new mode 100755
index dc7a33658db..bf2c3b6d00b
--- a/src/bin/pg_dump/t/010_dump_connstr.pl
+++ b/src/bin/pg_dump/t/010_dump_connstr.pl
@@ -153,20 +153,6 @@ $node->command_ok(
 	],
 	'pg_dumpall --dbname accepts connection string');
 
-$node->run_log(
-	[ 'createdb', '--username' => $src_bootstrap_super, "foo\n\rbar" ]);
-
-# not sufficient to use --roles-only here
-$node->command_fails(
-	[
-		'pg_dumpall', '--no-sync',
-		'--username' => $src_bootstrap_super,
-		'--file' => $discard,
-	],
-	'pg_dumpall with \n\r in database name');
-$node->run_log(
-	[ 'dropdb', '--username' => $src_bootstrap_super, "foo\n\rbar" ]);
-
 
 # make a table, so the parallel worker has something to dump
 $node->safe_psql(
diff --git a/src/bin/scripts/t/020_createdb.pl b/src/bin/scripts/t/020_createdb.pl
old mode 100644
new mode 100755
index 83b0077383a..a0995868363
--- a/src/bin/scripts/t/020_createdb.pl
+++ b/src/bin/scripts/t/020_createdb.pl
@@ -241,6 +241,18 @@ $node->command_fails(
 	],
 	'fails for invalid locale provider');
 
+$node->command_fails_like(
+    [ 'createdb', "invalid \n dbname" ],
+    qr(contains a newline or carriage return character),
+    'fails if database name contains a newline character in name'
+);
+
+$node->command_fails_like(
+    [ 'createdb', "invalid \r dbname" ],
+    qr(contains a newline or carriage return character),
+    'fails if database name contains a carriage return character in name'
+);
+
 # Check use of templates with shared dependencies copied from the template.
 my ($ret, $stdout, $stderr) = $node->psql(
 	'foobar2',
diff --git a/src/fe_utils/string_utils.c b/src/fe_utils/string_utils.c
index 89ce396ed4f..38fffbd036b 100644
--- a/src/fe_utils/string_utils.c
+++ b/src/fe_utils/string_utils.c
@@ -568,12 +568,6 @@ appendByteaLiteral(PQExpBuffer buf, const unsigned char *str, size_t length,
  * Append the given string to the shell command being built in the buffer,
  * with shell-style quoting as needed to create exactly one argument.
  *
- * Forbid LF or CR characters, which have scant practical use beyond designing
- * security breaches.  The Windows command shell is unusable as a conduit for
- * arguments containing LF or CR characters.  A future major release should
- * reject those characters in CREATE ROLE and CREATE DATABASE, because use
- * there eventually leads to errors here.
- *
  * appendShellString() simply prints an error and dies if LF or CR appears.
  * appendShellStringNoError() omits those characters from the result, and
  * returns false if there were any.
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 8f4a2d9bbc1..bd930b8905d 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -162,5 +162,6 @@ extern TypeName *defGetTypeName(DefElem *def);
 extern int	defGetTypeLength(DefElem *def);
 extern List *defGetStringList(DefElem *def);
 pg_noreturn extern void errorConflictingDefElem(DefElem *defel, ParseState *pstate);
+extern void reject_newline_in_name(const char *objname, const char *objtype);
 
 #endif							/* DEFREM_H */
diff --git a/src/test/modules/unsafe_tests/expected/alter_system_table.out b/src/test/modules/unsafe_tests/expected/alter_system_table.out
index b73b9442b8d..39c90ce30a7 100644
--- a/src/test/modules/unsafe_tests/expected/alter_system_table.out
+++ b/src/test/modules/unsafe_tests/expected/alter_system_table.out
@@ -64,6 +64,11 @@ ERROR:  permission denied: "pg_description" is a system catalog
 CREATE TABLESPACE pg_foo LOCATION '/no/such/location';
 ERROR:  unacceptable tablespace name "pg_foo"
 DETAIL:  The prefix "pg_" is reserved for system tablespaces.
+-- contains \n\r tablespace name
+CREATE TABLESPACE "inavlid
+name" LOCATION '/no/such/location';
+ERROR:  tablespace name "inavlid
+name" contains a newline or carriage return character
 -- triggers
 CREATE FUNCTION tf1() RETURNS trigger
 LANGUAGE plpgsql
diff --git a/src/test/modules/unsafe_tests/expected/rolenames.out b/src/test/modules/unsafe_tests/expected/rolenames.out
index 61396b2a805..9dfb8475e16 100644
--- a/src/test/modules/unsafe_tests/expected/rolenames.out
+++ b/src/test/modules/unsafe_tests/expected/rolenames.out
@@ -102,6 +102,10 @@ DETAIL:  Role names starting with "pg_" are reserved.
 CREATE ROLE "pg_abcdef"; -- error
 ERROR:  role name "pg_abcdef" is reserved
 DETAIL:  Role names starting with "pg_" are reserved.
+CREATE ROLE "invalid
+rolename"; -- error
+ERROR:  role name "invalid
+rolename" contains a newline or carriage return character
 CREATE ROLE regress_testrol0 SUPERUSER LOGIN;
 CREATE ROLE regress_testrolx SUPERUSER LOGIN;
 CREATE ROLE regress_testrol2 SUPERUSER;
diff --git a/src/test/modules/unsafe_tests/sql/alter_system_table.sql b/src/test/modules/unsafe_tests/sql/alter_system_table.sql
index c1515100845..9812115a6f0 100644
--- a/src/test/modules/unsafe_tests/sql/alter_system_table.sql
+++ b/src/test/modules/unsafe_tests/sql/alter_system_table.sql
@@ -64,6 +64,10 @@ ALTER TABLE pg_description SET SCHEMA public;
 -- reserved tablespace name
 CREATE TABLESPACE pg_foo LOCATION '/no/such/location';
 
+-- contains \n\r tablespace name
+CREATE TABLESPACE "inavlid
+name" LOCATION '/no/such/location';
+
 -- triggers
 CREATE FUNCTION tf1() RETURNS trigger
 LANGUAGE plpgsql
diff --git a/src/test/modules/unsafe_tests/sql/rolenames.sql b/src/test/modules/unsafe_tests/sql/rolenames.sql
index adac36536db..70b342abeb6 100644
--- a/src/test/modules/unsafe_tests/sql/rolenames.sql
+++ b/src/test/modules/unsafe_tests/sql/rolenames.sql
@@ -75,6 +75,8 @@ CREATE ROLE pg_abc; -- error
 CREATE ROLE "pg_abc"; -- error
 CREATE ROLE pg_abcdef; -- error
 CREATE ROLE "pg_abcdef"; -- error
+CREATE ROLE "invalid
+rolename"; -- error
 
 CREATE ROLE regress_testrol0 SUPERUSER LOGIN;
 CREATE ROLE regress_testrolx SUPERUSER LOGIN;
diff --git a/src/test/regress/expected/tablespace.out b/src/test/regress/expected/tablespace.out
index a90e39e5738..185880a3217 100644
--- a/src/test/regress/expected/tablespace.out
+++ b/src/test/regress/expected/tablespace.out
@@ -958,6 +958,11 @@ ALTER TABLE ALL IN TABLESPACE regress_tblspace_renamed SET TABLESPACE pg_default
 NOTICE:  no matching relations in tablespace "regress_tblspace_renamed" found
 ALTER MATERIALIZED VIEW ALL IN TABLESPACE regress_tblspace_renamed SET TABLESPACE pg_default;
 NOTICE:  no matching relations in tablespace "regress_tblspace_renamed" found
+-- Should fail, contains \n in name
+ALTER TABLESPACE regress_tblspace_renamed RENAME TO "invalid
+name";
+ERROR:  tablespace name "invalid
+name" contains a newline or carriage return character
 -- Should succeed
 DROP TABLESPACE regress_tblspace_renamed;
 DROP SCHEMA testschema CASCADE;
diff --git a/src/test/regress/sql/tablespace.sql b/src/test/regress/sql/tablespace.sql
index dfe3db096e2..c43a59e5957 100644
--- a/src/test/regress/sql/tablespace.sql
+++ b/src/test/regress/sql/tablespace.sql
@@ -430,6 +430,10 @@ ALTER MATERIALIZED VIEW ALL IN TABLESPACE regress_tblspace_renamed SET TABLESPAC
 ALTER TABLE ALL IN TABLESPACE regress_tblspace_renamed SET TABLESPACE pg_default;
 ALTER MATERIALIZED VIEW ALL IN TABLESPACE regress_tblspace_renamed SET TABLESPACE pg_default;
 
+-- Should fail, contains \n in name
+ALTER TABLESPACE regress_tblspace_renamed RENAME TO "invalid
+name";
+
 -- Should succeed
 DROP TABLESPACE regress_tblspace_renamed;
 
-- 
2.52.0