Match rule channel patterns consistently against concrete sources

This commit is contained in:
Shirofune-Security committed 2026-09-19 04:37:02 +09:00
1 parent 6f1ad9d93b
commit 70e6207a84
8 files changed
+34 -12

No files matched your search

+1 -1
View File
@@ -14,7 +14,7 @@
**バグ修正:**
- Windows標準チャネルを一律に`Enabled`と表示していた処理を、実際の有効状態・ログモード・ACLの読み取りとプロバイダーの前提条件の確認に置き換えた。AppLocker、NTLM、Defenderなどのイベント生成は検証されるまで条件付きとし、チャネルが有効なだけではルールを利用可能と判定しない。アクセス拒否・未登録の状態とソースの確認結果を保持するJSON/HTML監査レポート出力を追加した。 (#395) (@Shirofune-Security)
- Windows標準チャネルを一律に`Enabled`と表示していた処理を、実際の有効状態・ログモード・ACLの読み取りとプロバイダーの前提条件の確認に置き換えた。AppLocker、NTLM、Defenderなどのイベント生成は検証されるまで条件付きとし、チャネルが有効なだけではルールを利用可能と判定しない。アクセス拒否・未登録の状態とソースの確認結果を保持するJSON/HTML監査レポート出力を追加した。ルールのチャネルパターンを具体的なカタログのチャネル名と照合する処理を、ルールの絞り込みとソースの対応付けで統一した。 (#395) (@Shirofune-Security)
- `configure`が既定で送信NTLM認証をブロックしていた問題を修正した。未設定またはAllow allの場合はAudit all (`RestrictSendingNTLMTraffic=1`)を設定し、既存のDeny all (`2`)や不明な値・型は維持する。拒否設定を明示的に監査へ変更するには`-OutgoingNtlmMode Audit`、ブロックを有効にするには`Deny`を指定する。書き込み前にポリシーを再確認し、変更後の値の検証、失敗の報告、確認できたポリシー状態と取得可能な最終適用RSoP情報の表示に対応した。 (#388) (@Shirofune-Security)
- `audit-settings`でホストの役割に適用されない監査ポリシーを`Not applicable`と表示し、カテゴリの有効・無効の集計から除外するようにした。NTLMポリシーの値は、DWORD型で保存されている場合にのみ有効な設定値として解釈・検証する。 (#392) (@Shirofune-Security)
- 設定時に外部コマンドの終了コードと変更後の設定値を確認し、処理の終了前にも再確認するようにした。書き込み失敗、設定の未反映、CAサービスの再起動失敗、最終確認時の設定の不一致を明示的に報告し、一律に成功とせず、0以外の終了コードを返すようにした。 (#392) (@Shirofune-Security)
+1 -1
View File
@@ -16,7 +16,7 @@
**Bug Fixes:**
- Replaced static native-channel `Enabled` claims with actual channel state, mode and ACL reads plus provider prerequisite observations. AppLocker, NTLM, Defender and other native sources remain conditional until event generation is validated; channel enablement alone grants no usable-rule credit. Added JSON/HTML audit assessment exports preserving denied/absent states and source evidence. (#395) (@Shirofune-Security)
- Replaced static native-channel `Enabled` claims with actual channel state, mode and ACL reads plus provider prerequisite observations. AppLocker, NTLM, Defender and other native sources remain conditional until event generation is validated; channel enablement alone grants no usable-rule credit. Added JSON/HTML audit assessment exports preserving denied/absent states and source evidence. Rule channel patterns now match concrete catalog channels consistently during filtering and source mapping. (#395) (@Shirofune-Security)
- Fixed `configure` enabling outgoing NTLM blocking by default. It now sets Audit all (`RestrictSendingNTLMTraffic=1`) for unset or Allow policies while preserving existing Deny all (`2`) and unknown values/types. Use `-OutgoingNtlmMode Audit` to explicitly replace a deny policy, or `Deny` to enable blocking. Configuration rechecks policy before writing, verifies changes, reports failures, and displays the observed policy and available last-applied RSoP information. (#388) (@Shirofune-Security)
- `audit-settings` now reports role-inapplicable audit policies as `Not applicable` and excludes them from category enablement totals. NTLM policy values are interpreted and verified only when stored as DWORDs. (#392) (@Shirofune-Security)
- Configuration now checks native command exit codes, verifies settings after applying changes, and checks them again before finishing. Failed writes, ineffective changes, CA restart failures and settings that no longer match at the final check produce explicit results and a nonzero exit code instead of unconditional success. (#392) (@Shirofune-Security)
+3 -1
View File
@@ -172,7 +172,9 @@ function RuleFilter {
$hasCriteria = $true
if (-not ($rule.channel | Where-Object {
$ruleChannel = $_
$category_channels | Where-Object { $ruleChannel -like $_ }
# Catalog channels are concrete names/aliases; rule channels are patterns,
# matching the convention used by Get-WelaNativeSources.
$category_channels | Where-Object { $_ -like $ruleChannel }
})) {
return $false
}
+2 -2
View File
@@ -854,7 +854,7 @@
"type": "filter",
"eventIds": [],
"channels": [
"Microsoft-Windows-Security-Mitigations*"
"Microsoft-Windows-Security-Mitigations/KernelMode"
],
"guid": ""
},
@@ -874,7 +874,7 @@
"type": "filter",
"eventIds": [],
"channels": [
"Microsoft-Windows-Security-Mitigations*"
"Microsoft-Windows-Security-Mitigations/UserMode"
],
"guid": ""
},
+1 -1
View File
@@ -21,7 +21,7 @@ The JSON and self-contained HTML reports preserve channel state, log mode, secur
| Provider `Unknown` | A required provider/status query was unavailable or failed. The channel observation is still retained separately. |
| Domain NTLM `Not applicable` | The host is confirmed not to be a domain controller. An unreadable role remains unknown. |
Each exact channel is read independently. An enabled AppLocker EXE/DLL channel does not hide a disabled MSI/Script channel. PowerShell's `pwsh` metadata alias for classic event 400 maps to `Windows PowerShell`; it does not prove PowerShell 7 logging. The Security-Mitigations catalog now reads separate KernelMode and UserMode channels, preserving the existing wildcard rule selector without issuing broad wildcard channel queries.
Each exact channel is read independently. An enabled AppLocker EXE/DLL channel does not hide a disabled MSI/Script channel. PowerShell's `pwsh` metadata alias for classic event 400 maps to `Windows PowerShell`; it does not prove PowerShell 7 logging. Catalog selectors use concrete channel names or metadata aliases. In both filtering and source mapping, rule channels are patterns matched against these concrete names. The Security-Mitigations catalog reads separate KernelMode and UserMode channels, mapping the existing wildcard rules to both without issuing broad wildcard channel queries or mapping a channel-specific rule to its sibling.
## Provider evidence and coverage limits
+24 -4
View File
@@ -83,6 +83,11 @@ $script:ScriptRoot = Join-Path ([IO.Path]::GetTempPath()) ('wela-native-provider
$null = New-Item -ItemType Directory -Path $script:ScriptRoot
$script:SecurityRulesPath = Join-Path $script:ScriptRoot 'rules.json'
try {
# Exercise the real corpus's rule-side wildcard against the exact catalog
# selectors, through the public CSV/JSON/HTML assessment path.
$wildcardRules = @(Get-Content -LiteralPath (Join-Path $PSScriptRoot '../config/security_rules.json') -Raw |
ConvertFrom-Json | Where-Object { $_.channel -contains 'Microsoft-Windows-Security-Mitigations*' })
Assert-Equal ($wildcardRules.Count -gt 0) $true 'Real Security-Mitigations wildcard rules are present'
$fixtures = @(
@{ id = 'application'; channel = @('Application') }
@{ id = 'print'; channel = @('Microsoft-Windows-PrintService/Operational') }
@@ -97,6 +102,9 @@ try {
@{ id = 'mitigation-user'; channel = @('Microsoft-Windows-Security-Mitigations/UserMode') }
@{ id = 'security'; channel = @('sec'); subcategory_guids = @('0CCE9215-69AE-11D9-BED3-505054503030') }
)
foreach ($rule in $wildcardRules) {
$fixtures += @{ id = $rule.id; channel = @($rule.channel); event_ids = @($rule.event_ids) }
}
foreach ($rule in $fixtures) {
$rule.level = 'medium'; $rule.title = $rule.id
if (-not $rule.ContainsKey('event_ids')) { $rule.event_ids = @() }
@@ -154,13 +162,25 @@ try {
$classic = $sources | Where-Object { $_.Channel.Name -eq 'Windows PowerShell' }
Assert-Equal ($classic.MappedRuleIds -contains 'classic') $true 'Classic PowerShell alias maps to its real channel'
$kernel = $sources | Where-Object { $_.Channel.Name -eq 'Microsoft-Windows-Security-Mitigations/KernelMode' }
Assert-Equal ($kernel.MappedRuleIds -contains 'mitigation-kernel') $true 'Exact mitigation channel matches existing wildcard catalog selector'
$user = $sources | Where-Object { $_.Channel.Name -eq 'Microsoft-Windows-Security-Mitigations/UserMode' }
Assert-Equal ($kernel.MappedRuleIds -contains 'mitigation-kernel') $true 'Exact mitigation channel matches its concrete catalog selector'
Assert-Equal ($kernel.MappedRuleIds -contains 'mitigation-user') $false 'Kernel mitigation source does not inherit user-mode rules'
Assert-Equal (($rows | Where-Object { $_.NativeSources.Channel.Name -contains 'Microsoft-Windows-Security-Mitigations/KernelMode' }).RuleCount) 1 'Category count uses actual source mappings, not a sibling wildcard match'
Assert-Equal $report.Coverage.TotalRules 12 'Whole rule corpus remains in coverage denominator'
Assert-Equal ($user.MappedRuleIds -contains 'mitigation-user') $true 'User mitigation source retains its exact rule'
Assert-Equal ($user.MappedRuleIds -contains 'mitigation-kernel') $false 'User mitigation source does not inherit kernel rules'
foreach ($rule in $wildcardRules) {
Assert-Equal ($kernel.MappedRuleIds -contains $rule.id) $true 'Rule-side wildcard maps to the concrete KernelMode source'
Assert-Equal ($user.MappedRuleIds -contains $rule.id) $true 'Rule-side wildcard maps to the concrete UserMode source'
Assert-Equal (RuleFilter $rule @('999') @($kernel.Channel.Name) '') $false 'A wildcard channel match still requires the selected event ID'
Assert-Equal (RuleFilter $rule @($rule.event_ids[0]) @($kernel.Channel.Name) '') $true 'Matching channel and event ID pass together'
}
foreach ($source in @($kernel, $user)) {
Assert-Equal (($rows | Where-Object { $_.NativeSources.Channel.Name -contains $source.Channel.Name }).RuleCount) (1 + $wildcardRules.Count) 'Each category includes exact and wildcard rules without unrelated sibling rules'
}
Assert-Equal @(& $module { $script:channelReads | Where-Object { $_ -match '[*?]' } }).Count 0 'Wildcard rules never trigger wildcard native channel reads'
Assert-Equal $report.Coverage.TotalRules $fixtures.Count 'Rules mapped to both native sources appear once in the full denominator'
Assert-Equal $report.Coverage.UsableRules 1 'Only the independently enabled Security source receives usable credit'
Assert-Equal @(Import-Csv (Join-Path $script:ScriptRoot 'UsableRules.csv')).Count 1 'Usable CSV matches conservative JSON coverage'
Assert-Equal @(Import-Csv (Join-Path $script:ScriptRoot 'UnusableRules.csv')).Count 11 'Unconfirmed native rules are retained in CSV'
Assert-Equal @(Import-Csv (Join-Path $script:ScriptRoot 'UnusableRules.csv')).Count ($fixtures.Count - 1) 'Unconfirmed native rules are retained once in CSV'
Assert-Equal @($script:heatmapRules | Where-Object { $_.id -ne 'security' -and ($_.applicable -or $_.ideal) }).Count 0 'No current or ideal native provider uplift is fabricated'
$html = Get-Content -LiteralPath $htmlPath -Raw
Assert-Equal ($html -match 'Not installed') $true 'HTML retains absent-feature state'
+1 -1
View File
@@ -17,7 +17,7 @@
**バグ修正:**
- Windows標準チャネルを一律に`Enabled`と表示していた処理を、実際の有効状態・ログモード・ACLの読み取りとプロバイダーの前提条件の確認に置き換えた。AppLocker、NTLM、Defenderなどのイベント生成は検証されるまで条件付きとし、チャネルが有効なだけではルールを利用可能と判定しない。アクセス拒否・未登録の状態とソースの確認結果を保持するJSON/HTML監査レポート出力を追加した。 (#395) (@Shirofune-Security)
- Windows標準チャネルを一律に`Enabled`と表示していた処理を、実際の有効状態・ログモード・ACLの読み取りとプロバイダーの前提条件の確認に置き換えた。AppLocker、NTLM、Defenderなどのイベント生成は検証されるまで条件付きとし、チャネルが有効なだけではルールを利用可能と判定しない。アクセス拒否・未登録の状態とソースの確認結果を保持するJSON/HTML監査レポート出力を追加した。ルールのチャネルパターンを具体的なカタログのチャネル名と照合する処理を、ルールの絞り込みとソースの対応付けで統一した。 (#395) (@Shirofune-Security)
- `configure`が既定で送信NTLM認証をブロックしていた問題を修正した。未設定またはAllow allの場合はAudit all (`RestrictSendingNTLMTraffic=1`)を設定し、既存のDeny all (`2`)や不明な値・型は維持する。拒否設定を明示的に監査へ変更するには`-OutgoingNtlmMode Audit`、ブロックを有効にするには`Deny`を指定する。書き込み前にポリシーを再確認し、変更後の値の検証、失敗の報告、確認できたポリシー状態と取得可能な最終適用RSoP情報の表示に対応した。 (#388) (@Shirofune-Security)
- `audit-settings`でホストの役割に適用されない監査ポリシーを`Not applicable`と表示し、カテゴリの有効・無効の集計から除外するようにした。NTLMポリシーの値は、DWORD型で保存されている場合にのみ有効な設定値として解釈・検証する。 (#392) (@Shirofune-Security)
- 設定時に外部コマンドの終了コードと変更後の設定値を確認し、処理の終了前にも再確認するようにした。書き込み失敗、設定の未反映、CAサービスの再起動失敗、最終確認時の設定の不一致を明示的に報告し、一律に成功とせず、0以外の終了コードを返すようにした。 (#392) (@Shirofune-Security)
+1 -1
View File
@@ -19,7 +19,7 @@
**Bug Fixes:**
- Replaced static native-channel `Enabled` claims with actual channel state, mode and ACL reads plus provider prerequisite observations. AppLocker, NTLM, Defender and other native sources remain conditional until event generation is validated; channel enablement alone grants no usable-rule credit. Added JSON/HTML audit assessment exports preserving denied/absent states and source evidence. (#395) (@Shirofune-Security)
- Replaced static native-channel `Enabled` claims with actual channel state, mode and ACL reads plus provider prerequisite observations. AppLocker, NTLM, Defender and other native sources remain conditional until event generation is validated; channel enablement alone grants no usable-rule credit. Added JSON/HTML audit assessment exports preserving denied/absent states and source evidence. Rule channel patterns now match concrete catalog channels consistently during filtering and source mapping. (#395) (@Shirofune-Security)
- Fixed `configure` enabling outgoing NTLM blocking by default. It now sets Audit all (`RestrictSendingNTLMTraffic=1`) for unset or Allow policies while preserving existing Deny all (`2`) and unknown values/types. Use `-OutgoingNtlmMode Audit` to explicitly replace a deny policy, or `Deny` to enable blocking. Configuration rechecks policy before writing, verifies changes, reports failures, and displays the observed policy and available last-applied RSoP information. (#388) (@Shirofune-Security)
- `audit-settings` now reports role-inapplicable audit policies as `Not applicable` and excludes them from category enablement totals. NTLM policy values are interpreted and verified only when stored as DWORDs. (#392) (@Shirofune-Security)
- Configuration now checks native command exit codes, verifies settings after applying changes, and checks them again before finishing. Failed writes, ineffective changes, CA restart failures and settings that no longer match at the final check produce explicit results and a nonzero exit code instead of unconditional success. (#392) (@Shirofune-Security)