diff --git a/resources/sshdconfig/src/get.rs b/resources/sshdconfig/src/get.rs index b1504b890..70b2ced43 100644 --- a/resources/sshdconfig/src/get.rs +++ b/resources/sshdconfig/src/get.rs @@ -17,6 +17,7 @@ use crate::canonical_properties::CanonicalProperty; use crate::error::SshdConfigError; use crate::inputs::{CommandInfo, SSHD_CONFIG_FILEPATH}; use crate::parser::parse_text_to_map; +use crate::repeat_keyword::MULTI_ARG_KEYWORDS_SPACE_SEP; use crate::util::{ build_command_info, extract_sshd_defaults, @@ -133,6 +134,12 @@ pub fn get_sshd_settings(cmd_info: &CommandInfo, is_get: bool) -> Result Result, explicit_settings: &Map) { + for keyword in MULTI_ARG_KEYWORDS_SPACE_SEP { + if let Some(value) = explicit_settings.get(keyword) { + result.insert((*keyword).to_string(), value.clone()); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn overrides_split_group_list_with_quoted_file_value() { + // sshd -T stripped the quotes and split "openssh users" into two entries. + let mut result = Map::new(); + result.insert("allowgroups".to_string(), json!(["administrators", "openssh", "users"])); + + // The raw config file parse preserved the quoted grouping. + let mut explicit_settings = Map::new(); + explicit_settings.insert("allowgroups".to_string(), json!(["administrators", "openssh users"])); + + prefer_explicit_space_sep_lists(&mut result, &explicit_settings); + + assert_eq!( + result.get("allowgroups").unwrap(), + &json!(["administrators", "openssh users"]) + ); + } + + #[test] + fn leaves_keyword_absent_from_file_untouched() { + // allowgroups is present in sshd -T output but not explicitly set in the config file. + let mut result = Map::new(); + result.insert("allowgroups".to_string(), json!(["administrators", "openssh", "users"])); + + let explicit_settings = Map::new(); + + prefer_explicit_space_sep_lists(&mut result, &explicit_settings); + + assert_eq!( + result.get("allowgroups").unwrap(), + &json!(["administrators", "openssh", "users"]) + ); + } + + #[test] + fn leaves_non_space_sep_keyword_untouched() { + // port is not a space-separated list keyword and must not be overridden. + let mut result = Map::new(); + result.insert("port".to_string(), json!([22])); + + let mut explicit_settings = Map::new(); + explicit_settings.insert("port".to_string(), json!([2222])); + + prefer_explicit_space_sep_lists(&mut result, &explicit_settings); + + assert_eq!(result.get("port").unwrap(), &json!([22])); + } +} diff --git a/resources/sshdconfig/tests/sshdconfig.get.tests.ps1 b/resources/sshdconfig/tests/sshdconfig.get.tests.ps1 index d2408cc0a..0ca0fb57e 100644 --- a/resources/sshdconfig/tests/sshdconfig.get.tests.ps1 +++ b/resources/sshdconfig/tests/sshdconfig.get.tests.ps1 @@ -41,6 +41,12 @@ PasswordAuthentication no "@ $TestConfigPathWithInclude = Join-Path $TestDrive 'test_sshd_config_include' $configWithInclude | Set-Content -Path $TestConfigPathWithInclude + $configWithQuotedGroups = @" +PasswordAuthentication no +AllowGroups administrators "openssh users" +"@ + $TestConfigPathWithQuotedGroups = Join-Path $TestDrive 'test_sshd_config_quoted_groups' + $configWithQuotedGroups | Set-Content -Path $TestConfigPathWithQuotedGroups } AfterAll { @@ -53,6 +59,9 @@ PasswordAuthentication no if (Test-Path $TestConfigPathWithInclude) { Remove-Item -Path $TestConfigPathWithInclude -Force -ErrorAction SilentlyContinue } + if (Test-Path $TestConfigPathWithQuotedGroups) { + Remove-Item -Path $TestConfigPathWithQuotedGroups -Force -ErrorAction SilentlyContinue + } } It ' command ' -TestCases @( @@ -142,6 +151,29 @@ PasswordAuthentication no Remove-Item -Path $stderrFile -Force -ErrorAction SilentlyContinue } + It ' command preserves quoted group names containing spaces' -TestCases @( + @{ Command = 'get' } + @{ Command = 'export' } + ) { + param($Command) + + $inputData = @{ + sshd_config_filepath = $TestConfigPathWithQuotedGroups + } | 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 + } + + # "openssh users" must remain a single entry rather than being split into "openssh" and "users". + $result.AllowGroups.Count | Should -Be 2 + $result.AllowGroups[0] | Should -Be "administrators" + $result.AllowGroups[1] | Should -Be "openssh users" + } + It 'Should fail without creating target config when file does not exist' { $nonExistentPath = Join-Path $TestDrive 'nonexistent_sshd_config'