diff --git a/.github/workflows/cli-arguments.yml b/.github/workflows/cli-arguments.yml new file mode 100644 index 00000000..ba0108bd --- /dev/null +++ b/.github/workflows/cli-arguments.yml @@ -0,0 +1,35 @@ +name: Public CLI unknown argument rejection +on: + push: + branches: ['**'] + pull_request: + workflow_dispatch: +permissions: + contents: read +jobs: + cli-arguments: + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + os: [windows-2022, windows-2025] + engine: [powershell, pwsh] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + - name: Windows PowerShell 5.1 process and native state checks + if: matrix.engine == 'powershell' + shell: powershell + run: ./tests/CliArguments.Tests.ps1 + - name: PowerShell 7 process and native state checks + if: matrix.engine == 'pwsh' + shell: pwsh + run: ./tests/CliArguments.Tests.ps1 + - name: Retain native before and after observations + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 + with: + name: cli-arguments-${{ matrix.os }}-${{ matrix.engine }} + path: ${{ runner.temp }}/wela-cli-arguments.json + if-no-files-found: error + retention-days: 7 diff --git a/CHANGELOG-Japanese.md b/CHANGELOG-Japanese.md index d0e1cea8..ec0710f7 100644 --- a/CHANGELOG-Japanese.md +++ b/CHANGELOG-Japanese.md @@ -4,6 +4,8 @@ **改善:** +- 従来の設定コマンドでも、未対応の `-WhatIf` や入力ミスなどの未認識引数を実行前に拒否するようにしました。`-ErrorAction` や `-Verbose` などの PowerShell 共通パラメーターも拒否するため、自動化ラッパーへの影響をヘルプと診断に明記しました。正しい位置指定引数と文書化された `-DryRun` の動作は維持し、Windows PowerShell 5.1 と PowerShell 7 で公開CLIを検証します。 (@Shirofune-Security) + - Windows PowerShell 5.1 と PowerShell 7、使い捨ての Server 2022/2025 で標準チャネル設定の公開CLIを検証するテストを追加しました。有効化・サイズ・CAPI2読み取り専用権限の適用、既存記述子と大きいバッファーの保持、変更前記録、DryRun、再実行時の無変更、元設定への復元を確認し、ハッシュ付きの証拠を保存します。転送・保存期間・Sigmaの検証は別途必要です。 (@Shirofune-Security) - 完了済みのファイアウォールテキストログ設定を1プロファイルずつ復元する、明示的な `firewall-recovery` を追加しました。元の記録・結果、確認済み計画ハッシュ、実行者・ソース、永続記録と変更前後の確認により、PersistentStore の4項目だけを復元し、強制設定・他のプロファイル・ルールとフィルターを保持します。実効ポリシーを別に報告し、途中失敗を未検証として扱い、自動ロールバックや Sigma 加点は行いません。使い捨て環境の公開 CLI で設定、変更検出、復元、冪等性、完全な後始末を検証します。(関連 #375) (@Shirofune-Security) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08966619..401ee995 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ **Improvements:** +- Reject unbound command-line arguments before dispatch, including unsupported `-WhatIf` and misspelled options on legacy configuration commands. PowerShell common parameters such as `-ErrorAction` and `-Verbose` are also rejected; help and diagnostics explain the automation-wrapper compatibility change. Valid positional arguments and documented `-DryRun` behavior are preserved. Public CLI regressions cover both Windows PowerShell 5.1 and PowerShell 7. (@Shirofune-Security) + - Added disposable Server 2022/2025 validation of public native channel configuration under Windows PowerShell 5.1 and PowerShell 7. Tests apply enable/size controls and the explicit CAPI2 read-only grant, verify descriptor preservation, journals, larger buffers, DryRun and idempotence, and retain hashed native evidence with exact fixture cleanup. Forwarding, retention-duration and Sigma validation remain separate. (@Shirofune-Security) - Added opt-in `firewall-recovery` for one completed firewall text-log operation. Strict original journal/result matching, reviewed plan hashes, native operator/source guards, durable receipts and exact four-field PersistentStore restoration preserve enforcement, other profiles and bounded rule/filter configuration. Effective policy stays separately reported; partial writes remain unverified without automatic rollback or Sigma credit. Disposable public-CLI tests cover configuration, drift refusal, recovery, idempotence and exact cleanup. (Related #375) (@Shirofune-Security) diff --git a/WELA.ps1 b/WELA.ps1 index fd44c7a9..8b5b1fc7 100644 --- a/WELA.ps1 +++ b/WELA.ps1 @@ -1941,6 +1941,10 @@ function Get-WelaUserProfiles { } $usage = @" +WELA.ps1 accepts only its documented script parameters. PowerShell common parameters +(-ErrorAction, -Verbose, -WarningAction, -InformationAction) are not supported. +Remove these options from automation wrappers; check WELA's exit code instead. + Usage: ./WELA.ps1 dns-analytical -Help # Dedicated DNS Server direct-channel lifecycle ./WELA.ps1 wec-runtime -WecRuntimeId subscription-id -ResultsPath new-runtime.json @@ -2231,6 +2235,13 @@ if ($Cmd -ne 'ldap-diagnostics' -and @($PSBoundParameters.Keys | Where-Object { throw 'LDAP options require the dedicated ldap-diagnostics command. No command was run.' } +# Plain scripts retain unknown named options in $args. Check them before every +# dispatch, including the profile shortcut, so an unsupported -WhatIf or typo +# cannot accidentally reach a writer. Keep dedicated option diagnostics above. +if ($args.Count -gt 0) { + throw 'Unsupported trailing arguments. PowerShell common parameters (for example -ErrorAction or -Verbose) are not supported. Check -Help for documented options; no command was run.' +} + if ($Profile -and $Cmd.ToLower() -in @('plan', 'audit', 'audit-settings', 'configure') -and -not $Help) { Invoke-WelaProfileCommand -Command $Cmd.ToLower() return @@ -2688,6 +2699,7 @@ switch ($Cmd.ToLower()) { Write-Host " -BackupPath New directory for the pre-change recovery journal (unique default beside WELA)" Write-Host " -ResultsPath Save structured per-control outcomes as JSON" Write-Host "" + Write-Host "PowerShell common parameters (-ErrorAction, -Verbose, -WarningAction, -InformationAction) are not supported. Remove them from wrappers and check the exit code." Write-Host "Without -Profile, configure applies the YamatoSecurity native logging settings. -Profile applies advanced audit policy and its precedence prerequisite. -DryRun and recovery/results options work with both." Write-Host "" return diff --git a/docs/configuration-results.md b/docs/configuration-results.md index a3551c61..1f700726 100644 --- a/docs/configuration-results.md +++ b/docs/configuration-results.md @@ -18,6 +18,10 @@ or result-file error also exits with status 1. .\WELA.ps1 configure -Auto -ResultsPath .\results.json ``` +Unknown named options and other arguments left unbound by PowerShell are rejected before command dispatch. This includes unsupported `-WhatIf`, `-Confirm` and misspelled `-DryRun` options, even with `-Auto`. Use each command's `-Help` for its supported preview options; `-DryRun` is accepted only where documented. Valid positional binding and PowerShell's unambiguous parameter abbreviations remain supported. + +`WELA.ps1` is a plain PowerShell script and does not accept PowerShell common parameters such as `-ErrorAction`, `-Verbose`, `-WarningAction` or `-InformationAction`. Earlier versions silently ignored those unbound options; they now produce exit code 1 before any command runs, including read-only commands. Remove them from automation wrappers and use WELA's exit code and structured results to check the outcome. The explicitly declared WELA `-Debug` switch remains supported where documented. + Keep the complete WELA directory, including `scripts/Configuration.ps1`. Choose a recovery path whose parent directory is writable only by the operators who manage these settings. The backup directory must not already exist. Without `-BackupPath`, diff --git a/tests/CliArguments.Tests.ps1 b/tests/CliArguments.Tests.ps1 new file mode 100644 index 00000000..9f28df52 --- /dev/null +++ b/tests/CliArguments.Tests.ps1 @@ -0,0 +1,70 @@ +# Public process-boundary regression: no mocked dispatcher or Windows writers. +$ErrorActionPreference = 'Stop' +$repo = Split-Path $PSScriptRoot -Parent +$engine = (Get-Process -Id $PID).Path +$count = 0 +$root = Join-Path ([IO.Path]::GetTempPath()) ('wela-cli-arguments-' + [guid]::NewGuid().ToString('N')) +$null = New-Item -ItemType Directory -Path $root +function Assert($Value, $Message) { if (-not $Value) { throw $Message }; $script:count++ } +function Invoke-Case([string[]]$Arguments, [int]$Expected, [string]$Pattern) { + $prior = $ErrorActionPreference + try { + $ErrorActionPreference = 'Continue' + $output = & $engine -NoLogo -NoProfile -NonInteractive -File "$repo/WELA.ps1" @Arguments 2>&1 | Out-String + $code = $LASTEXITCODE + } finally { $ErrorActionPreference = $prior } + Assert ($code -eq $Expected -and $output -match $Pattern) "Unexpected public CLI exit/output [$code]: $output" +} +$isWindowsHost = [Environment]::OSVersion.Platform -eq [PlatformID]::Win32NT +function Read-NativeState { + $logs = @('Security','System','Application','ForwardedEvents','Microsoft-Windows-CAPI2/Operational') + $state = [ordered]@{ Audit = Get-WelaEffectiveAuditPolicy; Channels = @() } + foreach ($name in $logs) { $state.Channels += Get-WelaNativeChannel $name } + return ($state | ConvertTo-Json -Depth 12 -Compress) +} +try { + if ($isWindowsHost) { + Import-Module "$repo/modules/AuditProfiles.psm1" -Force + Import-Module "$repo/modules/NativeProviders.psm1" -Force + $before = Read-NativeState + } + # These previously reached legacy writers, including the profile fast path. + $commands = @( + @('configure','-Auto'), + @('configure','-Profile','wela-2.2.0','-Auto'), + @('configure-eventlogs','-LogProfile','asd-collector-archive-2021-10','-ApplyLogMode','-Auto'), + @('configure-sacl','-Auto'), + @('channel-settings','-ChannelAction','Configure','-GrantEventLogReaders','-Auto'), + @('powershell-transcription','-TranscriptionAction','Configure','-Auto'), + @('firewall-logging','-FirewallAction','Configure','-Auto'), + @('smb-auditing','-SmbAction','Configure','-Auto'), + @('audit-integrity','-IntegrityAction','Configure','-Auto'), + @('provider-packs','-ProviderAction','Configure','-Auto'), + @('wec-collector','-WefAction','Configure','-Auto'), + @('audit-settings','-Help') + ) + foreach ($command in $commands) { + foreach ($unknown in @('-WhatIf','-DryRnu')) { + Invoke-Case ($command + @('-BackupPath',"$root/journal",'-ResultsPath',"$root/result.json",$unknown)) 1 'Unsupported trailing arguments' + Assert (-not (Test-Path "$root/journal") -and -not (Test-Path "$root/result.json")) 'Rejected arguments must not create journals/results' + } + } + # Unknown argument values are deliberately omitted from WELA's diagnostic. + Invoke-Case @('configure','-Auto','-UnrecognizedOption','opaque-value') 1 'Unsupported trailing arguments' + Invoke-Case @('configure','-Help','-WhatIf:$false') 1 'Unsupported trailing arguments' + Invoke-Case @('-WhatIf','configure','-Auto') 1 'Unsupported trailing arguments' + # Preserve documented named/positional binding, help, abbreviations and DryRun. + Invoke-Case @('configure','-Help','-Auto','-DryRun') 0 'Read live state' + Invoke-Case @('-Cmd','configure','-Help') 0 'Usage:' + Invoke-Case @('configure','std','-Help') 0 'Usage:' + Invoke-Case @('configure','-Hel') 0 'Usage:' + Invoke-Case @('profiles') 0 'wela-2.2.0' + Invoke-Case @('failed-logon-probe','-FailedLogonAction','Run','-WhatIf') 1 'only dedicated' + if ($isWindowsHost) { + Assert ((Read-NativeState) -ceq $before) 'Actual audit masks and native channel settings must remain unchanged' + $evidence = [ordered]@{ Status='Passed'; Engine=$PSVersionTable.PSVersion.ToString(); OS=[Environment]::OSVersion.Version.ToString(); StateUnchanged=$true; Before=($before|ConvertFrom-Json); After=((Read-NativeState)|ConvertFrom-Json) } + if ($env:RUNNER_TEMP) { $evidence | ConvertTo-Json -Depth 16 | Set-Content (Join-Path $env:RUNNER_TEMP 'wela-cli-arguments.json') -Encoding UTF8 } + } + Write-Host "PASS: $count public CLI argument assertions." +} finally { Remove-Item -LiteralPath $root -Recurse -Force } +$global:LASTEXITCODE = 0 diff --git a/website/docs/resources/changelog.ja.md b/website/docs/resources/changelog.ja.md index 708d6ab9..92a34bc1 100644 --- a/website/docs/resources/changelog.ja.md +++ b/website/docs/resources/changelog.ja.md @@ -7,6 +7,8 @@ **改善:** +- 従来の設定コマンドでも、未対応の `-WhatIf` や入力ミスなどの未認識引数を実行前に拒否するようにしました。`-ErrorAction` や `-Verbose` などの PowerShell 共通パラメーターも拒否するため、自動化ラッパーへの影響をヘルプと診断に明記しました。正しい位置指定引数と文書化された `-DryRun` の動作は維持し、Windows PowerShell 5.1 と PowerShell 7 で公開CLIを検証します。 (@Shirofune-Security) + - Windows PowerShell 5.1 と PowerShell 7、使い捨ての Server 2022/2025 で標準チャネル設定の公開CLIを検証するテストを追加しました。有効化・サイズ・CAPI2読み取り専用権限の適用、既存記述子と大きいバッファーの保持、変更前記録、DryRun、再実行時の無変更、元設定への復元を確認し、ハッシュ付きの証拠を保存します。転送・保存期間・Sigmaの検証は別途必要です。 (@Shirofune-Security) - 完了済みのファイアウォールテキストログ設定を1プロファイルずつ復元する、明示的な `firewall-recovery` を追加しました。元の記録・結果、確認済み計画ハッシュ、実行者・ソース、永続記録と変更前後の確認により、PersistentStore の4項目だけを復元し、強制設定・他のプロファイル・ルールとフィルターを保持します。実効ポリシーを別に報告し、途中失敗を未検証として扱い、自動ロールバックや Sigma 加点は行いません。使い捨て環境の公開 CLI で設定、変更検出、復元、冪等性、完全な後始末を検証します。(関連 #375) (@Shirofune-Security) diff --git a/website/docs/resources/changelog.md b/website/docs/resources/changelog.md index 49cf7543..69f307a3 100644 --- a/website/docs/resources/changelog.md +++ b/website/docs/resources/changelog.md @@ -7,6 +7,8 @@ **Improvements:** +- Reject unbound command-line arguments before dispatch, including unsupported `-WhatIf` and misspelled options on legacy configuration commands. PowerShell common parameters such as `-ErrorAction` and `-Verbose` are also rejected; help and diagnostics explain the automation-wrapper compatibility change. Valid positional arguments and documented `-DryRun` behavior are preserved. Public CLI regressions cover both Windows PowerShell 5.1 and PowerShell 7. (@Shirofune-Security) + - Added disposable Server 2022/2025 validation of public native channel configuration under Windows PowerShell 5.1 and PowerShell 7. Tests apply enable/size controls and the explicit CAPI2 read-only grant, verify descriptor preservation, journals, larger buffers, DryRun and idempotence, and retain hashed native evidence with exact fixture cleanup. Forwarding, retention-duration and Sigma validation remain separate. (@Shirofune-Security) - Added opt-in `firewall-recovery` for one completed firewall text-log operation. Strict original journal/result matching, reviewed plan hashes, native operator/source guards, durable receipts and exact four-field PersistentStore restoration preserve enforcement, other profiles and bounded rule/filter configuration. Effective policy stays separately reported; partial writes remain unverified without automatic rollback or Sigma credit. Disposable public-CLI tests cover configuration, drift refusal, recovery, idempotence and exact cleanup. (Related #375) (@Shirofune-Security)