From 37ca21233d19b182b4d2d9a6725cedd9d57c1cbb Mon Sep 17 00:00:00 2001 From: Dominik Viererbe Date: Fri, 11 Jul 2025 16:28:08 +0300 Subject: [PATCH] fix: non-writable global user profile #14 Summary ======= PowerShell should search in a dedicated directory (other than the installation directory) for the all-users profile to adhere to the Filesystem Hierarchy Standard [FHS]. Context ======= Currently the all-users profiles are stored in the installation directory. Unix-based systems and derivatives mostly follow recommendations of the FHS. The FHS recommends to place configuration files into the `/etc` directory. For example, here are the locations of the system wide configuration files of other popular shells: - bash(1): `/etc/bash.bashrc` (Debian/Ubuntu) or `/etc/bashrc` (RedHat/Fedora) - zsh(1): `/etc/zshrc` - fish(1): `/etc/fish/config.fish` I took inspiration from where NuGet is locating its system-wide configuration, see [NuGet config file locations]: This commit introduces the environment variable `POWERSHELL_COMMON_APPLICATION_DATA`. If this variable is neither null nor empty, then this location is used; otherwise PowerShell uses `/etc/opt/powershell`. [FHS]: https://refspecs.linuxfoundation.org/FHS_3.0/fhs/index.html [NuGet config file locations]: https://learn.microsoft.com/en-us/nuget/consume-packages/configuring-nuget-behavior#config-file-locations-and-uses Fixes: #14 Signed-off-by: Dominik Viererbe --- .../global-profiles-stores-in-etc.patch | 24 +++++++++++++++++++ .../global-profiles-stores-in-etc.patch | 24 +++++++++++++++++++ .../global-profiles-stores-in-etc.patch | 24 +++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 powershell-lts/patches/global-profiles-stores-in-etc.patch create mode 100644 powershell-preview/patches/global-profiles-stores-in-etc.patch create mode 100644 powershell-stable/patches/global-profiles-stores-in-etc.patch diff --git a/powershell-lts/patches/global-profiles-stores-in-etc.patch b/powershell-lts/patches/global-profiles-stores-in-etc.patch new file mode 100644 index 0000000..08d325a --- /dev/null +++ b/powershell-lts/patches/global-profiles-stores-in-etc.patch @@ -0,0 +1,24 @@ +diff --git a/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs b/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs +index 439826984..d08d21f86 100644 +--- a/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs ++++ b/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs +@@ -255,16 +255,9 @@ namespace System.Management.Automation + /// The base path for all users profiles. + private static string GetAllUsersFolderPath(string shellId) + { +- string folderPath = string.Empty; +- try +- { +- folderPath = Utils.GetApplicationBase(shellId); +- } +- catch (System.Security.SecurityException) +- { +- } +- +- return folderPath; ++ string folderPath = Environment.GetEnvironmentVariable("POWERSHELL_COMMON_APPLICATION_DATA"); ++ if (!string.IsNullOrEmpty(folderPath)) return folderPath; ++ return "/etc/opt/powershell"; + } + #endregion GetProfileCommands + diff --git a/powershell-preview/patches/global-profiles-stores-in-etc.patch b/powershell-preview/patches/global-profiles-stores-in-etc.patch new file mode 100644 index 0000000..88ce256 --- /dev/null +++ b/powershell-preview/patches/global-profiles-stores-in-etc.patch @@ -0,0 +1,24 @@ +diff --git a/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs b/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs +index 003625791..18dbda34f 100644 +--- a/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs ++++ b/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs +@@ -233,16 +233,9 @@ namespace System.Management.Automation + /// The base path for all users profiles. + private static string GetAllUsersFolderPath(string shellId) + { +- string folderPath = string.Empty; +- try +- { +- folderPath = Utils.GetApplicationBase(shellId); +- } +- catch (System.Security.SecurityException) +- { +- } +- +- return folderPath; ++ string folderPath = Environment.GetEnvironmentVariable("POWERSHELL_COMMON_APPLICATION_DATA"); ++ if (!string.IsNullOrEmpty(folderPath)) return folderPath; ++ return "/etc/opt/powershell"; + } + #endregion GetProfileCommands + diff --git a/powershell-stable/patches/global-profiles-stores-in-etc.patch b/powershell-stable/patches/global-profiles-stores-in-etc.patch new file mode 100644 index 0000000..88ce256 --- /dev/null +++ b/powershell-stable/patches/global-profiles-stores-in-etc.patch @@ -0,0 +1,24 @@ +diff --git a/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs b/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs +index 003625791..18dbda34f 100644 +--- a/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs ++++ b/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs +@@ -233,16 +233,9 @@ namespace System.Management.Automation + /// The base path for all users profiles. + private static string GetAllUsersFolderPath(string shellId) + { +- string folderPath = string.Empty; +- try +- { +- folderPath = Utils.GetApplicationBase(shellId); +- } +- catch (System.Security.SecurityException) +- { +- } +- +- return folderPath; ++ string folderPath = Environment.GetEnvironmentVariable("POWERSHELL_COMMON_APPLICATION_DATA"); ++ if (!string.IsNullOrEmpty(folderPath)) return folderPath; ++ return "/etc/opt/powershell"; + } + #endregion GetProfileCommands +