When a powershell.config.json file is set, there's no handling of an invalid file. Essentially it breaks the whole PowerShell session, and since PowerShell is broken, you can't use Remove-Item to unbreak it...
Write some invalid JSON to the powershell.config.json.
On Windows this is at ~\Documents\PowerShell\powershell.config.json.
On *nix this is at ~/.config/powershell/powershell.config.json.
Write something like:
banana
Trying to start a new PowerShell session looks like this:
Unhandled Exception: Newtonsoft.Json.JsonReaderException: Unexpected character encountered while parsing value: b. Path '', line 0, position 0.
at Newtonsoft.Json.JsonTextReader.ParseValue()
at Newtonsoft.Json.JsonTextReader.Read()
at Newtonsoft.Json.JsonReader.ReadForType(JsonContract contract, Boolean hasConverter)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonSerializer.Deserialize[T](JsonReader reader)
at System.Management.Automation.Configuration.PowerShellConfig.ReadValueFromFile[T](ConfigScope scope, String key, T defaultValue, Func`4 readImpl)
at System.Management.Automation.Configuration.PowerShellConfig.GetPowerShellPolicies(ConfigScope scope)
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
at System.Management.Automation.Utils.GetPolicySettingFromConfigFile[T](ConfigScope[] preferenceOrder)
at System.Management.Automation.Utils.GetPolicySetting[T](ConfigScope[] preferenceOrder)
at Microsoft.PowerShell.Commands.ModuleCmdletBase.GetModuleLoggingInformation(IEnumerable`1& moduleNames)
at System.Management.Automation.PSSnapInReader.SetSnapInLoggingInformation(PSSnapInInfo psSnapInInfo)
at System.Management.Automation.PSSnapInReader.ReadCoreEngineSnapIn()
at System.Management.Automation.Runspaces.InitialSessionState.ImportCorePSSnapIn()
at System.Management.Automation.Runspaces.InitialSessionState.CreateDefault2()
at Microsoft.PowerShell.UnmanagedPSEntry.Start(String consoleFilePath, String[] args, Int32 argc)
at Microsoft.PowerShell.ManagedPSEntry.Main(String[] args) in C:\PowerShell\src\powershell\Program.cs:line 22
In some scenarios (I haven't worked out which yet) almost random failures seem to occur mid-PowerShell session (i.e. you have a working PowerShell session then create a bad powershell.config.json file). Such as:
> Invoke-Build Test
Invoke-Build : AuthorizationManager check failed.
At line:1 char:1
+ Invoke-Build Test
+ ~~~~~~~~~~~~
+ CategoryInfo : SecurityError: (:) [], PSSecurityException
+ FullyQualifiedErrorId : UnauthorizedAccess
(Deleting the bad powershell.config.json makes this work again)
For this to work as a robust feature, I would expect:
> $PSVersionTable
Name Value
---- -----
PSVersion 6.1.0
PSEdition Core
GitCommitId 6.1.0
OS Microsoft Windows 10.0.18242
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
PowerShell should ignore the config file entirely if there is a problem with it
Maybe better stop until user/administrator fix the file?
Well there's always the possibility of starting PowerShell with an invalid config file. So we have to handle it and the question becomes how. I see a couple of possibilities:
In (1) and (2), the problem is that if PowerShell is the only shell you can use, you've now locked yourself out -- there's nothing you can do, because PowerShell can't be started. It's likely you have another shell or a way to delete the file on the machine, or if you're lucky someone else administers your box. But we have to support all scenarios, and having to fall back to another shell in any case doesn't seem like what we want to tell users.
That leaves (3) and (4), and to me (3) would be like finding a parse error in a PowerShell script and executing until that point anyway -- rather than what we actually do, which is execute nothing and tell the user that there's a problem.
So I think the solution is (4). PowerShell starts as if there were no config file and displays an error message with a full explanation, including:
you've now locked yourself out -- there's nothing you can do, because PowerShell can't be started
If exe or some dll was corrupted we'll be locked too.
I am most worried about security if we allow run without config. If an user accidentally spoils this file and PowerShell starts, then there might be a security hole. /cc @PaulHigin @TravisEz13
Another side. What behavior is for ps1-profiles? Should we follow the rule too?
Ah that is good thinking!
On the other hand, if they have write access to the file, they could also edit it to be a different valid configuration already.
If exe or some dll was corrupted we'll be locked too.
In the pwsh.exe case there鈥檚 not a whole lot we can do.
In the DLL case I would hope for a BadImageFormatException.
Ideally in both cases signing saves us.
Profiles are just ps1 scripts right? I assume they get dot sourced at startup. If they contain a parse-error I鈥檓 guessing we never execute them, print the error and otherwise behave like no profile exists.
If we consider a scenario like _login_ shell I guess that profiles can be critical for security too.
Maybe @mklement0 can clarify what bash behavior is in the scenario.
cc @PaulHigin
@iSazonov:
bash - and presumably other POSIX-like shells - behave the same way as described by @rjmholt with respect to PowerShell profiles (note that we don't have a startup problem per se in this case, because the shell by definition has already launched successfully by the time it attempts to source initialization files).
As for the security concern:
Perhaps switching to NoLanguage language mode if the config file cannot be read is a solution?
That would severely limit what can be done in the session (though it probably doesn't close _all_ security holes), while still allowing calling cmdlets such as Remove-Item or invoking external programs such as an editor to fix the problem.
NoLanguage won't work for an interactive session.

We would need to turn on every security feature, some of which require configuration parameters, such as how to encrypt the script block logging.
I think the best option is to fail as @iSazonov suggests, although the error message should be better.
The main scenario that I'm concerned about is that the admin edits the systemwide configuration and accidentally corrupts it and then a non-admin runs PowerShell. If we then ignore the configuration, we could ignore security setting that were intended to be deployed.
We should also think about non-interactive sessions. In the case an startup error would be invisible and useless - I'd expect that a record would be added in event log. An error message should be better for interactive session like "write error message - sleep 5 sec - abort".
@TravisEz13
Relying on global configuration for security seems like a mistake, though -- I would have expected configuration to enable insecure things. I believe I should not have to create a global configuration to make powershell safe for everyone to use, the default state should be safe.
Can you give examples of things needed in a global config to secure powershell?
Sorry for coming late to this discussion. I think it is Ok to just emit warning if config.json file fails to load. I agree that the config file should not be used to configure security, and in fact we should ensure that the config file cannot affect session security. For example PowerShell provides security guarantees in two ways:
a. When working with system wide application control such as WDAC (formerly DeviceGuard), e.g., ConstrainedfLanguage mode.
b. Remote end point sessions, e.g., LanguageMode, command, provider accessibility.
We need to make sure the config file cannot affect security stance in these cases, such as changing LanguageMode, or changing visibility of functions, cmdlets, providers, etc.
On Linux and macOS, you can only configure ScriptBlock logging and I suspect other security features via the configuration file.
Most helpful comment
NoLanguagewon't work for an interactive session.We would need to turn on every security feature, some of which require configuration parameters, such as how to encrypt the script block logging.
I think the best option is to fail as @iSazonov suggests, although the error message should be better.
The main scenario that I'm concerned about is that the admin edits the systemwide configuration and accidentally corrupts it and then a non-admin runs PowerShell. If we then ignore the configuration, we could ignore security setting that were intended to be deployed.