From 02f16014c3458e549719aaaaf4bb6c338e55b6e2 Mon Sep 17 00:00:00 2001
From: Greg Stark <stark@mit.edu>
Date: Thu, 16 Jun 2022 01:40:49 -0400
Subject: [PATCH 2/2] Instead of checks actually force search_path on immutable
 and security definer functions

---
 src/backend/commands/functioncmds.c | 79 ++++++++++++++++++++++++++++-
 1 file changed, 78 insertions(+), 1 deletion(-)

diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 9347ed70e2..20da751202 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -41,6 +41,7 @@
 #include "catalog/indexing.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_aggregate.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_cast.h"
 #include "catalog/pg_language.h"
 #include "catalog/pg_namespace.h"
@@ -683,9 +684,83 @@ update_proconfig_value(ArrayType *a, List *set_items)
  * https://www.postgresql.org/docs/current/sql-createfunction.html#SQL-CREATEFUNCTION-SECURITY
  */ 
 
-/* XXX This logic should perhaps be moved to namespace.c XXX */
+/* XXX Some or all of this logic should perhaps be moved to namespace.c XXX */
 #include "utils/varlena.h"
 
+static ArrayType *
+fixup_proconfig_value(ArrayType *a, char provolatile, bool prosecdef) {
+	char *search_path = NULL;
+
+	List	   *namelist;
+	ListCell   *l;
+	StringInfoData string;
+
+	if (provolatile != PROVOLATILE_IMMUTABLE && !prosecdef) {
+		return a;
+	}
+	if (a != NULL) {
+		search_path = GUCArrayLookup(a, "search_path");
+	}
+	if (!search_path) {
+		search_path = pstrdup(namespace_search_path);
+	}
+
+	/* Parse string into list of identifiers
+	 * GUCArrayLookup returns a pstrdup'd string so this is safe
+	 */
+	if (!SplitIdentifierString(search_path, ',', &namelist))
+	{
+		/* syntax error in name list */
+		elog(ERROR, "invalid list syntax in search_path setting on function");
+	}
+
+	initStringInfo(&string);
+	appendStringInfoString(&string, "pg_catalog");
+	foreach(l, namelist)
+	{
+		char	   *curname = (char *) lfirst(l);
+
+		if (strcmp(curname, "pg_catalog") == 0)
+		{
+			if (foreach_current_index(l) != 0)
+				elog(WARNING, "moving pg_catalog to first position in search_path for IMMUTABLE or SECURITY DEFINER function");
+			continue;
+		}
+		if (strcmp(curname, "pg_temp") == 0)
+		{
+			if (foreach_current_index(l) != namelist->length-1)
+				elog(WARNING, "moving pg_temp to last position in search_path for IMMUTABLE or SECURITY DEFINER function");
+			continue;
+		}
+		if (strcmp(curname, "$user") == 0)
+		{
+			/* $user --- substitute namespace matching user name, if any */
+			HeapTuple	tuple;
+
+			tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(GetUserId()));
+			if (HeapTupleIsValid(tuple))
+			{
+				curname = NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname);
+				ReleaseSysCache(tuple);
+				/*elog(WARNING, "IMMUTABLE or SECURITY DEFINER functions should not have a search path including $user");*/
+			} else {
+				/* No great options here */
+				ReleaseSysCache(tuple);
+				elog(WARNING, "removing invalid $user from search_path on IMMUTABLE or SECURITY DEFINER function");
+				continue;
+			}
+		}
+		appendStringInfoChar(&string, ',');
+		appendStringInfoString(&string, quote_identifier(curname));
+	}
+	appendStringInfoChar(&string, ',');
+	appendStringInfoString(&string, "pg_temp");
+
+	a = GUCArrayAdd(a, "search_path", string.data);
+	return a;
+}
+	
+
 static void
 verify_proconfig_value(ArrayType *a, char provolatile, bool prosecdef) {
 	char *proconfig_search_path = NULL;
@@ -1233,6 +1308,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
 		}
 	}
 
+	proconfig = fixup_proconfig_value(proconfig, volatility, security);
 	verify_proconfig_value(proconfig, volatility, security);
 
 	/*
@@ -1564,6 +1640,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 		/* update according to each SET or RESET item, left to right */
 		a = update_proconfig_value(a, set_items);
 
+		a = fixup_proconfig_value(a, procForm->provolatile, procForm->prosecdef);
 		verify_proconfig_value(a, procForm->provolatile, procForm->prosecdef);
 
 		/* update the tuple */
-- 
2.36.1

