From 8cd3559436bdaf0e75f5d7288f5c7bd8559bdaa3 Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Thu, 9 Apr 2026 16:57:44 -0400 Subject: [PATCH 1/8] init file from sshd_config_default on Windows --- resources/sshdconfig/locales/en-us.toml | 2 + resources/sshdconfig/src/set.rs | 23 ++------ resources/sshdconfig/src/util.rs | 57 ++++++++++++++++++- .../sshdconfig/tests/sshdconfig.get.tests.ps1 | 23 +++++++- .../sshdconfig/tests/sshdconfig.set.tests.ps1 | 27 +++++++-- .../tests/sshdconfigRepeat.tests.ps1 | 33 +++++++++++ .../tests/sshdconfigRepeatList.tests.ps1 | 35 ++++++++++++ 7 files changed, 173 insertions(+), 27 deletions(-) diff --git a/resources/sshdconfig/locales/en-us.toml b/resources/sshdconfig/locales/en-us.toml index 2b215dc9e..a8c4b7e3b 100644 --- a/resources/sshdconfig/locales/en-us.toml +++ b/resources/sshdconfig/locales/en-us.toml @@ -99,6 +99,8 @@ writingTempConfig = "Writing temporary sshd_config file" [util] cleanupFailed = "Failed to clean up temporary file %{path}: %{error}" getIgnoresInputFilters = "get command does not support filtering based on input settings, provided input will be ignored" +seededConfigFromDefault = "Seeded missing sshd_config from '%{source}' to '%{target}'" +sshdConfigDefaultNotFound = "sshd_config file does not exist and no default source could be found. Checked: %{paths}" sshdConfigReadFailed = "failed to read sshd_config at path: '%{path}'" sshdElevation = "elevated security context required" tempFileCreated = "temporary file created at: %{path}" diff --git a/resources/sshdconfig/src/set.rs b/resources/sshdconfig/src/set.rs index 14c7564ad..47d4e9f47 100644 --- a/resources/sshdconfig/src/set.rs +++ b/resources/sshdconfig/src/set.rs @@ -24,7 +24,7 @@ use crate::repeat_keyword::{ RepeatInput, RepeatListInput, NameValueEntry, add_or_update_entry, extract_single_keyword, remove_entry, parse_and_validate_entries }; -use crate::util::{build_command_info, get_default_sshd_config_path, invoke_sshd_config_validation}; +use crate::util::{build_command_info, ensure_sshd_config_exists, get_default_sshd_config_path, invoke_sshd_config_validation}; /// Invoke the set command. /// @@ -189,16 +189,9 @@ fn set_sshd_config(cmd_info: &mut CommandInfo) -> Result<(), SshdConfigError> { let mut get_cmd_info = cmd_info.clone(); get_cmd_info.include_defaults = false; get_cmd_info.input = Map::new(); + ensure_sshd_config_exists(get_cmd_info.metadata.filepath.clone())?; - let mut existing_config = match get_sshd_settings(&get_cmd_info, true) { - Ok(config) => config, - Err(SshdConfigError::FileNotFound(_)) => { - return Err(SshdConfigError::InvalidInput( - t!("set.purgeFalseRequiresExistingFile").to_string() - )); - } - Err(e) => return Err(e), - }; + let mut existing_config = get_sshd_settings(&get_cmd_info, true)?; for (key, value) in &cmd_info.input { if value.is_null() { existing_config.remove(key); @@ -281,12 +274,6 @@ fn get_existing_config(cmd_info: &CommandInfo) -> Result, Ssh let mut get_cmd_info = cmd_info.clone(); get_cmd_info.include_defaults = false; get_cmd_info.input = Map::new(); - match get_sshd_settings(&get_cmd_info, false) { - Ok(config) => Ok(config), - Err(SshdConfigError::FileNotFound(_)) => { - // If file doesn't exist, create empty config - Ok(Map::new()) - } - Err(e) => Err(e), - } + ensure_sshd_config_exists(get_cmd_info.metadata.filepath.clone())?; + get_sshd_settings(&get_cmd_info, false) } diff --git a/resources/sshdconfig/src/util.rs b/resources/sshdconfig/src/util.rs index 4031e190b..295396393 100644 --- a/resources/sshdconfig/src/util.rs +++ b/resources/sshdconfig/src/util.rs @@ -107,6 +107,59 @@ pub fn get_default_sshd_config_path(input: Option) -> Result Vec { + let mut candidates: Vec = Vec::new(); + + if cfg!(windows) { + if let Ok(system_drive) = std::env::var("SystemDrive") { + candidates.push(PathBuf::from(format!("{system_drive}\\Windows\\System32\\OpenSSH\\sshd_config_default"))); + } + } + + candidates +} + +/// Ensure the target `sshd_config` exists by seeding it from a platform default source. +/// +/// # Errors +/// +/// This function returns an error if the target cannot be created or no source default config is available. +pub fn ensure_sshd_config_exists(input: Option) -> Result { + let target_path = get_default_sshd_config_path(input)?; + if target_path.exists() { + return Ok(target_path); + } + + if !cfg!(windows) { + return Err(SshdConfigError::InvalidInput( + t!("util.sshdConfigDefaultNotFound", paths = "Windows seeding is only supported on Windows hosts").to_string() + )); + } + + let candidates = get_sshd_config_default_source_candidates(); + let source_path = candidates + .iter() + .find(|candidate| candidate.is_file()) + .cloned() + .ok_or_else(|| { + let paths = candidates + .iter() + .map(|path| path.display().to_string()) + .collect::>() + .join(", "); + SshdConfigError::InvalidInput(t!("util.sshdConfigDefaultNotFound", paths = paths).to_string()) + })?; + + if let Some(parent) = target_path.parent() { + std::fs::create_dir_all(parent)?; + } + + std::fs::copy(&source_path, &target_path)?; + debug!("{}", t!("util.seededConfigFromDefault", source = source_path.display(), target = target_path.display())); + + Ok(target_path) +} + /// Invoke sshd -T. /// /// # Errors @@ -118,7 +171,7 @@ pub fn invoke_sshd_config_validation(args: Option) -> Result) -> Result$stderrFile $LASTEXITCODE | Should -Not -Be 0 + Test-Path $nonExistentPath | Should -Be $false + + $stderr = Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue + $stderr | Should -Match "File not found" + + Remove-Item -Path $stderrFile -Force -ErrorAction SilentlyContinue + } + + It 'Should fail when config file does not exist even when default source is missing' { + $nonExistentPath = Join-Path $TestDrive 'nonexistent_sshd_config' + + $inputData = @{ + _metadata = @{ + filepath = $nonExistentPath + } + } | ConvertTo-Json + + $stderrFile = Join-Path $TestDrive "stderr_missing_default_source.txt" + sshdconfig get --input $inputData -s sshd-config 2>$stderrFile + $LASTEXITCODE | Should -Not -Be 0 + $stderr = Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue $stderr | Should -Match "File not found" 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 e90261bad..882766a14 100644 --- a/resources/sshdconfig/tests/sshdconfig.set.tests.ps1 +++ b/resources/sshdconfig/tests/sshdconfig.set.tests.ps1 @@ -21,6 +21,9 @@ Describe 'sshd_config Set Tests' -Skip:($skipTest) { $TestDir = Join-Path $TestDrive "sshd_test" New-Item -Path $TestDir -ItemType Directory -Force | Out-Null $TestConfigPath = Join-Path $TestDir "sshd_config" + + $script:DefaultSourceExists = $IsWindows -and + (Test-Path -Path "$env:SystemDrive\Windows\System32\OpenSSH\sshd_config_default" -PathType Leaf -ErrorAction SilentlyContinue) } AfterEach { @@ -180,7 +183,7 @@ Describe 'sshd_config Set Tests' -Skip:($skipTest) { sshdconfig set --input $validConfig -s sshd-config } - It 'Should fail with purge=false when file does not exist' { + It 'Should seed missing file from default source when available on Windows, or fail otherwise' { $nonExistentPath = Join-Path $TestDrive "nonexistent_sshd_config" $inputConfig = @{ @@ -193,12 +196,26 @@ Describe 'sshd_config Set Tests' -Skip:($skipTest) { $stderrFile = Join-Path $TestDrive "stderr_purgefalse_nofile.txt" sshdconfig set --input $inputConfig -s sshd-config 2>$stderrFile - $LASTEXITCODE | Should -Not -Be 0 - $stderr = Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue - $stderr | Should -Match "_purge=false requires an existing sshd_config file" - $stderr | Should -Match "Use _purge=true to create a new configuration file" + if ($IsWindows -and $script:DefaultSourceExists) { + $LASTEXITCODE | Should -Be 0 + Test-Path $nonExistentPath | Should -Be $true + + $getInput = @{ + _metadata = @{ + filepath = $nonExistentPath + } + } | ConvertTo-Json + $result = sshdconfig get --input $getInput -s sshd-config 2>$null | ConvertFrom-Json + $result.Port | Should -Be "8888" + } else { + $LASTEXITCODE | Should -Not -Be 0 + $stderr = Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue + $stderr | Should -Match "no default source could be found" + } + Remove-Item -Path $stderrFile -Force -ErrorAction SilentlyContinue + Remove-Item -Path $nonExistentPath -Force -ErrorAction SilentlyContinue } It 'Should fail with invalid keyword and not modify file' { diff --git a/resources/sshdconfig/tests/sshdconfigRepeat.tests.ps1 b/resources/sshdconfig/tests/sshdconfigRepeat.tests.ps1 index ac39caf23..bd5ee858f 100644 --- a/resources/sshdconfig/tests/sshdconfigRepeat.tests.ps1 +++ b/resources/sshdconfig/tests/sshdconfigRepeat.tests.ps1 @@ -33,6 +33,9 @@ Describe 'sshd-config-repeat Set Tests' -Skip:($skipTest) { $script:DefaultSftpPath = "/usr/lib/openssh/sftp-server" $script:AlternatePath = "/usr/libexec/sftp-server" } + + $script:DefaultSourceExists = $IsWindows -and + (Test-Path -Path "$env:SystemDrive\Windows\System32\OpenSSH\sshd_config_default" -PathType Leaf -ErrorAction SilentlyContinue) } AfterEach { @@ -212,5 +215,35 @@ PasswordAuthentication yes Remove-Item -Path $stderrFile -Force -ErrorAction SilentlyContinue } + + It 'Should seed missing file from default source when available on Windows, or fail otherwise' { + $nonExistentPath = Join-Path $TestDrive "nonexistent_sshd_config" + + $inputConfig = @{ + _metadata = @{ + filepath = $nonExistentPath + } + _exist = $true + subsystem = @{ + name = "powershell" + value = "/usr/bin/pwsh -sshs" + } + } | ConvertTo-Json + + $stderrFile = Join-Path $TestDrive "stderr_missing_default_repeat.txt" + sshdconfig set --input $inputConfig -s sshd-config-repeat 2>$stderrFile + + if ($IsWindows -and $script:DefaultSourceExists) { + $LASTEXITCODE | Should -Be 0 + Test-Path $nonExistentPath | Should -Be $true + } else { + $LASTEXITCODE | Should -Not -Be 0 + $stderr = Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue + $stderr | Should -Match "no default source could be found" + } + + Remove-Item -Path $stderrFile -Force -ErrorAction SilentlyContinue + Remove-Item -Path $nonExistentPath -Force -ErrorAction SilentlyContinue + } } } diff --git a/resources/sshdconfig/tests/sshdconfigRepeatList.tests.ps1 b/resources/sshdconfig/tests/sshdconfigRepeatList.tests.ps1 index 1869ea60d..f927cd279 100644 --- a/resources/sshdconfig/tests/sshdconfigRepeatList.tests.ps1 +++ b/resources/sshdconfig/tests/sshdconfigRepeatList.tests.ps1 @@ -33,6 +33,9 @@ Describe 'sshd-config-repeat-list Set Tests' -Skip:($skipTest) { $script:DefaultSftpPath = "/usr/lib/openssh/sftp-server" $script:AlternatePath = "/usr/libexec/sftp-server" } + + $script:DefaultSourceExists = $IsWindows -and + (Test-Path -Path "$env:SystemDrive\Windows\System32\OpenSSH\sshd_config_default" -PathType Leaf -ErrorAction SilentlyContinue) } AfterEach { @@ -320,5 +323,37 @@ PasswordAuthentication yes $subsystems = Get-Content $TestConfigPath | Where-Object { $_ -match '^\s*subsystem\s+' } $subsystems.Count | Should -Be 0 } + + It 'Should seed missing file from default source when available on Windows, or fail otherwise' { + $nonExistentPath = Join-Path $TestDrive "nonexistent_sshd_config" + + $inputConfig = @{ + _metadata = @{ + filepath = $nonExistentPath + } + _purge = $false + subsystem = @( + @{ + name = "powershell" + value = "/usr/bin/pwsh -sshs" + } + ) + } | ConvertTo-Json -Depth 10 + + $stderrFile = Join-Path $TestDrive "stderr_missing_default_repeat_list.txt" + sshdconfig set --input $inputConfig -s sshd-config-repeat-list 2>$stderrFile + + if ($IsWindows -and $script:DefaultSourceExists) { + $LASTEXITCODE | Should -Be 0 + Test-Path $nonExistentPath | Should -Be $true + } else { + $LASTEXITCODE | Should -Not -Be 0 + $stderr = Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue + $stderr | Should -Match "no default source could be found" + } + + Remove-Item -Path $stderrFile -Force -ErrorAction SilentlyContinue + Remove-Item -Path $nonExistentPath -Force -ErrorAction SilentlyContinue + } } } From 509f6eb5e8d1e39215efd3a4613da50bd9eff2c6 Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Thu, 9 Apr 2026 17:05:39 -0400 Subject: [PATCH 2/8] fix error message --- resources/sshdconfig/locales/en-us.toml | 1 + resources/sshdconfig/src/util.rs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/sshdconfig/locales/en-us.toml b/resources/sshdconfig/locales/en-us.toml index a8c4b7e3b..8c2e883a0 100644 --- a/resources/sshdconfig/locales/en-us.toml +++ b/resources/sshdconfig/locales/en-us.toml @@ -101,6 +101,7 @@ cleanupFailed = "Failed to clean up temporary file %{path}: %{error}" getIgnoresInputFilters = "get command does not support filtering based on input settings, provided input will be ignored" seededConfigFromDefault = "Seeded missing sshd_config from '%{source}' to '%{target}'" sshdConfigDefaultNotFound = "sshd_config file does not exist and no default source could be found. Checked: %{paths}" +sshdConfigNotFoundNonWindows = "sshd_config file does not exist. Start sshd once to initialize the configuration file, then retry." sshdConfigReadFailed = "failed to read sshd_config at path: '%{path}'" sshdElevation = "elevated security context required" tempFileCreated = "temporary file created at: %{path}" diff --git a/resources/sshdconfig/src/util.rs b/resources/sshdconfig/src/util.rs index 295396393..4863c08f8 100644 --- a/resources/sshdconfig/src/util.rs +++ b/resources/sshdconfig/src/util.rs @@ -131,8 +131,8 @@ pub fn ensure_sshd_config_exists(input: Option) -> Result) -> Result Date: Thu, 9 Apr 2026 17:06:43 -0400 Subject: [PATCH 3/8] fix clippy --- resources/sshdconfig/src/util.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/resources/sshdconfig/src/util.rs b/resources/sshdconfig/src/util.rs index 4863c08f8..862744bf9 100644 --- a/resources/sshdconfig/src/util.rs +++ b/resources/sshdconfig/src/util.rs @@ -110,10 +110,8 @@ pub fn get_default_sshd_config_path(input: Option) -> Result Vec { let mut candidates: Vec = Vec::new(); - if cfg!(windows) { - if let Ok(system_drive) = std::env::var("SystemDrive") { - candidates.push(PathBuf::from(format!("{system_drive}\\Windows\\System32\\OpenSSH\\sshd_config_default"))); - } + if cfg!(windows) && let Ok(system_drive) = std::env::var("SystemDrive") { + candidates.push(PathBuf::from(format!("{system_drive}\\Windows\\System32\\OpenSSH\\sshd_config_default"))); } candidates From 8884a0a709d5ab0a2bcd81d1a1dcf349af00f9f8 Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Fri, 10 Apr 2026 10:57:10 -0400 Subject: [PATCH 4/8] fix i8n --- resources/sshdconfig/locales/en-us.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/sshdconfig/locales/en-us.toml b/resources/sshdconfig/locales/en-us.toml index 8c2e883a0..eedd2b39e 100644 --- a/resources/sshdconfig/locales/en-us.toml +++ b/resources/sshdconfig/locales/en-us.toml @@ -86,7 +86,6 @@ defaultShellDebug = "default_shell: %{shell}" expectedArrayForKeyword = "Expected array for keyword '%{keyword}'" failedToParse = "failed to parse: '%{input}'" failedToParseDefaultShell = "failed to parse input for DefaultShell with error: '%{error}'" -purgeFalseRequiresExistingFile = "_purge=false requires an existing sshd_config file. Use _purge=true to create a new configuration file." settingDefaultShell = "Setting default shell" settingSshdConfig = "Setting sshd_config" shellPathDoesNotExist = "shell path does not exist: '%{shell}'" From 11d465cb79f2553ef90112f1652dfc33715872af Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Fri, 10 Apr 2026 11:26:31 -0400 Subject: [PATCH 5/8] address copilot feedback --- resources/sshdconfig/locales/en-us.toml | 5 +++-- resources/sshdconfig/src/error.rs | 2 ++ resources/sshdconfig/src/util.rs | 22 ++++++++++++++----- .../sshdconfig/tests/sshdconfig.get.tests.ps1 | 18 --------------- .../sshdconfig/tests/sshdconfig.set.tests.ps1 | 8 ++++++- .../tests/sshdconfigRepeat.tests.ps1 | 8 ++++++- .../tests/sshdconfigRepeatList.tests.ps1 | 8 ++++++- 7 files changed, 43 insertions(+), 28 deletions(-) diff --git a/resources/sshdconfig/locales/en-us.toml b/resources/sshdconfig/locales/en-us.toml index eedd2b39e..ce838b746 100644 --- a/resources/sshdconfig/locales/en-us.toml +++ b/resources/sshdconfig/locales/en-us.toml @@ -11,6 +11,7 @@ inputMustBeBoolean = "value of '%{input}' must be true or false" [error] command = "Command" +configInitRequired = "Configuration File Initialization Required" envVar = "Environment Variable" fileNotFound = "File not found: %{path}" invalidInput = "Invalid Input" @@ -99,8 +100,8 @@ writingTempConfig = "Writing temporary sshd_config file" cleanupFailed = "Failed to clean up temporary file %{path}: %{error}" getIgnoresInputFilters = "get command does not support filtering based on input settings, provided input will be ignored" seededConfigFromDefault = "Seeded missing sshd_config from '%{source}' to '%{target}'" -sshdConfigDefaultNotFound = "sshd_config file does not exist and no default source could be found. Checked: %{paths}" -sshdConfigNotFoundNonWindows = "sshd_config file does not exist. Start sshd once to initialize the configuration file, then retry." +sshdConfigDefaultNotFound = "'%{path}' does not exist and no default source could be found. Checked: %{paths}. Start the sshd service to initialize it, then retry." +sshdConfigNotFoundNonWindows = "'%{path}' does not exist. Start the sshd service to initialize it, then retry." sshdConfigReadFailed = "failed to read sshd_config at path: '%{path}'" sshdElevation = "elevated security context required" tempFileCreated = "temporary file created at: %{path}" diff --git a/resources/sshdconfig/src/error.rs b/resources/sshdconfig/src/error.rs index 52d0d62f5..94a0edbe3 100644 --- a/resources/sshdconfig/src/error.rs +++ b/resources/sshdconfig/src/error.rs @@ -9,6 +9,8 @@ use thiserror::Error; pub enum SshdConfigError { #[error("{t}: {0}", t = t!("error.command"))] CommandError(String), + #[error("{t}: {0}", t = t!("error.configInitRequired"))] + ConfigInitRequired(String), #[error("{t}: {0}", t = t!("error.envVar"))] EnvVarError(#[from] std::env::VarError), #[error("{t}", t = t!("error.fileNotFound", path = .0))] diff --git a/resources/sshdconfig/src/util.rs b/resources/sshdconfig/src/util.rs index 862744bf9..27dae940e 100644 --- a/resources/sshdconfig/src/util.rs +++ b/resources/sshdconfig/src/util.rs @@ -110,8 +110,13 @@ pub fn get_default_sshd_config_path(input: Option) -> Result Vec { let mut candidates: Vec = Vec::new(); - if cfg!(windows) && let Ok(system_drive) = std::env::var("SystemDrive") { - candidates.push(PathBuf::from(format!("{system_drive}\\Windows\\System32\\OpenSSH\\sshd_config_default"))); + if cfg!(windows) && let Ok(win_dir) = std::env::var("windir") { + candidates.push( + PathBuf::from(win_dir) + .join("System32") + .join("OpenSSH") + .join("sshd_config_default"), + ); } candidates @@ -129,8 +134,8 @@ pub fn ensure_sshd_config_exists(input: Option) -> Result) -> Result>() .join(", "); - SshdConfigError::InvalidInput(t!("util.sshdConfigDefaultNotFound", paths = paths).to_string()) + SshdConfigError::ConfigInitRequired( + t!( + "util.sshdConfigDefaultNotFound", + path = target_path.display(), + paths = paths + ) + .to_string(), + ) })?; if let Some(parent) = target_path.parent() { diff --git a/resources/sshdconfig/tests/sshdconfig.get.tests.ps1 b/resources/sshdconfig/tests/sshdconfig.get.tests.ps1 index 78a5599eb..3b73c785b 100644 --- a/resources/sshdconfig/tests/sshdconfig.get.tests.ps1 +++ b/resources/sshdconfig/tests/sshdconfig.get.tests.ps1 @@ -168,22 +168,4 @@ PasswordAuthentication no Remove-Item -Path $stderrFile -Force -ErrorAction SilentlyContinue } - - It 'Should fail when config file does not exist even when default source is missing' { - $nonExistentPath = Join-Path $TestDrive 'nonexistent_sshd_config' - - $inputData = @{ - _metadata = @{ - filepath = $nonExistentPath - } - } | ConvertTo-Json - - $stderrFile = Join-Path $TestDrive "stderr_missing_default_source.txt" - sshdconfig get --input $inputData -s sshd-config 2>$stderrFile - $LASTEXITCODE | Should -Not -Be 0 - - $stderr = Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue - $stderr | Should -Match "File not found" - 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 882766a14..ae20c30ba 100644 --- a/resources/sshdconfig/tests/sshdconfig.set.tests.ps1 +++ b/resources/sshdconfig/tests/sshdconfig.set.tests.ps1 @@ -208,11 +208,17 @@ Describe 'sshd_config Set Tests' -Skip:($skipTest) { } | ConvertTo-Json $result = sshdconfig get --input $getInput -s sshd-config 2>$null | ConvertFrom-Json $result.Port | Should -Be "8888" - } else { + } + elseif ($IsWindows) { $LASTEXITCODE | Should -Not -Be 0 $stderr = Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue $stderr | Should -Match "no default source could be found" } + else { + $LASTEXITCODE | Should -Not -Be 0 + $stderr = Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue + $stderr | Should -Match "does not exist" + } Remove-Item -Path $stderrFile -Force -ErrorAction SilentlyContinue Remove-Item -Path $nonExistentPath -Force -ErrorAction SilentlyContinue diff --git a/resources/sshdconfig/tests/sshdconfigRepeat.tests.ps1 b/resources/sshdconfig/tests/sshdconfigRepeat.tests.ps1 index bd5ee858f..5b1969b4f 100644 --- a/resources/sshdconfig/tests/sshdconfigRepeat.tests.ps1 +++ b/resources/sshdconfig/tests/sshdconfigRepeat.tests.ps1 @@ -236,11 +236,17 @@ PasswordAuthentication yes if ($IsWindows -and $script:DefaultSourceExists) { $LASTEXITCODE | Should -Be 0 Test-Path $nonExistentPath | Should -Be $true - } else { + } + elseif ($IsWindows) { $LASTEXITCODE | Should -Not -Be 0 $stderr = Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue $stderr | Should -Match "no default source could be found" } + else { + $LASTEXITCODE | Should -Not -Be 0 + $stderr = Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue + $stderr | Should -Match "does not exist" + } Remove-Item -Path $stderrFile -Force -ErrorAction SilentlyContinue Remove-Item -Path $nonExistentPath -Force -ErrorAction SilentlyContinue diff --git a/resources/sshdconfig/tests/sshdconfigRepeatList.tests.ps1 b/resources/sshdconfig/tests/sshdconfigRepeatList.tests.ps1 index f927cd279..06033442a 100644 --- a/resources/sshdconfig/tests/sshdconfigRepeatList.tests.ps1 +++ b/resources/sshdconfig/tests/sshdconfigRepeatList.tests.ps1 @@ -346,11 +346,17 @@ PasswordAuthentication yes if ($IsWindows -and $script:DefaultSourceExists) { $LASTEXITCODE | Should -Be 0 Test-Path $nonExistentPath | Should -Be $true - } else { + } + elseif ($IsWindows) { $LASTEXITCODE | Should -Not -Be 0 $stderr = Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue $stderr | Should -Match "no default source could be found" } + else { + $LASTEXITCODE | Should -Not -Be 0 + $stderr = Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue + $stderr | Should -Match "does not exist" + } Remove-Item -Path $stderrFile -Force -ErrorAction SilentlyContinue Remove-Item -Path $nonExistentPath -Force -ErrorAction SilentlyContinue From 75d859ff0ba1d8ce15f478ffba1ece449c1bcd3e Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Tue, 14 Apr 2026 11:27:27 -0400 Subject: [PATCH 6/8] create distinct test cases --- .../sshdconfig/tests/sshdconfig.set.tests.ps1 | 158 ++++++++++++++---- 1 file changed, 129 insertions(+), 29 deletions(-) diff --git a/resources/sshdconfig/tests/sshdconfig.set.tests.ps1 b/resources/sshdconfig/tests/sshdconfig.set.tests.ps1 index ae20c30ba..d5500542f 100644 --- a/resources/sshdconfig/tests/sshdconfig.set.tests.ps1 +++ b/resources/sshdconfig/tests/sshdconfig.set.tests.ps1 @@ -21,9 +21,6 @@ Describe 'sshd_config Set Tests' -Skip:($skipTest) { $TestDir = Join-Path $TestDrive "sshd_test" New-Item -Path $TestDir -ItemType Directory -Force | Out-Null $TestConfigPath = Join-Path $TestDir "sshd_config" - - $script:DefaultSourceExists = $IsWindows -and - (Test-Path -Path "$env:SystemDrive\Windows\System32\OpenSSH\sshd_config_default" -PathType Leaf -ErrorAction SilentlyContinue) } AfterEach { @@ -183,45 +180,148 @@ Describe 'sshd_config Set Tests' -Skip:($skipTest) { sshdconfig set --input $validConfig -s sshd-config } - It 'Should seed missing file from default source when available on Windows, or fail otherwise' { - $nonExistentPath = Join-Path $TestDrive "nonexistent_sshd_config" + Context 'Missing target with _purge=false on non-Windows' -Skip:($IsWindows) { + It 'Should fail when the target file does not exist' { + $nonExistentPath = Join-Path $TestDrive "nonexistent_sshd_config_nonwindows" + $stderrFile = Join-Path $TestDrive "stderr_purgefalse_nofile_nonwindows.txt" + $inputConfig = @{ + _metadata = @{ + filepath = $nonExistentPath + } + _purge = $false + Port = "8888" + } | ConvertTo-Json + + sshdconfig set --input $inputConfig -s sshd-config 2>$stderrFile - $inputConfig = @{ - _metadata = @{ - filepath = $nonExistentPath + $LASTEXITCODE | Should -Not -Be 0 + Test-Path $nonExistentPath | Should -Be $false + (Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue) | Should -Match "does not exist" + + Remove-Item -Path $stderrFile -Force -ErrorAction SilentlyContinue + Remove-Item -Path $nonExistentPath -Force -ErrorAction SilentlyContinue + } + } + + Context 'Missing target with _purge=false on Windows' -Skip:(-not $IsWindows) { + BeforeAll { + $script:WindowsDefaultSourcePath = Join-Path $env:windir "System32\OpenSSH\sshd_config_default" + + function Ensure-WindowsDefaultSourceExists { + $script:CreatedWindowsDefaultSourceDirectories = @() + + if (Test-Path -Path $script:WindowsDefaultSourcePath -PathType Leaf -ErrorAction SilentlyContinue) { + return $false + } + + $defaultSourceDirectory = Split-Path -Path $script:WindowsDefaultSourcePath -Parent + $directoriesToCreate = @() + $pathToCheck = $defaultSourceDirectory + + while (-not (Test-Path -Path $pathToCheck -PathType Container -ErrorAction SilentlyContinue)) { + $directoriesToCreate += $pathToCheck + $parentPath = Split-Path -Path $pathToCheck -Parent + + if ([string]::IsNullOrEmpty($parentPath) -or ($parentPath -eq $pathToCheck)) { + break + } + + $pathToCheck = $parentPath + } + + if (-not (Test-Path -Path $defaultSourceDirectory -PathType Container -ErrorAction SilentlyContinue)) { + New-Item -Path $defaultSourceDirectory -ItemType Directory -Force | Out-Null + } + + $script:CreatedWindowsDefaultSourceDirectories = $directoriesToCreate + + Set-Content -Path $script:WindowsDefaultSourcePath -Value @( + "Port 22", + "PasswordAuthentication yes" + ) -Encoding ascii + + return $true } - _purge = $false - Port = "8888" - } | ConvertTo-Json + } - $stderrFile = Join-Path $TestDrive "stderr_purgefalse_nofile.txt" - sshdconfig set --input $inputConfig -s sshd-config 2>$stderrFile + AfterEach { + Remove-Item -Path $script:CurrentWindowsStderrFile -Force -ErrorAction SilentlyContinue + Remove-Item -Path $script:CurrentWindowsTargetPath -Force -ErrorAction SilentlyContinue + } - if ($IsWindows -and $script:DefaultSourceExists) { - $LASTEXITCODE | Should -Be 0 - Test-Path $nonExistentPath | Should -Be $true + It 'Should create the target file from the default source' { + $script:CurrentWindowsTargetPath = Join-Path $TestDrive "nonexistent_sshd_config_windows_success" + $script:CurrentWindowsStderrFile = Join-Path $TestDrive "stderr_purgefalse_nofile_windows_success.txt" + $createdWindowsDefaultSourceForTest = Ensure-WindowsDefaultSourceExists + $inputConfig = @{ + _metadata = @{ + filepath = $script:CurrentWindowsTargetPath + } + _purge = $false + Port = "8888" + } | ConvertTo-Json + + sshdconfig set --input $inputConfig -s sshd-config 2>$script:CurrentWindowsStderrFile + $LASTEXITCODE | Should -Be 0 + Test-Path $script:CurrentWindowsTargetPath | Should -Be $true $getInput = @{ _metadata = @{ - filepath = $nonExistentPath + filepath = $script:CurrentWindowsTargetPath } } | ConvertTo-Json $result = sshdconfig get --input $getInput -s sshd-config 2>$null | ConvertFrom-Json $result.Port | Should -Be "8888" + + if ($createdWindowsDefaultSourceForTest) { + Remove-Item -Path $script:WindowsDefaultSourcePath -Force -ErrorAction SilentlyContinue + + foreach ($createdDirectory in $script:CreatedWindowsDefaultSourceDirectories) { + if (Test-Path -Path $createdDirectory -PathType Container -ErrorAction SilentlyContinue) { + $directoryItems = Get-ChildItem -Path $createdDirectory -Force -ErrorAction SilentlyContinue + if (($null -eq $directoryItems) -or ($directoryItems.Count -eq 0)) { + Remove-Item -Path $createdDirectory -Force -ErrorAction SilentlyContinue + } + } + } + } } - elseif ($IsWindows) { - $LASTEXITCODE | Should -Not -Be 0 - $stderr = Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue - $stderr | Should -Match "no default source could be found" - } - else { - $LASTEXITCODE | Should -Not -Be 0 - $stderr = Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue - $stderr | Should -Match "does not exist" - } - Remove-Item -Path $stderrFile -Force -ErrorAction SilentlyContinue - Remove-Item -Path $nonExistentPath -Force -ErrorAction SilentlyContinue + It 'Should fail and leave the target file absent when the default source is temporarily unavailable' { + $script:CurrentWindowsTargetPath = Join-Path $TestDrive "nonexistent_sshd_config_windows_missing_default" + $script:CurrentWindowsStderrFile = Join-Path $TestDrive "stderr_purgefalse_nofile_windows_missing_default.txt" + $windowsDefaultSourceBackupPath = Join-Path (Split-Path -Path $script:WindowsDefaultSourcePath -Parent) ( + "sshd_config_default.dsc-test-backup.{0}" -f ([guid]::NewGuid().ToString('N')) + ) + $windowsDefaultSourceWasPresent = Test-Path -Path $script:WindowsDefaultSourcePath -PathType Leaf -ErrorAction SilentlyContinue + + if ($windowsDefaultSourceWasPresent) { + Move-Item -Path $script:WindowsDefaultSourcePath -Destination $windowsDefaultSourceBackupPath -Force + } + + try { + Test-Path -Path $script:WindowsDefaultSourcePath -PathType Leaf -ErrorAction SilentlyContinue | Should -Be $false + + $inputConfig = @{ + _metadata = @{ + filepath = $script:CurrentWindowsTargetPath + } + _purge = $false + Port = "8888" + } | ConvertTo-Json + + sshdconfig set --input $inputConfig -s sshd-config 2>$script:CurrentWindowsStderrFile + + $LASTEXITCODE | Should -Not -Be 0 + Test-Path $script:CurrentWindowsTargetPath | Should -Be $false + (Get-Content -Path $script:CurrentWindowsStderrFile -Raw -ErrorAction SilentlyContinue) | Should -Match "no default source could be found" + } + finally { + if (Test-Path -Path $windowsDefaultSourceBackupPath -PathType Leaf -ErrorAction SilentlyContinue) { + Move-Item -Path $windowsDefaultSourceBackupPath -Destination $script:WindowsDefaultSourcePath -Force + } + } + } } It 'Should fail with invalid keyword and not modify file' { From f20b82c3c7cfe3922d68b88582dd927d09dcbadf Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Tue, 14 Apr 2026 13:58:01 -0400 Subject: [PATCH 7/8] use mock C:\WINDOWS in tests --- .../sshdconfig/tests/sshdconfig.set.tests.ps1 | 119 +++++++----------- 1 file changed, 47 insertions(+), 72 deletions(-) diff --git a/resources/sshdconfig/tests/sshdconfig.set.tests.ps1 b/resources/sshdconfig/tests/sshdconfig.set.tests.ps1 index d5500542f..6e999bb61 100644 --- a/resources/sshdconfig/tests/sshdconfig.set.tests.ps1 +++ b/resources/sshdconfig/tests/sshdconfig.set.tests.ps1 @@ -205,43 +205,23 @@ Describe 'sshd_config Set Tests' -Skip:($skipTest) { Context 'Missing target with _purge=false on Windows' -Skip:(-not $IsWindows) { BeforeAll { - $script:WindowsDefaultSourcePath = Join-Path $env:windir "System32\OpenSSH\sshd_config_default" + $script:OriginalWinDir = $env:windir + $script:MockWinDir = Join-Path $TestDrive "mock_windir" + New-Item -Path $script:MockWinDir -ItemType Directory -Force | Out-Null + $env:windir = $script:MockWinDir - function Ensure-WindowsDefaultSourceExists { - $script:CreatedWindowsDefaultSourceDirectories = @() - - if (Test-Path -Path $script:WindowsDefaultSourcePath -PathType Leaf -ErrorAction SilentlyContinue) { - return $false - } - - $defaultSourceDirectory = Split-Path -Path $script:WindowsDefaultSourcePath -Parent - $directoriesToCreate = @() - $pathToCheck = $defaultSourceDirectory - - while (-not (Test-Path -Path $pathToCheck -PathType Container -ErrorAction SilentlyContinue)) { - $directoriesToCreate += $pathToCheck - $parentPath = Split-Path -Path $pathToCheck -Parent - - if ([string]::IsNullOrEmpty($parentPath) -or ($parentPath -eq $pathToCheck)) { - break - } - - $pathToCheck = $parentPath - } - - if (-not (Test-Path -Path $defaultSourceDirectory -PathType Container -ErrorAction SilentlyContinue)) { - New-Item -Path $defaultSourceDirectory -ItemType Directory -Force | Out-Null - } - - $script:CreatedWindowsDefaultSourceDirectories = $directoriesToCreate - - Set-Content -Path $script:WindowsDefaultSourcePath -Value @( - "Port 22", - "PasswordAuthentication yes" - ) -Encoding ascii + $script:WindowsDefaultSourcePath = Join-Path $script:MockWinDir "System32\OpenSSH\sshd_config_default" + } - return $true + AfterAll { + if ($null -ne $script:OriginalWinDir) { + $env:windir = $script:OriginalWinDir + } + else { + Remove-Item -Path env:windir -ErrorAction SilentlyContinue } + + Remove-Item -Path $script:MockWinDir -Recurse -Force -ErrorAction SilentlyContinue } AfterEach { @@ -252,7 +232,14 @@ Describe 'sshd_config Set Tests' -Skip:($skipTest) { It 'Should create the target file from the default source' { $script:CurrentWindowsTargetPath = Join-Path $TestDrive "nonexistent_sshd_config_windows_success" $script:CurrentWindowsStderrFile = Join-Path $TestDrive "stderr_purgefalse_nofile_windows_success.txt" - $createdWindowsDefaultSourceForTest = Ensure-WindowsDefaultSourceExists + + $defaultSourceDirectory = Split-Path -Path $script:WindowsDefaultSourcePath -Parent + New-Item -Path $defaultSourceDirectory -ItemType Directory -Force | Out-Null + Set-Content -Path $script:WindowsDefaultSourcePath -Value @( + "Port 22", + "PasswordAuthentication yes" + ) -Encoding ascii + $inputConfig = @{ _metadata = @{ filepath = $script:CurrentWindowsTargetPath @@ -261,7 +248,14 @@ Describe 'sshd_config Set Tests' -Skip:($skipTest) { Port = "8888" } | ConvertTo-Json - sshdconfig set --input $inputConfig -s sshd-config 2>$script:CurrentWindowsStderrFile + $origWinDir = $env:windir + try { + $env:windir = $script:MockWinDir + sshdconfig set --input $inputConfig -s sshd-config 2>$script:CurrentWindowsStderrFile + } + finally { + $env:windir = $origWinDir + } $LASTEXITCODE | Should -Be 0 Test-Path $script:CurrentWindowsTargetPath | Should -Be $true @@ -273,54 +267,35 @@ Describe 'sshd_config Set Tests' -Skip:($skipTest) { $result = sshdconfig get --input $getInput -s sshd-config 2>$null | ConvertFrom-Json $result.Port | Should -Be "8888" - if ($createdWindowsDefaultSourceForTest) { - Remove-Item -Path $script:WindowsDefaultSourcePath -Force -ErrorAction SilentlyContinue - - foreach ($createdDirectory in $script:CreatedWindowsDefaultSourceDirectories) { - if (Test-Path -Path $createdDirectory -PathType Container -ErrorAction SilentlyContinue) { - $directoryItems = Get-ChildItem -Path $createdDirectory -Force -ErrorAction SilentlyContinue - if (($null -eq $directoryItems) -or ($directoryItems.Count -eq 0)) { - Remove-Item -Path $createdDirectory -Force -ErrorAction SilentlyContinue - } - } - } - } + Remove-Item -Path $script:WindowsDefaultSourcePath -Force -ErrorAction SilentlyContinue } It 'Should fail and leave the target file absent when the default source is temporarily unavailable' { $script:CurrentWindowsTargetPath = Join-Path $TestDrive "nonexistent_sshd_config_windows_missing_default" $script:CurrentWindowsStderrFile = Join-Path $TestDrive "stderr_purgefalse_nofile_windows_missing_default.txt" - $windowsDefaultSourceBackupPath = Join-Path (Split-Path -Path $script:WindowsDefaultSourcePath -Parent) ( - "sshd_config_default.dsc-test-backup.{0}" -f ([guid]::NewGuid().ToString('N')) - ) - $windowsDefaultSourceWasPresent = Test-Path -Path $script:WindowsDefaultSourcePath -PathType Leaf -ErrorAction SilentlyContinue - if ($windowsDefaultSourceWasPresent) { - Move-Item -Path $script:WindowsDefaultSourcePath -Destination $windowsDefaultSourceBackupPath -Force - } + Test-Path -Path $script:WindowsDefaultSourcePath -PathType Leaf -ErrorAction SilentlyContinue | Should -Be $false - try { - Test-Path -Path $script:WindowsDefaultSourcePath -PathType Leaf -ErrorAction SilentlyContinue | Should -Be $false - - $inputConfig = @{ - _metadata = @{ - filepath = $script:CurrentWindowsTargetPath - } - _purge = $false - Port = "8888" - } | ConvertTo-Json + $inputConfig = @{ + _metadata = @{ + filepath = $script:CurrentWindowsTargetPath + } + _purge = $false + Port = "8888" + } | ConvertTo-Json + $origWinDir = $env:windir + try { + $env:windir = $script:MockWinDir sshdconfig set --input $inputConfig -s sshd-config 2>$script:CurrentWindowsStderrFile - - $LASTEXITCODE | Should -Not -Be 0 - Test-Path $script:CurrentWindowsTargetPath | Should -Be $false - (Get-Content -Path $script:CurrentWindowsStderrFile -Raw -ErrorAction SilentlyContinue) | Should -Match "no default source could be found" } finally { - if (Test-Path -Path $windowsDefaultSourceBackupPath -PathType Leaf -ErrorAction SilentlyContinue) { - Move-Item -Path $windowsDefaultSourceBackupPath -Destination $script:WindowsDefaultSourcePath -Force - } + $env:windir = $origWinDir } + + $LASTEXITCODE | Should -Not -Be 0 + Test-Path $script:CurrentWindowsTargetPath | Should -Be $false + (Get-Content -Path $script:CurrentWindowsStderrFile -Raw -ErrorAction SilentlyContinue) | Should -Match "no default source could be found" } } From 4ba3c01be16b9d88ff119902497543a706c8d645 Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Tue, 14 Apr 2026 14:19:23 -0400 Subject: [PATCH 8/8] update subsystem tests for distinct test cases --- .../sshdconfig/tests/sshdconfig.set.tests.ps1 | 23 +--- .../tests/sshdconfigRepeat.tests.ps1 | 115 ++++++++++++++--- .../tests/sshdconfigRepeatList.tests.ps1 | 120 +++++++++++++++--- 3 files changed, 199 insertions(+), 59 deletions(-) diff --git a/resources/sshdconfig/tests/sshdconfig.set.tests.ps1 b/resources/sshdconfig/tests/sshdconfig.set.tests.ps1 index 6e999bb61..268e8a055 100644 --- a/resources/sshdconfig/tests/sshdconfig.set.tests.ps1 +++ b/resources/sshdconfig/tests/sshdconfig.set.tests.ps1 @@ -205,25 +205,11 @@ Describe 'sshd_config Set Tests' -Skip:($skipTest) { Context 'Missing target with _purge=false on Windows' -Skip:(-not $IsWindows) { BeforeAll { - $script:OriginalWinDir = $env:windir $script:MockWinDir = Join-Path $TestDrive "mock_windir" New-Item -Path $script:MockWinDir -ItemType Directory -Force | Out-Null - $env:windir = $script:MockWinDir - $script:WindowsDefaultSourcePath = Join-Path $script:MockWinDir "System32\OpenSSH\sshd_config_default" } - AfterAll { - if ($null -ne $script:OriginalWinDir) { - $env:windir = $script:OriginalWinDir - } - else { - Remove-Item -Path env:windir -ErrorAction SilentlyContinue - } - - Remove-Item -Path $script:MockWinDir -Recurse -Force -ErrorAction SilentlyContinue - } - AfterEach { Remove-Item -Path $script:CurrentWindowsStderrFile -Force -ErrorAction SilentlyContinue Remove-Item -Path $script:CurrentWindowsTargetPath -Force -ErrorAction SilentlyContinue @@ -259,18 +245,13 @@ Describe 'sshd_config Set Tests' -Skip:($skipTest) { $LASTEXITCODE | Should -Be 0 Test-Path $script:CurrentWindowsTargetPath | Should -Be $true - $getInput = @{ - _metadata = @{ - filepath = $script:CurrentWindowsTargetPath - } - } | ConvertTo-Json - $result = sshdconfig get --input $getInput -s sshd-config 2>$null | ConvertFrom-Json + $result = sshdconfig get --input $inputConfig -s sshd-config 2>$null | ConvertFrom-Json $result.Port | Should -Be "8888" Remove-Item -Path $script:WindowsDefaultSourcePath -Force -ErrorAction SilentlyContinue } - It 'Should fail and leave the target file absent when the default source is temporarily unavailable' { + It 'Should fail and leave the target file absent when the default source is unavailable' { $script:CurrentWindowsTargetPath = Join-Path $TestDrive "nonexistent_sshd_config_windows_missing_default" $script:CurrentWindowsStderrFile = Join-Path $TestDrive "stderr_purgefalse_nofile_windows_missing_default.txt" diff --git a/resources/sshdconfig/tests/sshdconfigRepeat.tests.ps1 b/resources/sshdconfig/tests/sshdconfigRepeat.tests.ps1 index 156cb8b74..018613248 100644 --- a/resources/sshdconfig/tests/sshdconfigRepeat.tests.ps1 +++ b/resources/sshdconfig/tests/sshdconfigRepeat.tests.ps1 @@ -34,8 +34,6 @@ Describe 'sshd-config-repeat Set Tests' -Skip:($skipTest) { $script:AlternatePath = "/usr/libexec/sftp-server" } - $script:DefaultSourceExists = $IsWindows -and - (Test-Path -Path "$env:SystemDrive\Windows\System32\OpenSSH\sshd_config_default" -PathType Leaf -ErrorAction SilentlyContinue) } AfterEach { @@ -233,10 +231,12 @@ PasswordAuthentication yes $subsystems = Get-Content $TestConfigPath | Where-Object { $_ -match '^\s*subsystem\s+' } $subsystems | Should -Contain "subsystem testExistDefault /path/to/subsystem" } + } - It 'Should seed missing file from default source when available on Windows, or fail otherwise' { - $nonExistentPath = Join-Path $TestDrive "nonexistent_sshd_config" - + Context 'Missing target file on non-Windows' -Skip:($IsWindows) { + It 'Should fail when the target file does not exist' { + $nonExistentPath = Join-Path $TestDrive "nonexistent_sshd_config_repeat_nonwindows" + $stderrFile = Join-Path $TestDrive "stderr_nofile_repeat_nonwindows.txt" $inputConfig = @{ _metadata = @{ filepath = $nonExistentPath @@ -248,26 +248,103 @@ PasswordAuthentication yes } } | ConvertTo-Json - $stderrFile = Join-Path $TestDrive "stderr_missing_default_repeat.txt" sshdconfig set --input $inputConfig -s sshd-config-repeat 2>$stderrFile - if ($IsWindows -and $script:DefaultSourceExists) { - $LASTEXITCODE | Should -Be 0 - Test-Path $nonExistentPath | Should -Be $true + $LASTEXITCODE | Should -Not -Be 0 + Test-Path $nonExistentPath | Should -Be $false + (Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue) | Should -Match "does not exist" + + Remove-Item -Path $stderrFile -Force -ErrorAction SilentlyContinue + Remove-Item -Path $nonExistentPath -Force -ErrorAction SilentlyContinue + } + } + + Context 'Missing target file on Windows' -Skip:(-not $IsWindows) { + BeforeAll { + $script:MockWinDir = Join-Path $TestDrive "mock_windir_repeat" + New-Item -Path $script:MockWinDir -ItemType Directory -Force | Out-Null + $script:WindowsDefaultSourcePath = Join-Path $script:MockWinDir "System32\OpenSSH\sshd_config_default" + } + + AfterEach { + Remove-Item -Path $script:CurrentWindowsStderrFile -Force -ErrorAction SilentlyContinue + Remove-Item -Path $script:CurrentWindowsTargetPath -Force -ErrorAction SilentlyContinue + } + + It 'Should create the target file from the default source' { + $script:CurrentWindowsTargetPath = Join-Path $TestDrive "nonexistent_sshd_config_repeat_windows_success" + $script:CurrentWindowsStderrFile = Join-Path $TestDrive "stderr_nofile_repeat_windows_success.txt" + + $defaultSourceDirectory = Split-Path -Path $script:WindowsDefaultSourcePath -Parent + New-Item -Path $defaultSourceDirectory -ItemType Directory -Force | Out-Null + Set-Content -Path $script:WindowsDefaultSourcePath -Value @( + "Port 22", + "PasswordAuthentication yes" + ) -Encoding ascii + + $inputConfig = @{ + _metadata = @{ + filepath = $script:CurrentWindowsTargetPath + } + _exist = $true + subsystem = @{ + name = "powershell" + value = "$env:ProgramFiles\PowerShell\7\pwsh.exe -sshs -NoLogo -NoProfile" + } + } | ConvertTo-Json + + $origWinDir = $env:windir + try { + $env:windir = $script:MockWinDir + sshdconfig set --input $inputConfig -s sshd-config-repeat 2>$script:CurrentWindowsStderrFile } - elseif ($IsWindows) { - $LASTEXITCODE | Should -Not -Be 0 - $stderr = Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue - $stderr | Should -Match "no default source could be found" + finally { + $env:windir = $origWinDir } - else { - $LASTEXITCODE | Should -Not -Be 0 - $stderr = Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue - $stderr | Should -Match "does not exist" + + $LASTEXITCODE | Should -Be 0 + Test-Path $script:CurrentWindowsTargetPath | Should -Be $true + $getInput = @{ + _metadata = @{ + filepath = $script:CurrentWindowsTargetPath + } + } | ConvertTo-Json + $result = sshdconfig get --input $getInput -s sshd-config 2>$null | ConvertFrom-Json + $result.subsystem.name | Should -Be "powershell" + $result.subsystem.value | Should -Be "$env:ProgramFiles\PowerShell\7\pwsh.exe -sshs -NoLogo -NoProfile" + + Remove-Item -Path $script:WindowsDefaultSourcePath -Force -ErrorAction SilentlyContinue + } + + It 'Should fail and leave the target file absent when the default source is unavailable' { + $script:CurrentWindowsTargetPath = Join-Path $TestDrive "nonexistent_sshd_config_repeat_windows_missing_default" + $script:CurrentWindowsStderrFile = Join-Path $TestDrive "stderr_nofile_repeat_windows_missing_default.txt" + + Test-Path -Path $script:WindowsDefaultSourcePath -PathType Leaf -ErrorAction SilentlyContinue | Should -Be $false + + $inputConfig = @{ + _metadata = @{ + filepath = $script:CurrentWindowsTargetPath + } + _exist = $true + subsystem = @{ + name = "powershell" + value = "$env:ProgramFiles\PowerShell\7\pwsh.exe -sshs -NoLogo -NoProfile" + } + } | ConvertTo-Json + + $origWinDir = $env:windir + try { + $env:windir = $script:MockWinDir + sshdconfig set --input $inputConfig -s sshd-config-repeat 2>$script:CurrentWindowsStderrFile + } + finally { + $env:windir = $origWinDir } - Remove-Item -Path $stderrFile -Force -ErrorAction SilentlyContinue - Remove-Item -Path $nonExistentPath -Force -ErrorAction SilentlyContinue + $LASTEXITCODE | Should -Not -Be 0 + Test-Path $script:CurrentWindowsTargetPath | Should -Be $false + (Get-Content -Path $script:CurrentWindowsStderrFile -Raw -ErrorAction SilentlyContinue) | Should -Match "no default source could be found" } } } diff --git a/resources/sshdconfig/tests/sshdconfigRepeatList.tests.ps1 b/resources/sshdconfig/tests/sshdconfigRepeatList.tests.ps1 index 06033442a..f1123ab3b 100644 --- a/resources/sshdconfig/tests/sshdconfigRepeatList.tests.ps1 +++ b/resources/sshdconfig/tests/sshdconfigRepeatList.tests.ps1 @@ -34,8 +34,6 @@ Describe 'sshd-config-repeat-list Set Tests' -Skip:($skipTest) { $script:AlternatePath = "/usr/libexec/sftp-server" } - $script:DefaultSourceExists = $IsWindows -and - (Test-Path -Path "$env:SystemDrive\Windows\System32\OpenSSH\sshd_config_default" -PathType Leaf -ErrorAction SilentlyContinue) } AfterEach { @@ -323,10 +321,12 @@ PasswordAuthentication yes $subsystems = Get-Content $TestConfigPath | Where-Object { $_ -match '^\s*subsystem\s+' } $subsystems.Count | Should -Be 0 } + } - It 'Should seed missing file from default source when available on Windows, or fail otherwise' { - $nonExistentPath = Join-Path $TestDrive "nonexistent_sshd_config" - + Context 'Missing target file on non-Windows' -Skip:($IsWindows) { + It 'Should fail when the target file does not exist' { + $nonExistentPath = Join-Path $TestDrive "nonexistent_sshd_config_repeatlist_nonwindows" + $stderrFile = Join-Path $TestDrive "stderr_nofile_repeatlist_nonwindows.txt" $inputConfig = @{ _metadata = @{ filepath = $nonExistentPath @@ -340,26 +340,108 @@ PasswordAuthentication yes ) } | ConvertTo-Json -Depth 10 - $stderrFile = Join-Path $TestDrive "stderr_missing_default_repeat_list.txt" sshdconfig set --input $inputConfig -s sshd-config-repeat-list 2>$stderrFile - if ($IsWindows -and $script:DefaultSourceExists) { - $LASTEXITCODE | Should -Be 0 - Test-Path $nonExistentPath | Should -Be $true + $LASTEXITCODE | Should -Not -Be 0 + Test-Path $nonExistentPath | Should -Be $false + (Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue) | Should -Match "does not exist" + + Remove-Item -Path $stderrFile -Force -ErrorAction SilentlyContinue + Remove-Item -Path $nonExistentPath -Force -ErrorAction SilentlyContinue + } + } + + Context 'Missing target file on Windows' -Skip:(-not $IsWindows) { + BeforeAll { + $script:MockWinDir = Join-Path $TestDrive "mock_windir_repeatlist" + New-Item -Path $script:MockWinDir -ItemType Directory -Force | Out-Null + $script:WindowsDefaultSourcePath = Join-Path $script:MockWinDir "System32\OpenSSH\sshd_config_default" + } + + AfterEach { + Remove-Item -Path $script:CurrentWindowsStderrFile -Force -ErrorAction SilentlyContinue + Remove-Item -Path $script:CurrentWindowsTargetPath -Force -ErrorAction SilentlyContinue + } + + It 'Should create the target file from the default source' { + $script:CurrentWindowsTargetPath = Join-Path $TestDrive "nonexistent_sshd_config_repeatlist_windows_success" + $script:CurrentWindowsStderrFile = Join-Path $TestDrive "stderr_nofile_repeatlist_windows_success.txt" + + $defaultSourceDirectory = Split-Path -Path $script:WindowsDefaultSourcePath -Parent + New-Item -Path $defaultSourceDirectory -ItemType Directory -Force | Out-Null + Set-Content -Path $script:WindowsDefaultSourcePath -Value @( + "Port 22", + "PasswordAuthentication yes" + ) -Encoding ascii + + $inputConfig = @{ + _metadata = @{ + filepath = $script:CurrentWindowsTargetPath + } + _purge = $false + subsystem = @( + @{ + name = "powershell" + value = "$env:ProgramFiles\PowerShell\7\pwsh.exe -sshs -NoLogo -NoProfile" + } + ) + } | ConvertTo-Json -Depth 10 + + $origWinDir = $env:windir + try { + $env:windir = $script:MockWinDir + sshdconfig set --input $inputConfig -s sshd-config-repeat-list 2>$script:CurrentWindowsStderrFile } - elseif ($IsWindows) { - $LASTEXITCODE | Should -Not -Be 0 - $stderr = Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue - $stderr | Should -Match "no default source could be found" + finally { + $env:windir = $origWinDir } - else { - $LASTEXITCODE | Should -Not -Be 0 - $stderr = Get-Content -Path $stderrFile -Raw -ErrorAction SilentlyContinue - $stderr | Should -Match "does not exist" + + $LASTEXITCODE | Should -Be 0 + Test-Path $script:CurrentWindowsTargetPath | Should -Be $true + $getInput = @{ + _metadata = @{ + filepath = $script:CurrentWindowsTargetPath + } + } | ConvertTo-Json + $result = sshdconfig get --input $getInput -s sshd-config 2>$null | ConvertFrom-Json + $psEntry = $result.subsystem | Where-Object { $_.name -eq "powershell" } + $psEntry | Should -Not -BeNullOrEmpty + $psEntry.value | Should -Be "$env:ProgramFiles\PowerShell\7\pwsh.exe -sshs -NoLogo -NoProfile" + + Remove-Item -Path $script:WindowsDefaultSourcePath -Force -ErrorAction SilentlyContinue + } + + It 'Should fail and leave the target file absent when the default source is unavailable' { + $script:CurrentWindowsTargetPath = Join-Path $TestDrive "nonexistent_sshd_config_repeatlist_windows_missing_default" + $script:CurrentWindowsStderrFile = Join-Path $TestDrive "stderr_nofile_repeatlist_windows_missing_default.txt" + + Test-Path -Path $script:WindowsDefaultSourcePath -PathType Leaf -ErrorAction SilentlyContinue | Should -Be $false + + $inputConfig = @{ + _metadata = @{ + filepath = $script:CurrentWindowsTargetPath + } + _purge = $false + subsystem = @( + @{ + name = "powershell" + value = "$env:ProgramFiles\PowerShell\7\pwsh.exe -sshs -NoLogo -NoProfile" + } + ) + } | ConvertTo-Json -Depth 10 + + $origWinDir = $env:windir + try { + $env:windir = $script:MockWinDir + sshdconfig set --input $inputConfig -s sshd-config-repeat-list 2>$script:CurrentWindowsStderrFile + } + finally { + $env:windir = $origWinDir } - Remove-Item -Path $stderrFile -Force -ErrorAction SilentlyContinue - Remove-Item -Path $nonExistentPath -Force -ErrorAction SilentlyContinue + $LASTEXITCODE | Should -Not -Be 0 + Test-Path $script:CurrentWindowsTargetPath | Should -Be $false + (Get-Content -Path $script:CurrentWindowsStderrFile -Raw -ErrorAction SilentlyContinue) | Should -Match "no default source could be found" } } }