mirror of
https://github.com/Icinga/icinga-powershell-framework.git
synced 2025-12-20 23:00:35 -05:00
Fixes UTF8 encoding for plugin execution
This commit is contained in:
parent
fd3ddd1538
commit
787500c452
8 changed files with 206 additions and 56 deletions
|
|
@ -15,6 +15,7 @@ Released closed milestones can be found on [GitHub](https://github.com/Icinga/ic
|
|||
|
||||
* [#473](https://github.com/Icinga/icinga-powershell-framework/pull/473) Fixes an issue with current string rendering config implementation, as string values containing whitespaces or `$` are rendered wrong by default, if not set in single quotes `''`
|
||||
* [#476](https://github.com/Icinga/icinga-powershell-framework/pull/476) Fixes exception `You cannot call a method on va null-valued expression` during installation in case no background daemon is configured
|
||||
* [#482](https://github.com/Icinga/icinga-powershell-framework/pull/482) Fixes encoding problems with special chars or German umlauts during plugin execution and unescaped whitespace in plugin argument strings like `Icinga for Windows`, which was previously wrongly rended as `Icinga` for example
|
||||
* [#529](https://github.com/Icinga/icinga-powershell-framework/pull/529) Fixes package manifest reader for Icinga for Windows components on Windows 2012 R2 and older
|
||||
* [#523](https://github.com/Icinga/icinga-powershell-framework/pull/523) Fixes errors on encapsulated PowerShell calls for missing Cmdlets `Write-IcingaConsoleError` and `Optimize-IcingaForWindowsMemory`
|
||||
* [#524](https://github.com/Icinga/icinga-powershell-framework/issues/524) Fixes uninstallation process by improving the location handling of PowerShell instances with Icinga IMC or Shell
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ function Write-IcingaFrameworkCodeCache()
|
|||
# Load modules from directory
|
||||
Get-ChildItem -Path $directory -Recurse -Filter '*.psm1' |
|
||||
ForEach-Object {
|
||||
$CacheContent += (Get-Content -Path $_.FullName -Raw);
|
||||
$CacheContent += (Get-Content -Path $_.FullName -Raw -Encoding 'UTF8');
|
||||
$CacheContent += "`r`n";
|
||||
}
|
||||
|
||||
|
|
@ -122,7 +122,7 @@ function Write-IcingaFrameworkCodeCache()
|
|||
return;
|
||||
}
|
||||
|
||||
Set-Content -Path $CacheFile -Value $CacheContent;
|
||||
Set-Content -Path $CacheFile -Value $CacheContent -Encoding 'UTF8';
|
||||
|
||||
Remove-IcingaFrameworkDependencyFile;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ function Invoke-IcingaInternalServiceCall()
|
|||
{
|
||||
param (
|
||||
[string]$Command = '',
|
||||
[array]$Arguments = @(),
|
||||
[hashtable]$Arguments = @{ },
|
||||
[switch]$NoExit = $FALSE
|
||||
);
|
||||
|
||||
|
|
@ -49,48 +49,12 @@ function Invoke-IcingaInternalServiceCall()
|
|||
Set-IcingaTLSVersion;
|
||||
Enable-IcingaUntrustedCertificateValidation -SuppressMessages;
|
||||
|
||||
[hashtable]$CommandArguments = @{ };
|
||||
[int]$ArgumentIndex = 0;
|
||||
|
||||
# Resolve our array arguments provided by $args and build proper check arguments
|
||||
while ($ArgumentIndex -lt $Arguments.Count) {
|
||||
$Value = $Arguments[$ArgumentIndex];
|
||||
[string]$Argument = [string]$Value;
|
||||
$ArgumentValue = $null;
|
||||
|
||||
if ($Argument -eq '-IcingaForWindowsRemoteExecution' -Or $Argument -eq '-IcingaForWindowsJEARemoteExecution') {
|
||||
$ArgumentIndex += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($Value[0] -eq '-') {
|
||||
if (($ArgumentIndex + 1) -lt $Arguments.Count) {
|
||||
[string]$NextValue = $Arguments[$ArgumentIndex + 1];
|
||||
if ($NextValue[0] -eq '-') {
|
||||
$ArgumentValue = $TRUE;
|
||||
} else {
|
||||
$ArgumentValue = $Arguments[$ArgumentIndex + 1];
|
||||
}
|
||||
} else {
|
||||
$ArgumentValue = $TRUE;
|
||||
}
|
||||
} else {
|
||||
$ArgumentIndex += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
$Argument = $Argument.Replace('-', '');
|
||||
|
||||
$CommandArguments.Add($Argument, $ArgumentValue);
|
||||
$ArgumentIndex += 1;
|
||||
}
|
||||
|
||||
# Now queue the check inside our REST-Api
|
||||
try {
|
||||
$ApiResult = Invoke-WebRequest -Method POST -UseBasicParsing -Uri ([string]::Format('https://localhost:{0}/v1/checker?command={1}', $RestApiPort, $Command)) -Body (ConvertTo-JsonUTF8Bytes -InputObject $CommandArguments -Depth 100 -Compress) -ContentType 'application/json' -TimeoutSec $Timeout;
|
||||
$ApiResult = Invoke-WebRequest -Method POST -UseBasicParsing -Uri ([string]::Format('https://localhost:{0}/v1/checker?command={1}', $RestApiPort, $Command)) -Body (ConvertTo-JsonUTF8Bytes -InputObject $Arguments -Depth 100 -Compress) -ContentType 'application/json' -TimeoutSec $Timeout;
|
||||
} catch {
|
||||
# Fallback to execute plugin locally
|
||||
Write-IcingaEventMessage -Namespace 'Framework' -EventId 1553 -ExceptionObject $_ -Objects $Command, $CommandArguments;
|
||||
Write-IcingaEventMessage -Namespace 'Framework' -EventId 1553 -ExceptionObject $_ -Objects $Command, $Arguments;
|
||||
return $NULL;
|
||||
}
|
||||
|
||||
|
|
@ -100,12 +64,12 @@ function Invoke-IcingaInternalServiceCall()
|
|||
|
||||
# In case we didn't receive a check result, fallback to local execution
|
||||
if ([string]::IsNullOrEmpty($IcingaResult.$Command.checkresult)) {
|
||||
Write-IcingaEventMessage -Namespace 'Framework' -EventId 1553 -Objects 'The check result for the executed command was empty', $Command, $CommandArguments;
|
||||
Write-IcingaEventMessage -Namespace 'Framework' -EventId 1553 -Objects 'The check result for the executed command was empty', $Command, $Arguments;
|
||||
return $NULL;
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrEmpty($IcingaResult.$Command.exitcode)) {
|
||||
Write-IcingaEventMessage -Namespace 'Framework' -EventId 1553 -Objects 'The check result for the executed command was empty', $Command, $CommandArguments;
|
||||
Write-IcingaEventMessage -Namespace 'Framework' -EventId 1553 -Objects 'The check result for the executed command was empty', $Command, $Arguments;
|
||||
return $NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@ function Read-IcingaAgentDebugLogFile()
|
|||
return;
|
||||
}
|
||||
|
||||
Get-Content -Path $Logfile -Tail 20 -Wait;
|
||||
Get-Content -Path $Logfile -Tail 20 -Wait -Encoding 'UTF8';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,6 @@ function Read-IcingaAgentLogFile()
|
|||
return;
|
||||
}
|
||||
|
||||
Get-Content -Path $Logfile -Tail 20 -Wait;
|
||||
Get-Content -Path $Logfile -Tail 20 -Wait -Encoding 'UTF8';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
133
lib/core/tools/ConvertTo-IcingaPowerShellArguments.psm1
Normal file
133
lib/core/tools/ConvertTo-IcingaPowerShellArguments.psm1
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Fixes the current encoding hell for arguments by taking every argument
|
||||
parsed from Icinga and converting it from PowerShell native encoding
|
||||
to UTF8
|
||||
.DESCRIPTION
|
||||
Fixes the current encoding hell for arguments by taking every argument
|
||||
parsed from Icinga and converting it from PowerShell native encoding
|
||||
to UTF8
|
||||
.PARAMETER Arguments
|
||||
The array of arguments for re-encoding. By default, this could be $args
|
||||
for calls from Exit-IcingaExecutePlugin
|
||||
.EXAMPLE
|
||||
PS> [hashtable]$ConvertedArgs = ConvertTo-IcingaPowerShellArguments -Arguments $args;
|
||||
#>
|
||||
|
||||
function ConvertTo-IcingaPowerShellArguments()
|
||||
{
|
||||
param (
|
||||
[array]$Arguments = @()
|
||||
);
|
||||
|
||||
[hashtable]$IcingaArguments = @{ };
|
||||
[int]$ArgumentIndex = 0;
|
||||
|
||||
while ($ArgumentIndex -lt $Arguments.Count) {
|
||||
# Check if the current position is a string
|
||||
if ($Arguments[$ArgumentIndex] -IsNot [string]) {
|
||||
# Continue if we are not a string (argument)
|
||||
$ArgumentIndex += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
# check_by_icingaforwindows arguments -> not required for any plugin execution
|
||||
if ($Arguments[$ArgumentIndex] -eq '-IcingaForWindowsRemoteExecution' -Or $Arguments[$ArgumentIndex] -eq '-IcingaForWindowsJEARemoteExecution') {
|
||||
$ArgumentIndex += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
# Check if it starts with '-', which should indicate it being an argument
|
||||
if ($Arguments[$ArgumentIndex][0] -ne '-') {
|
||||
# Continue if we are not an argument
|
||||
$ArgumentIndex += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
# First convert our argument
|
||||
[string]$Argument = ConvertTo-IcingaUTF8Value -InputObject $Arguments[$ArgumentIndex];
|
||||
# Cut the first '-'
|
||||
$Argument = $Argument.Substring(1, $Argument.Length - 1);
|
||||
|
||||
# Check if there is anything beyond this argument, if not
|
||||
# -> We are a switch argument, adding TRUE;
|
||||
if (($ArgumentIndex + 1) -ge $Arguments.Count) {
|
||||
$IcingaArguments.Add($Argument, $TRUE);
|
||||
$ArgumentIndex += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
# Check if our next value in the array is a string
|
||||
if ($Arguments[$ArgumentIndex + 1] -Is [string]) {
|
||||
[string]$NextValue = $Arguments[$ArgumentIndex + 1];
|
||||
|
||||
# If our next value on the index starts with '-', we found another argument
|
||||
# -> The current argument seems to be a switch argument
|
||||
if ($NextValue[0] -eq '-') {
|
||||
$IcingaArguments.Add($Argument, $TRUE);
|
||||
$ArgumentIndex += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
# It could be that we parse strings without quotation which is broken because on how
|
||||
# Icinga is actually writing the arguments, let's fix this by building the string ourselves
|
||||
[int]$ReadStringIndex = $ArgumentIndex;
|
||||
$StringValue = New-Object -TypeName 'System.Text.StringBuilder';
|
||||
while ($TRUE) {
|
||||
# Check if we read beyond our array
|
||||
if (($ReadStringIndex + 1) -ge $Arguments.Count) {
|
||||
break;
|
||||
}
|
||||
|
||||
# Check if the next element is no longer a string element
|
||||
if ($Arguments[$ReadStringIndex + 1] -IsNot [string]) {
|
||||
break;
|
||||
}
|
||||
|
||||
[string]$NextValue = $Arguments[$ReadStringIndex + 1];
|
||||
|
||||
# In case the next string element starts with '-', this could be an argument
|
||||
if ($NextValue[0] -eq '-') {
|
||||
break;
|
||||
}
|
||||
|
||||
# If we already added elements to our string builder before, add a whitespace
|
||||
if ($StringValue.Length -ne 0) {
|
||||
$StringValue.Append(' ') | Out-Null;
|
||||
}
|
||||
|
||||
# Append our string value to the string builder
|
||||
$StringValue.Append($NextValue) | Out-Null;
|
||||
$ReadStringIndex += 1;
|
||||
}
|
||||
|
||||
# Add our argument with the string builder value, in case we had something to add there
|
||||
if ($StringValue.Length -ne 0) {
|
||||
$IcingaArguments.Add($Argument, (ConvertTo-IcingaUTF8Value -InputObject $StringValue.ToString()));
|
||||
$ArgumentIndex += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
# All Remaining values
|
||||
|
||||
# If we are an array object, handle empty arrays
|
||||
if ($Arguments[$ArgumentIndex + 1] -Is [array]) {
|
||||
if ($null -eq $Arguments[$ArgumentIndex + 1] -Or ($Arguments[$ArgumentIndex + 1]).Count -eq 0) {
|
||||
$IcingaArguments.Add($Argument, @());
|
||||
$ArgumentIndex += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
# Add everything else
|
||||
$IcingaArguments.Add(
|
||||
$Argument,
|
||||
(ConvertTo-IcingaUTF8Value -InputObject $Arguments[$ArgumentIndex + 1])
|
||||
);
|
||||
|
||||
$ArgumentIndex += 1;
|
||||
}
|
||||
|
||||
return $IcingaArguments;
|
||||
}
|
||||
50
lib/core/tools/ConvertTo-IcingaUTF8Value.psm1
Normal file
50
lib/core/tools/ConvertTo-IcingaUTF8Value.psm1
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Converts strings and all objects within an array from Default PowerShell encoding
|
||||
to UTF8
|
||||
.DESCRIPTION
|
||||
Converts strings and all objects within an array from Default PowerShell encoding
|
||||
to UTF8
|
||||
.PARAMETER InputObject
|
||||
A string or array object to convert
|
||||
.EXAMPLE
|
||||
PS> [array]$ConvertedArgs = ConvertTo-IcingaUTF8Arguments -Arguments $args;
|
||||
#>
|
||||
|
||||
function ConvertTo-IcingaUTF8Value()
|
||||
{
|
||||
param (
|
||||
$InputObject = $null
|
||||
);
|
||||
|
||||
if ($null -eq $InputObject) {
|
||||
return $InputObject;
|
||||
}
|
||||
|
||||
if ($InputObject -Is [string]) {
|
||||
# If german umlauts are contained, do not convert the value
|
||||
# Fixing issues for running checks locally on CLI vs. Icinga Agent
|
||||
if ($InputObject -Match "[äöüÄÖÜß]") {
|
||||
return $InputObject;
|
||||
}
|
||||
|
||||
$InputInBytes = [System.Text.Encoding]::Default.GetBytes($InputObject);
|
||||
|
||||
return ([string]([System.Text.Encoding]::UTF8.GetString($InputInBytes)));
|
||||
} elseif ($InputObject -Is [array]) {
|
||||
[array]$ArrayObject = @();
|
||||
|
||||
foreach ($entry in $InputObject) {
|
||||
if ($entry -Is [array]) {
|
||||
$ArrayObject += , (ConvertTo-IcingaUTF8Value -InputObject $entry);
|
||||
} else {
|
||||
$ArrayObject += ConvertTo-IcingaUTF8Value -InputObject $entry;
|
||||
}
|
||||
}
|
||||
|
||||
return $ArrayObject;
|
||||
}
|
||||
|
||||
# If we are not a string or a array, just return the object
|
||||
return $InputObject;
|
||||
}
|
||||
|
|
@ -4,6 +4,8 @@ function Exit-IcingaExecutePlugin()
|
|||
[string]$Command = ''
|
||||
);
|
||||
|
||||
# We need to fix the argument encoding hell
|
||||
[hashtable]$ConvertedArgs = ConvertTo-IcingaPowerShellArguments -Arguments $args;
|
||||
[string]$JEAProfile = Get-IcingaJEAContext;
|
||||
[bool]$CheckByIcingaForWindows = $FALSE;
|
||||
[bool]$CheckByJEAShell = $FALSE;
|
||||
|
|
@ -19,7 +21,7 @@ function Exit-IcingaExecutePlugin()
|
|||
# checks from a Linux/Windows remote source
|
||||
if ($CheckByIcingaForWindows) {
|
||||
# First try to queue the check over the REST-Api
|
||||
$CheckResult = Invoke-IcingaInternalServiceCall -Command $Command -Arguments $args -NoExit;
|
||||
$CheckResult = Invoke-IcingaInternalServiceCall -Command $Command -Arguments $ConvertedArgs -NoExit;
|
||||
|
||||
if ($null -ne $CheckResult) {
|
||||
# Seems we got a result
|
||||
|
|
@ -36,14 +38,14 @@ function Exit-IcingaExecutePlugin()
|
|||
|
||||
try {
|
||||
# Execute our plugin
|
||||
(& $Command @args) | Out-Null;
|
||||
(& $Command @ConvertedArgs) | Out-Null;
|
||||
} catch {
|
||||
# Handle errors within our plugins
|
||||
# If anything goes wrong handle the error very detailed
|
||||
|
||||
$Global:Icinga.Protected.RunAsDaemon = $FALSE;
|
||||
Write-IcingaExecutePluginException -Command $Command -ErrorObject $_ -Arguments $args;
|
||||
$args.Clear();
|
||||
Write-IcingaExecutePluginException -Command $Command -ErrorObject $_ -Arguments $ConvertedArgs;
|
||||
$ConvertedArgs.Clear();
|
||||
|
||||
# Do not close the session, we need to read the ExitCode from Get-IcingaInternalPluginExitCode
|
||||
# The plugin itself will terminate the session
|
||||
|
|
@ -64,7 +66,7 @@ function Exit-IcingaExecutePlugin()
|
|||
|
||||
# Regardless of JEA enabled or disabled, forward all checks to the internal API
|
||||
# and check if we get a result from there
|
||||
Invoke-IcingaInternalServiceCall -Command $Command -Arguments $args;
|
||||
Invoke-IcingaInternalServiceCall -Command $Command -Arguments $ConvertedArgs;
|
||||
|
||||
try {
|
||||
# If the plugin is not installed, throw a good exception
|
||||
|
|
@ -106,7 +108,7 @@ function Exit-IcingaExecutePlugin()
|
|||
'PerfData' = (Get-IcingaCheckSchedulerPerfData)
|
||||
'ExitCode' = $ExitCode;
|
||||
}
|
||||
} -args $Command, $args
|
||||
} -args $Command, $ConvertedArgs
|
||||
) 2>$ErrorHandler;
|
||||
|
||||
# If we have an exit code larger or equal 0, the execution inside the JEA shell was successfully and we can share the result
|
||||
|
|
@ -123,12 +125,12 @@ function Exit-IcingaExecutePlugin()
|
|||
} else {
|
||||
# If we simply run the check without JEA context or from remote, we can just execute the plugin and
|
||||
# exit with the exit code received from the result
|
||||
exit (& $Command @args);
|
||||
exit (& $Command @ConvertedArgs);
|
||||
}
|
||||
} catch {
|
||||
# If anything goes wrong handle the error
|
||||
Write-IcingaExecutePluginException -Command $Command -ErrorObject $_ -Arguments $args;
|
||||
$args.Clear();
|
||||
Write-IcingaExecutePluginException -Command $Command -ErrorObject $_ -Arguments $ConvertedArgs;
|
||||
$ConvertedArgs.Clear();
|
||||
|
||||
exit 3;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue