diff --git a/dsc/tests/dsc_sshdconfig.tests.ps1 b/dsc/tests/dsc_sshdconfig.tests.ps1 index 9ff323e39..ffc9b6389 100644 --- a/dsc/tests/dsc_sshdconfig.tests.ps1 +++ b/dsc/tests/dsc_sshdconfig.tests.ps1 @@ -5,12 +5,16 @@ BeforeDiscovery { $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent() $principal = [System.Security.Principal.WindowsPrincipal]::new($identity) $isElevated = $principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator) - $sshdExists = ($null -ne (Get-Command sshd -CommandType Application -ErrorAction Ignore)) - $skipTest = !$isElevated -or !$sshdExists } + else { + $isElevated = (id -u) -eq 0 + } + + $sshdExists = ($null -ne (Get-Command sshd -CommandType Application -ErrorAction Ignore)) + $skipTest = !$isElevated -or !$sshdExists } -Describe 'SSHDConfig resource tests' -Skip:(!$IsWindows -or $skipTest) { +Describe 'SSHDConfig resource tests' -Skip:($skipTest) { BeforeAll { # set a non-default value in a temporary sshd_config file "LogLevel Debug3`nPasswordAuthentication no" | Set-Content -Path $TestDrive/test_sshd_config diff --git a/grammars/tree-sitter-ssh-server-config/grammar.js b/grammars/tree-sitter-ssh-server-config/grammar.js index 113b3c7d4..b49a19e4e 100644 --- a/grammars/tree-sitter-ssh-server-config/grammar.js +++ b/grammars/tree-sitter-ssh-server-config/grammar.js @@ -11,27 +11,26 @@ const PREC = { export default grammar({ name: 'ssh_server_config', - extras: $ => [' ', '\t', '\r'], + extras: $ => [' ', '\t', '\r', $.comment], rules: { - server_config: $ => seq(repeat(choice($._empty_line, $.comment, $.keyword)), repeat($.match)), + server_config: $ => seq(repeat(choice($._new_line, $.keyword)), repeat($.match)), - // check for an empty line that is just a /n character - _empty_line: $ => '\n', - comment: $ => /#.*\n/, + _new_line: $ => '\n', + comment: $ => /#.*/, keyword: $ => seq( field('keyword', $.alphanumeric), choice(seq(/[ \t]/, optional('=')), '='), optional(field('operator', $.operator)), field('arguments', $.arguments), - "\n" + $._new_line ), match: $ => seq( token(prec(PREC.MATCH, /match/i)), - seq(repeat1($.criteria), $._empty_line), - repeat1(choice($.comment, $.keyword)), + seq(repeat1($.criteria), $._new_line), + repeat1(choice($._new_line, $.keyword)) ), criteria: $ => seq( @@ -48,7 +47,7 @@ export default grammar({ boolean: $ => choice('yes', 'no'), number: $ => /\d+/, operator: $ => token(prec(PREC.OPERATOR, /[-+\^]/)), - string: $ => /[^\r\n,"'\s]+/, /* cannot contain spaces */ + string: $ => /[^\n\r\s,"'#]+/, /* cannot contain spaces */ _quotedString: $ => /[^\r\n,"']+/, /* can contain spaces */ _doublequotedString: $ => seq('"', alias($._quotedString, $.string), repeat(seq(',', alias($._quotedString, $.string))), '"'), diff --git a/grammars/tree-sitter-ssh-server-config/test/corpus/valid_expressions.txt b/grammars/tree-sitter-ssh-server-config/test/corpus/valid_expressions.txt index e178f04dd..529f278ba 100644 --- a/grammars/tree-sitter-ssh-server-config/test/corpus/valid_expressions.txt +++ b/grammars/tree-sitter-ssh-server-config/test/corpus/valid_expressions.txt @@ -429,3 +429,199 @@ passwordauthentication no (alphanumeric) (arguments (boolean))))) +==== +parse comments between keywords +==== +port 2222 # use non-default port + +--- +(server_config + (keyword + (alphanumeric) + (arguments + (number)) + (comment))) +==== +parse comments in match blocks +==== +port 2222 +passwordauthentication no + +match User testuser # comment about criteria + PasswordAuthentication yes + AllowTcpForwarding no +# comment between match blocks +match Address 192.168.1.0/24 + X11Forwarding yes + MaxAuthTries 3 + +--- +(server_config + (keyword + (alphanumeric) + (arguments + (number))) + (keyword + (alphanumeric) + (arguments + (boolean))) + (match + (criteria + (alpha) + (argument + (string))) + (comment) + (keyword + (alphanumeric) + (arguments + (boolean))) + (keyword + (alphanumeric) + (arguments + (boolean))) + (comment)) + (match + (criteria + (alpha) + (argument + (string))) + (keyword + (alphanumeric) + (arguments + (boolean))) + (keyword + (alphanumeric) + (arguments + (number))))) +==== +parse newlines between match blocks +==== +port 2222 +passwordauthentication no + +match User testuser + PasswordAuthentication yes + AllowTcpForwarding no + +match Address 192.168.1.0/24 + X11Forwarding yes + MaxAuthTries 3 + +--- +(server_config + (keyword + (alphanumeric) + (arguments + (number))) + (keyword + (alphanumeric) + (arguments + (boolean))) + (match + (criteria + (alpha) + (argument + (string))) + (keyword + (alphanumeric) + (arguments + (boolean))) + (keyword + (alphanumeric) + (arguments + (boolean)))) + (match + (criteria + (alpha) + (argument + (string))) + (keyword + (alphanumeric) + (arguments + (boolean))) + (keyword + (alphanumeric) + (arguments + (number))))) +==== +parse comment within match block +==== +match user developer + # Enable password authentication for developers - comment ignored + passwordauthentication yes + +--- +(server_config + (match + (criteria + (alpha) + (argument + (string))) + (comment) + (keyword + (alphanumeric) + (arguments + (boolean))))) +==== +parse newlines between match blocks +==== +match User testuser + PasswordAuthentication yes + AllowTcpForwarding no + # comment line 1 + # comment line 2 + + +match Address 192.168.1.0/24 + X11Forwarding yes + MaxAuthTries 3 + +--- +(server_config + (match + (criteria + (alpha) + (argument + (string))) + (keyword + (alphanumeric) + (arguments + (boolean))) + (keyword + (alphanumeric) + (arguments + (boolean))) + (comment) + (comment)) + (match + (criteria + (alpha) + (argument + (string))) + (keyword + (alphanumeric) + (arguments + (boolean))) + (keyword + (alphanumeric) + (arguments + (number))))) +==== +parse multiple comments and multiple lines +==== +# comment line 1 +# comment line 2 +# comment lin3 3 + + +passwordauthentication yes + +--- +(server_config + (comment) + (comment) + (comment) + (keyword + (alphanumeric) + (arguments + (boolean)))) \ No newline at end of file diff --git a/resources/sshdconfig/locales/en-us.toml b/resources/sshdconfig/locales/en-us.toml index d7d9a3e75..f5d1603f6 100644 --- a/resources/sshdconfig/locales/en-us.toml +++ b/resources/sshdconfig/locales/en-us.toml @@ -25,6 +25,7 @@ defaultShellCmdOptionMustBeString = "cmdOption must be a string" defaultShellEscapeArgsMustBe0Or1 = "'%{input}' must be a 0 or 1" defaultShellEscapeArgsMustBeDWord = "escapeArguments must be a DWord" defaultShellMustBeString = "shell must be a string" +includeWarning = "Include directive found in sshd_config. This resource uses 'sshd -T' to process the overall configuration state, which merges all included files but does not return the Include directive itself" traceInput = "Get input:" windowsOnly = "Microsoft.OpenSSH.SSHD/Windows is only applicable to Windows" diff --git a/resources/sshdconfig/src/get.rs b/resources/sshdconfig/src/get.rs index 681d561fd..14d1209cb 100644 --- a/resources/sshdconfig/src/get.rs +++ b/resources/sshdconfig/src/get.rs @@ -10,7 +10,7 @@ use { use rust_i18n::t; use serde_json::{Map, Value}; -use tracing::{debug, trace}; +use tracing::{debug, trace, warn}; use crate::args::Setting; use crate::error::SshdConfigError; @@ -121,10 +121,15 @@ pub fn get_sshd_settings(cmd_info: &CommandInfo, is_get: bool) -> Result, is_get: bool) -> Result command ' -TestCases @( + @{ + Command = 'get' + Description = 'ignores input filtering and returns all properties' + } + @{ + Command = 'export' + Description = 'uses input filtering and returns only specified properties' + } + ) { + param($Command, $Description) + + $inputData = @{ + _metadata = @{ + filepath = $TestConfigPath + } + passwordAuthentication = $false + } | ConvertTo-Json + + if ($Command -eq 'get') { + $result = sshdconfig $Command --input $inputData -s sshd-config 2>$null | ConvertFrom-Json + } + else { + $result = sshdconfig $Command --input $inputData 2>$null | ConvertFrom-Json + } + + if ($Command -eq 'get') { + # Get should return all properties regardless of input + $result.LogLevel | Should -Be "Debug3" + $result.PasswordAuthentication | Should -Be $false + } + else { + # Export should return only specified properties + $result.PasswordAuthentication | Should -Be $false + $result.PSObject.Properties.Name | Should -Not -Contain "loglevel" + } + } + + It ' command returns config with match blocks' -TestCases @( + @{ Command = 'get' } + @{ Command = 'export' } + ) { + param($Command) + + $inputData = @{ + _metadata = @{ + filepath = $TestConfigPathWithMatch + } + } | ConvertTo-Json + + if ($Command -eq 'get') { + $result = sshdconfig $Command --input $inputData -s sshd-config 2>$null | ConvertFrom-Json + } + else { + $result = sshdconfig $Command --input $inputData 2>$null | ConvertFrom-Json + } + $result.Port | Should -Be "2222" + $result.PasswordAuthentication | Should -Be $false + $result.Match | Should -Not -BeNullOrEmpty + $result.Match.Count | Should -Be 2 + $result.Match[0].Criteria.User | Should -Be "testuser" + $result.Match[0].PasswordAuthentication | Should -Be $true + $result.Match[0].AllowTcpForwarding | Should -Be $false + $result.Match[1].Criteria.Address | Should -Be "192.168.1.0/24" + $result.Match[1].X11Forwarding | Should -Be $true + $result.Match[1].MaxAuthTries | Should -Be "3" + } + + It ' command displays warning when Include directive is present' -TestCases @( + @{ Command = 'get' } + @{ Command = 'export' } + ) { + param($Command) + + $inputData = @{ + _metadata = @{ + filepath = $TestConfigPathWithInclude + } + } | ConvertTo-Json + + $stderrFile = Join-Path $TestDrive "stderr_$Command.txt" + if ($Command -eq 'get') { + $result = sshdconfig $Command --input $inputData -s sshd-config 2>$stderrFile | ConvertFrom-Json + } + else { + $result = sshdconfig $Command --input $inputData 2>$stderrFile | ConvertFrom-Json + } + + $stderr = Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue + $stderr | Should -BeLike "*WARN*Include directive found in sshd_config*" + Remove-Item -Path $stderrFile -Force -ErrorAction SilentlyContinue + } +} diff --git a/resources/sshdconfig/tests/sshdconfig.set.tests.ps1 b/resources/sshdconfig/tests/sshdconfig.set.tests.ps1 index d9eab06e4..23f49d823 100644 --- a/resources/sshdconfig/tests/sshdconfig.set.tests.ps1 +++ b/resources/sshdconfig/tests/sshdconfig.set.tests.ps1 @@ -6,12 +6,16 @@ BeforeDiscovery { $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent() $principal = [System.Security.Principal.WindowsPrincipal]::new($identity) $isElevated = $principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator) - $sshdExists = ($null -ne (Get-Command sshd -CommandType Application -ErrorAction Ignore)) - $skipTest = !$isElevated -or !$sshdExists } + else { + $isElevated = (id -u) -eq 0 + } + + $sshdExists = ($null -ne (Get-Command sshd -CommandType Application -ErrorAction Ignore)) + $skipTest = !$isElevated -or !$sshdExists } -Describe 'sshd_config Set Tests' -Skip:(!$IsWindows -or $skipTest) { +Describe 'sshd_config Set Tests' -Skip:($skipTest) { BeforeAll { # Create a temporary test directory for sshd_config files $TestDir = Join-Path $TestDrive "sshd_test"