From c1597aa134ba2761fd8c9184de3da3ff64f6a35b Mon Sep 17 00:00:00 2001 From: alxcreate Date: Wed, 24 Jul 2024 14:10:37 +0200 Subject: [PATCH] Add UTF8 --- public/Send-SyslogMessage.ps1 | 37 ++++++++++++--- tests/Send-SyslogMessage.Tests.ps1 | 76 +++++++++++++++++++++--------- 2 files changed, 85 insertions(+), 28 deletions(-) diff --git a/public/Send-SyslogMessage.ps1 b/public/Send-SyslogMessage.ps1 index 800c0af..4d9a4eb 100644 --- a/public/Send-SyslogMessage.ps1 +++ b/public/Send-SyslogMessage.ps1 @@ -51,6 +51,14 @@ public enum Syslog_Protocol } "@ +Add-Type -TypeDefinition @" +public enum Syslog_Encoding +{ + ASCII, + UTF8 +} +"@ + Function Send-SyslogMessage { <# @@ -161,6 +169,13 @@ Function Send-SyslogMessage [Syslog_Protocol] $Transport = 'UDP', + # Encoding charset (ASCII or UTF8) to be used when sending the message. Default is ASCII. + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true)] + [ValidateNotNullOrEmpty()] + [Syslog_Encoding] + $Encoding = 'ASCII', + #ProcessID or PID of generator of message. Will automatically use $PID global variable. If you want to override this and send null, specify '-' to meet RFC 5424 rquirements. [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, @@ -218,8 +233,18 @@ Function Send-SyslogMessage { Write-Debug -Message 'Starting the BEGIN block...' - # Create an ASCII Encoding object - $Encoding = [Text.Encoding]::ASCII + # Create an Encoding object + Switch ($Encoding) + { + 'ASCII' { + Write-Verbose -Message 'Selected Encoding is ASCII' + $EncodingText = [System.Text.Encoding]::ASCII + } + 'UTF8' { + Write-Verbose -Message 'Selected Encoding is UTF8' + $EncodingText = [System.Text.Encoding]::UTF8 + } + } # Initiate the required network objects Switch ($Transport) @@ -365,7 +390,7 @@ Function Send-SyslogMessage Write-Verbose -Message ('Message attempting to send is: {0}' -f $FullSyslogMessage) # Ensure that the message is not too long. We could just compare the strings length, however using the encoding is the more appropriate way of confirming the length in bytes. - if ($Encoding.GetByteCount($FullSyslogMessage) -gt $MaxLength) + if ($EncodingText.GetByteCount($FullSyslogMessage) -gt $MaxLength) { $FullSyslogMessage = $FullSyslogMessage.Substring(0,$MaxLength) Write-Verbose -Message ('Message was too long and was shortened to {0} characters' -f $MaxLength) @@ -377,7 +402,7 @@ Function Send-SyslogMessage 'UDP' { # Convert into byte array representation - $ByteSyslogMessage = $Encoding.GetBytes($FullSyslogMessage) + $ByteSyslogMessage = $EncodingText.GetBytes($FullSyslogMessage) # Send the Message Try @@ -402,7 +427,7 @@ Function Send-SyslogMessage { 'Octet-Counting' { - $OctetCount = ($Encoding.GetBytes($FullSyslogMessage)).Length + $OctetCount = ($EncodingText.GetBytes($FullSyslogMessage)).Length $FramedSyslogMessage = '{0} {1}' -f $OctetCount, $FullSyslogMessage Write-Verbose -Message ('Octet-Counting - Framed message is: {0}' -f $FullSyslogMessage) } @@ -421,7 +446,7 @@ Function Send-SyslogMessage } # Convert into byte array representation - $ByteSyslogMessage = $Encoding.GetBytes($FramedSyslogMessage) + $ByteSyslogMessage = $EncodingText.GetBytes($FramedSyslogMessage) # Send the Message Try diff --git a/tests/Send-SyslogMessage.Tests.ps1 b/tests/Send-SyslogMessage.Tests.ps1 index 8f19299..a985d7f 100644 --- a/tests/Send-SyslogMessage.Tests.ps1 +++ b/tests/Send-SyslogMessage.Tests.ps1 @@ -47,15 +47,17 @@ InModuleScope $script:ModuleName { } Mock -ModuleName Posh-SYSLOG -CommandName Send-TCPMessage -MockWith { $script:TestResult = $Datagram; return $null + $script:TestEncoding = $Encoding } Mock -ModuleName Posh-SYSLOG -CommandName Send-UDPMessage -MockWith { $script:TestResult = $Datagram; return $null + $script:TestEncoding = $Encoding } $ExpectedTimeStamp = (New-Object datetime(2000,1,1)).ToString('yyyy-MM-ddTHH:mm:ss.ffffffzzz') # Create an ASCII Encoding object - $Encoding = [Text.Encoding]::ASCII + $EncodingText = [Text.Encoding]::ASCII Context 'Send-SyslogMessage = Parameter Validation' { It 'Should not accept a null value for the server' { @@ -222,6 +224,22 @@ InModuleScope $script:ModuleName { {Send-SyslogMessage -Server '127.0.0.1' -Message 'Test Syslog Message' -Severity 'Alert' -Facility 'auth' -Transport 'bob'} | Should Throw } + It 'Should Accept ASCII as a Encoding' { + {Send-SyslogMessage -Server '127.0.0.1' -Message 'Test Syslog Message' -Severity 'Alert' -Facility 'auth' -Encoding 'ASCII'} | Should Not Throw 'Cannot validate argument on parameter' + } + + It 'Should Accept UTF8 as a Encoding' { + {Send-SyslogMessage -Server '127.0.0.1' -Message 'Test Syslog Message' -Severity 'Alert' -Facility 'auth' -Encoding 'UTF8'} | Should Not Throw 'Cannot validate argument on parameter' + } + + It 'Should not accept a null value for Encoding' { + {Send-SyslogMessage -Server '127.0.0.1' -Message 'Test Syslog Message' -Severity 'Alert' -Facility 'auth' -Encoding $null} | Should Throw + } + + It 'Should not accept an invalid value for Encoding' { + {Send-SyslogMessage -Server '127.0.0.1' -Message 'Test Syslog Message' -Severity 'Alert' -Facility 'auth' -Encoding 'encoding'} | Should Throw + } + It 'Should reject ProcessID parameter if -RFC3164 is specified' { {Send-SyslogMessage -Server '127.0.0.1' -Message 'Test Syslog Message' -Severity 'Alert' -Facility 'auth' -RFC3164 -ProcessID 1} | Should Throw 'Parameter set cannot be resolved using the specified named parameters' } @@ -251,7 +269,6 @@ InModuleScope $script:ModuleName { {Send-SyslogMessage -Server '127.0.0.1' -Message 'Test Syslog Message' -Severity 'Alert' -Facility 'auth' -Transport 'TCPwithTLS' -SslProtocols $value} | Should Not Throw } } - } Context 'Send-SyslogMessage = Pipeline input' { @@ -270,7 +287,7 @@ InModuleScope $script:ModuleName { } $ExpectedResult = '<0>1 {0} TestHostname RandomAppName 12345678 messageid structuredata Test Syslog Message' -f $ExpectedTimeStamp $null = $PipelineInput | Send-SyslogMessage -Server 'localhost' - $Encoding.GetString($script:TestResult) | should be $ExpectedResult + $EncodingText.GetString($script:TestResult) | should be $ExpectedResult } It 'Should accept valid input from the pipeline for RFC3164' { @@ -284,7 +301,7 @@ InModuleScope $script:ModuleName { } $ExpectedResult = '<0>Jan 1 00:00:00 TestHostname RandomAppName Test Syslog Message' $null = $PipelineInput | Send-SyslogMessage -Server 'localhost' -RFC3164 - $Encoding.GetString($script:TestResult) | should be $ExpectedResult + $EncodingText.GetString($script:TestResult) | should be $ExpectedResult } } @@ -294,25 +311,25 @@ InModuleScope $script:ModuleName { It 'Calculates the correct priority of 0 if Facility is Kern and Severity is Emergency' { $ExpectedResult = '<0>1 {0} TestHostname Send-SyslogMessage.Tests.ps1 {1} - - Test Syslog Message' -f $ExpectedTimeStamp, $PID $null = Send-SyslogMessage -Server '127.0.0.1' -Message 'Test Syslog Message' -Severity 'Emergency' -Facility 'kern' - $Encoding.GetString($script:TestResult) | should be $ExpectedResult + $EncodingText.GetString($script:TestResult) | should be $ExpectedResult } It 'Calculates the correct priority of 7 if Facility is Kern and Severity is Debug' { $ExpectedResult = '<7>1 {0} TestHostname Send-SyslogMessage.Tests.ps1 {1} - - Test Syslog Message' -f $ExpectedTimeStamp, $PID $null = Send-SyslogMessage -Server '127.0.0.1' -Message 'Test Syslog Message' -Severity 'Debug' -Facility 'kern' - $Encoding.GetString($script:TestResult) | should be $ExpectedResult + $EncodingText.GetString($script:TestResult) | should be $ExpectedResult } It 'Calculates the correct priority of 24 if Facility is daemon and Severity is Emergency' { $ExpectedResult = '<24>1 {0} TestHostname Send-SyslogMessage.Tests.ps1 {1} - - Test Syslog Message' -f $ExpectedTimeStamp, $PID $null = Send-SyslogMessage -Server '127.0.0.1' -Message 'Test Syslog Message' -Severity 'Emergency' -Facility 'daemon' - $Encoding.GetString($script:TestResult) | should be $ExpectedResult + $EncodingText.GetString($script:TestResult) | should be $ExpectedResult } It 'Calculates the correct priority of 31 if Facility is daemon and Severity is Debug' { $ExpectedResult = '<31>1 {0} TestHostname Send-SyslogMessage.Tests.ps1 {1} - - Test Syslog Message' -f $ExpectedTimeStamp, $PID $null = Send-SyslogMessage -Server '127.0.0.1' -Message 'Test Syslog Message' -Severity 'Debug' -Facility 'daemon' - $Encoding.GetString($script:TestResult) | should be $ExpectedResult + $EncodingText.GetString($script:TestResult) | should be $ExpectedResult } } @@ -324,7 +341,7 @@ InModuleScope $script:ModuleName { $ExpectedResult = '<33>Jan 10 00:00:00 TestHostname Send-SyslogMessage.Tests.ps1 Test Syslog Message' $null = Send-SyslogMessage -Server '127.0.0.1' -Message 'Test Syslog Message' -Severity 'Alert' -Facility 'auth' -RFC3164 - $Encoding.GetString($script:TestResult) | should be $ExpectedResult + $EncodingText.GetString($script:TestResult) | should be $ExpectedResult } It 'Should send RFC5424 formatted message with correct date format (1 to 9)' { @@ -332,7 +349,7 @@ InModuleScope $script:ModuleName { $ExpectedResult = '<33>Jan 1 00:00:00 TestHostname Send-SyslogMessage.Tests.ps1 Test Syslog Message' $null = Send-SyslogMessage -Server '127.0.0.1' -Message 'Test Syslog Message' -Severity 'Alert' -Facility 'auth' -RFC3164 - $Encoding.GetString($script:TestResult) | should be $ExpectedResult + $EncodingText.GetString($script:TestResult) | should be $ExpectedResult } } @@ -342,7 +359,7 @@ InModuleScope $script:ModuleName { It 'Should send RFC5424 formatted message' { $ExpectedResult = '<33>1 {0} TestHostname Send-SyslogMessage.Tests.ps1 {1} - - Test Syslog Message' -f $ExpectedTimeStamp, $PID $null = Send-SyslogMessage -Server '127.0.0.1' -Message 'Test Syslog Message' -Severity 'Alert' -Facility 'auth' - $Encoding.GetString($script:TestResult) | should be $ExpectedResult + $EncodingText.GetString($script:TestResult) | should be $ExpectedResult } } @@ -354,12 +371,12 @@ InModuleScope $script:ModuleName { It 'truncates RFC 5424 messages to 2k' { $ExpectedResult = ('<33>1 {0} TestHostname Send-SyslogMessage.Tests.ps1 {1} - - {2}' -f $ExpectedTimeStamp, $PID, $LongMsg).Substring(0,2048) $null = Send-SyslogMessage -Server '127.0.0.1' -Message $LongMsg -Severity 'Alert' -Facility 'auth' - $Encoding.GetString($script:TestResult) | should be $ExpectedResult + $EncodingText.GetString($script:TestResult) | should be $ExpectedResult } It 'truncates RFC 3164 messages to 1k' { $ExpectedResult = ('<33>Jan 1 00:00:00 TestHostname Send-SyslogMessage.Tests.ps1 {1}' -f $ExpectedTimeStamp, $LongMsg).Substring(0,1024) $null = Send-SyslogMessage -Server '127.0.0.1' -Message $LongMsg -Severity 'Alert' -Facility 'auth' -RFC3164 - $Encoding.GetString($script:TestResult) | should be $ExpectedResult + $EncodingText.GetString($script:TestResult) | should be $ExpectedResult } } @@ -372,14 +389,14 @@ InModuleScope $script:ModuleName { $FramedResult = '2048 {0}' -f $ExpectedResult $null = Send-SyslogMessage -Server '127.0.0.1' -Message $LongMsg -Severity 'Alert' -Facility 'auth' -Transport TCP - $Encoding.GetString($script:TestResult) | should be $FramedResult + $EncodingText.GetString($script:TestResult) | should be $FramedResult } It 'truncates RFC 3164 messages to 1k' { $ExpectedResult = ('<33>Jan 1 00:00:00 TestHostname Send-SyslogMessage.Tests.ps1 {1}' -f $ExpectedTimeStamp, $LongMsg).Substring(0,1024) $FramedResult = '1024 {0}' -f $ExpectedResult $null = Send-SyslogMessage -Server '127.0.0.1' -Message $LongMsg -Severity 'Alert' -Facility 'auth' -RFC3164 -Transport TCP - $Encoding.GetString($script:TestResult) | should be $FramedResult + $EncodingText.GetString($script:TestResult) | should be $FramedResult } } @@ -391,7 +408,7 @@ InModuleScope $script:ModuleName { $FramedResult = '{0} {1}' -f $ExpectedResult.Length, $ExpectedResult $null = Send-SyslogMessage -Server '127.0.0.1' -Message 'Test Syslog Message' -Severity 'Alert' -Facility 'auth' -Hostname TestHostname -Transport TCP - $Encoding.GetString($script:TestResult) | should be $FramedResult + $EncodingText.GetString($script:TestResult) | should be $FramedResult } It 'sends using TCP transport with Octet-Counting as the framing' { @@ -399,20 +416,36 @@ InModuleScope $script:ModuleName { $FramedResult = '{0} {1}' -f $ExpectedResult.Length, $ExpectedResult $null = Send-SyslogMessage -Server '127.0.0.1' -Message 'Test Syslog Message' -Severity 'Alert' -Facility 'auth' -Hostname TestHostname -Transport TCP -FramingMethod Octet-Counting - $Encoding.GetString($script:TestResult) | should be $FramedResult + $EncodingText.GetString($script:TestResult) | should be $FramedResult } It 'sends using TCP transport with Non-Transparent-Framing as the framing' { $ExpectedResult = '<33>1 {0} TestHostname Send-SyslogMessage.Tests.ps1 {1} - - Test Syslog Message{2}' -f $ExpectedTimeStamp, $PID, "`n" $null = Send-SyslogMessage -Server '127.0.0.1' -Message 'Test Syslog Message' -Severity 'Alert' -Facility 'auth' -Hostname TestHostname -Transport TCP -FramingMethod Non-Transparent-Framing - $Encoding.GetString($script:TestResult) | should be $ExpectedResult + $EncodingText.GetString($script:TestResult) | should be $ExpectedResult } It 'sends using TCP transport with no framing' { $ExpectedResult = '<33>1 {0} TestHostname Send-SyslogMessage.Tests.ps1 {1} - - Test Syslog Message' -f $ExpectedTimeStamp, $PID $null = Send-SyslogMessage -Server '127.0.0.1' -Message 'Test Syslog Message' -Severity 'Alert' -Facility 'auth' -Hostname TestHostname -Transport TCP -FramingMethod None - $Encoding.GetString($script:TestResult) | should be $ExpectedResult + $EncodingText.GetString($script:TestResult) | should be $ExpectedResult } + } + Context 'Send-SyslogMessage = Encoding Tests' { + + It 'Correctly encodes non-ASCII characters with UTF8' { + $message = 'Test Syslog Message with non-ASCII characters: áéíóú' + $null = Send-SyslogMessage -Server '127.0.0.1' -Message $message -Severity 'Alert' -Facility 'auth' -Encoding 'UTF8' + $decodedMessage = [System.Text.Encoding]::UTF8.GetString($script:TestResult) + $decodedMessage | Should BeLike '*áéíóú*' + } + + It 'Handles ASCII encoding for non-ASCII characters' { + $message = 'Test Syslog Message with non-ASCII characters: áéíóú' + $null = Send-SyslogMessage -Server '127.0.0.1' -Message $message -Severity 'Alert' -Facility 'auth' -Encoding 'ASCII' + $decodedMessage = [System.Text.Encoding]::ASCII.GetString($script:TestResult) + $decodedMessage | Should Not BeLike '*áéíóú*' + } } Context 'Send-SyslogMessage = Application Name Selection' { @@ -421,13 +454,13 @@ InModuleScope $script:ModuleName { It 'Takes an Application Name as specified' { $ExpectedResult = '<33>1 {0} TestHostname SomeRandomName {1} - - Test Syslog Message' -f $ExpectedTimeStamp, $PID $null = Send-SyslogMessage -Server '127.0.0.1' -Message 'Test Syslog Message' -Severity 'Alert' -Facility 'auth' -ApplicationName 'SomeRandomName' - $Encoding.GetString($script:TestResult) | should be $ExpectedResult + $EncodingText.GetString($script:TestResult) | should be $ExpectedResult } It 'uses myInvocation.ScriptName if one is available' { $ExpectedResult = '<33>1 {0} TestHostname Send-SyslogMessage.Tests.ps1 {1} - - Test Syslog Message' -f $ExpectedTimeStamp, $PID $null = Send-SyslogMessage -Server '127.0.0.1' -Message 'Test Syslog Message' -Severity 'Alert' -Facility 'auth' - $Encoding.GetString($script:TestResult) | should be $ExpectedResult + $EncodingText.GetString($script:TestResult) | should be $ExpectedResult } } @@ -440,5 +473,4 @@ InModuleScope $script:ModuleName { } } } - }