From 01b1b468e1e1d539d5fcaa7fd7e161f15c01f79f Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Thu, 23 Jan 2025 01:34:55 +0200 Subject: [PATCH] Fix potential erroneous load of assembly This change makes the assembly resolve handler be more strict when checking whether or not to load the YamlDotNet assembly. This should prevent accidentally loading the YamlDotNet assembly as a result of a request coming from another assembly that is looking to load a different assembly. Signed-off-by: Gabriel Adrian Samfira --- powershell-yaml.psm1 | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/powershell-yaml.psm1 b/powershell-yaml.psm1 index 80ce133..9e39a46 100644 --- a/powershell-yaml.psm1 +++ b/powershell-yaml.psm1 @@ -34,7 +34,7 @@ function Invoke-LoadFile { [string]$assemblyPath ) - $global:powershellYamlDotNetAssemblyPath = Join-Path $assemblyPath "YamlDotNet.dll" + $powershellYamlDotNetAssemblyPath = Join-Path $assemblyPath "YamlDotNet.dll" $serializerAssemblyPath = Join-Path $assemblyPath "PowerShellYamlSerializer.dll" $yamlAssembly = [Reflection.Assembly]::LoadFile($powershellYamlDotNetAssemblyPath) $serializerAssembly = [Reflection.Assembly]::LoadFile($serializerAssemblyPath) @@ -42,25 +42,32 @@ function Invoke-LoadFile { if ($PSVersionTable['PSEdition'] -eq 'Core') { # Register the AssemblyResolve event to load dependencies manually. This seems to be needed only on # PowerShell Core. - [System.AppDomain]::CurrentDomain.add_AssemblyResolve({ + $resolver = { param ($snd, $e) + # This event only needs to run once when the Invoke-LoadFile function is called. + # If it's called again, the variables defined in this functions will not be available, + # so we can safely ignore the event. + if (-not $serializerAssemblyPath -or -not $powershellYamlDotNetAssemblyPath) { + return $null + } # Load YamlDotNet if it's requested by PowerShellYamlSerializer. Ignore other requests as they might # originate from other assemblies that are not part of this module and which might have different # versions of the module that they need to load. - if ($e.Name -like "*YamlDotNet*" -and $e.RequestingAssembly -like "*PowerShellYamlSerializer*" ) { + if ($e.Name -match "^YamlDotNet,*" -and $e.RequestingAssembly.Location -eq $serializerAssemblyPath) { return [System.Reflection.Assembly]::LoadFile($powershellYamlDotNetAssemblyPath) } return $null - }) + } + [System.AppDomain]::CurrentDomain.add_AssemblyResolve($resolver) # Load the StringQuotingEmitter from PowerShellYamlSerializer to force the resolver handler to fire once. - # This will load the YamlDotNet assembly and expand the global variable $powershellYamlDotNetAssemblyPath. - # We then remove it to avoid polluting the global scope. # This is an ugly hack I am not happy with. $serializerAssembly.GetType("StringQuotingEmitter") | Out-Null + + # Remove the resolver handler after it has been used. + [System.AppDomain]::CurrentDomain.remove_AssemblyResolve($resolver) } - Remove-Variable -Name powershellYamlDotNetAssemblyPath -Scope Global return @{ "yaml"= $yamlAssembly; "quoted" = $serializerAssembly } }