mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Fix error in windows provider when the environment has accidental duplicates that differ only by case (#6489)
Fix error in windows provider when the environment has accidental duplicates that differ only by case. Make the provider storage for the environment on windows ignore duplicates and only report the effective value. Add tests to verify existing environment get-item behavior and to ensure that Get-Item env:<var> reports the same as $env:<var>, namely the effective value. Fixes #6305 and supersedes #6320 based on discussion in #6460.
This commit is contained in:
committed by
Travis Plunk
parent
7c7204a1ba
commit
87834d07b4
@@ -198,15 +198,29 @@ namespace Microsoft.PowerShell.Commands
|
||||
IDictionary environmentTable = Environment.GetEnvironmentVariables();
|
||||
foreach (DictionaryEntry entry in environmentTable)
|
||||
{
|
||||
// Windows only: duplicate key (variable name that differs only in case)
|
||||
// NOTE: Even though this shouldn't happen, it can, e.g. when npm
|
||||
// creates duplicate environment variables that differ only in case -
|
||||
// see https://github.com/PowerShell/PowerShell/issues/6305.
|
||||
// However, because retrieval *by name* later is invariably
|
||||
// case-Insensitive, in effect only a *single* variable exists.
|
||||
// We simply ask Environment.GetEnvironmentVariable() which value is
|
||||
// the effective one, and use that.
|
||||
providerTable.TryAdd((string)entry.Key, entry);
|
||||
if (!providerTable.TryAdd((string)entry.Key, entry))
|
||||
{ // Windows only: duplicate key (variable name that differs only in case)
|
||||
// NOTE: Even though this shouldn't happen, it can, e.g. when npm
|
||||
// creates duplicate environment variables that differ only in case -
|
||||
// see https://github.com/PowerShell/PowerShell/issues/6305.
|
||||
// However, because retrieval *by name* later is invariably
|
||||
// case-INsensitive, in effect only a *single* variable exists.
|
||||
// We simply ask Environment.GetEnvironmentVariable() for the effective value
|
||||
// and use that as the only entry, because for a given key 'foo' (and all its case variations),
|
||||
// that is guaranteed to match what $env:FOO and [environment]::GetEnvironmentVariable('foo') return.
|
||||
// (If, by contrast, we just used `entry` as-is every time a duplicate is encountered,
|
||||
// it could - intermittently - represent a value *other* than the effective one.)
|
||||
string effectiveValue = Environment.GetEnvironmentVariable((string)entry.Key);
|
||||
if (((string)entry.Value).Equals(effectiveValue, StringComparison.Ordinal)) { // We've found the effective definition.
|
||||
// Note: We *recreate* the entry so that the specific name casing of the
|
||||
// effective definition is also reflected. However, if the case variants
|
||||
// define the same value, it is unspecified which name variant is reflected
|
||||
// in Get-Item env: output; given the always case-insensitive nature of the retrieval,
|
||||
// that shouldn't matter.
|
||||
providerTable.Remove((string)entry.Key);
|
||||
providerTable.Add((string)entry.Key, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return providerTable;
|
||||
|
||||
@@ -143,3 +143,29 @@ Describe "Get-Item" -Tags "CI" {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Get-Item environment provider on Windows with accidental case-variant duplicates" -Tags "Scenario" {
|
||||
BeforeAll {
|
||||
$env:testVar = 'a' # Note: Even though PSScriptAnalyzer can't detect it, this variable *is* used below, namely via Node.js.
|
||||
}
|
||||
AfterAll {
|
||||
$env:testVar = $null
|
||||
}
|
||||
It "Reports the effective value among accidental case-variant duplicates on Windows" -skip:$skipNotWindows {
|
||||
if (-not (Get-Command -ErrorAction Ignore node.exe)) {
|
||||
Write-Warning "Test skipped, because prerequisite Node.js is not installed."
|
||||
} else {
|
||||
$valDirect, $valGetItem, $unused = node.exe -pe @"
|
||||
env = {}
|
||||
env.testVar = process.env.testVar // include the original case variant with its original value.
|
||||
env.TESTVAR = 'b' // redefine with a case variant name and different value
|
||||
// Note: Which value will win is not deterministic(!); what matters, however, is that both
|
||||
// $env:testvar and Get-Item env:testvar report the same value.
|
||||
// The nondeterministic behavior makes it hard to prove that the values are *always* the
|
||||
// same, however.
|
||||
require('child_process').execSync(\"\\\"$($PSHOME -replace '\\', '/')/pwsh.exe\\\" -noprofile -command `$env:testvar, (Get-Item env:testvar).Value\", { env: env }).toString()
|
||||
"@
|
||||
$valGetItem | Should -BeExactly $valDirect
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user