diff --git a/Diagnostics/HealthChecker/Analyzer/Get-URLRewriteRule.ps1 b/Diagnostics/HealthChecker/Analyzer/Get-URLRewriteRule.ps1 index 8118a81b07..1cb97c214e 100644 --- a/Diagnostics/HealthChecker/Analyzer/Get-URLRewriteRule.ps1 +++ b/Diagnostics/HealthChecker/Analyzer/Get-URLRewriteRule.ps1 @@ -3,9 +3,10 @@ <# .SYNOPSIS - Pulls out URL Rewrite Rules from the web.config and applicationHost.config file to return a Hashtable of those settings. + Pulls out URL Rewrite Rules from the web.config and applicationHost.config file to return inbound and outbound rules. .DESCRIPTION This is a function that is designed to pull out the URL Rewrite Rules that are set on a location of IIS. + It extracts both inbound rules (from rewrite/rules) and outbound rules (from rewrite/outboundRules). Because you can set it on an individual web.config file or the parent site(s), or the ApplicationHostConfig file for the location We need to check all locations to properly determine what is all set. The ApplicationHostConfig file must be able to be converted to Xml, but the web.config file doesn't. @@ -14,11 +15,12 @@ 2. ApplicationHost.config file for the same location 3. Then move up one level (Default Web Site/mapi -> Default Web Site) and repeat 1 and 2 till no more locations. a. If the 'clear' flag was set at any point, we stop at that location in the process. + b. Inbound and outbound rules track the 'clear' flag independently. 4. Then there is a global setting in the ApplicationHost.config file. #> function Get-URLRewriteRule { [CmdletBinding()] - [OutputType([hashtable])] + [OutputType([PSCustomObject])] param( [Parameter(Mandatory = $true)] [System.Xml.XmlNode]$ApplicationHostConfig, @@ -31,51 +33,82 @@ function Get-URLRewriteRule { begin { Write-Verbose "Calling: $($MyInvocation.MyCommand)" $urlRewriteRules = @{} + $urlOutboundRewriteRules = @{} $appHostConfigLocations = $ApplicationHostConfig.configuration.Location.path } process { foreach ($key in $WebConfigContent.Keys) { Write-Verbose "Working on key: $key" $continue = $true - $clear = $false + $clearInbound = $false + $clearOutbound = $false $currentKey = $key $urlRewriteRules.Add($key, (New-Object System.Collections.Generic.List[object])) + $urlOutboundRewriteRules.Add($key, (New-Object System.Collections.Generic.List[object])) do { Write-Verbose "Working on currentKey: $currentKey" try { # the Web.config is looked at first [xml]$content = $WebConfigContent[$currentKey] - $rules = $content.configuration.'system.webServer'.rewrite.rules - if ($null -ne $rules) { - $clear = $null -ne $rules.clear - $urlRewriteRules[$key].Add($rules) - } else { - Write-Verbose "No rewrite rules in the config file" + if (-not $clearInbound) { + $rules = $content.configuration.'system.webServer'.rewrite.rules + + if ($null -ne $rules) { + $clearInbound = $null -ne $rules.clear + $urlRewriteRules[$key].Add($rules) + } else { + Write-Verbose "No inbound rewrite rules in the config file" + } + } + + if (-not $clearOutbound) { + $outboundRules = $content.configuration.'system.webServer'.rewrite.outboundRules + + if ($null -ne $outboundRules) { + $clearOutbound = $null -ne $outboundRules.clear + $urlOutboundRewriteRules[$key].Add($outboundRules) + } else { + Write-Verbose "No outbound rewrite rules in the config file" + } } } catch { Write-Verbose "Failed to convert to xml" Invoke-CatchActions } - if (-not $clear) { + if (-not $clearInbound -or -not $clearOutbound) { # Now need to look at the applicationHost.config file to determine what is set at that location. # need to do this because of the case sensitive query to get the xmlNode - Write-Verbose "clear not set on config. Looking at the applicationHost.config file" + Write-Verbose "Looking at the applicationHost.config file" $appKey = $appHostConfigLocations | Where-Object { $_ -eq $currentKey } if ($appKey.Count -eq 1) { $location = $ApplicationHostConfig.SelectNodes("/configuration/location[@path = '$appKey']") if ($null -ne $location) { - $rules = $location.'system.webServer'.rewrite.rules - if ($null -ne $rules) { - $clear = $null -ne $rules.clear - $urlRewriteRules[$key].Add($rules) - } else { - Write-Verbose 'No rewrite rules in the applicationHost.config file' + if (-not $clearInbound) { + $rules = $location.'system.webServer'.rewrite.rules + + if ($null -ne $rules) { + $clearInbound = $null -ne $rules.clear + $urlRewriteRules[$key].Add($rules) + } else { + Write-Verbose "No inbound rewrite rules in the applicationHost.config file" + } + } + + if (-not $clearOutbound) { + $outboundRules = $location.'system.webServer'.rewrite.outboundRules + + if ($null -ne $outboundRules) { + $clearOutbound = $null -ne $outboundRules.clear + $urlOutboundRewriteRules[$key].Add($outboundRules) + } else { + Write-Verbose "No outbound rewrite rules in the applicationHost.config file" + } } } else { Write-Verbose "We didn't find the location for '$appKey' in the applicationHostConfig. This shouldn't occur." @@ -85,21 +118,34 @@ function Get-URLRewriteRule { } } - if ($clear) { - Write-Verbose "Clear was set, don't need to know what else was set." + if ($clearInbound -and $clearOutbound) { + Write-Verbose "Clear was set for both inbound and outbound, don't need to know what else was set." $continue = $false } else { $index = $currentKey.LastIndexOf("/") if ($index -eq -1) { $continue = $false - # look at the global configuration of the applicationHost.config file - $rules = $ApplicationHostConfig.configuration.'system.webServer'.rewrite.rules - if ($null -ne $rules) { - $urlRewriteRules[$key].Add($rules) - } else { - Write-Verbose "No global configuration for rewrite rules." + if (-not $clearInbound) { + # look at the global configuration of the applicationHost.config file + $rules = $ApplicationHostConfig.configuration.'system.webServer'.rewrite.rules + + if ($null -ne $rules) { + $urlRewriteRules[$key].Add($rules) + } else { + Write-Verbose "No global configuration for inbound rewrite rules." + } + } + + if (-not $clearOutbound) { + $outboundRules = $ApplicationHostConfig.configuration.'system.webServer'.rewrite.outboundRules + + if ($null -ne $outboundRules) { + $urlOutboundRewriteRules[$key].Add($outboundRules) + } else { + Write-Verbose "No global configuration for outbound rewrite rules." + } } } else { $currentKey = $currentKey.Substring(0, $index) @@ -111,6 +157,9 @@ function Get-URLRewriteRule { } } end { - return $urlRewriteRules + return [PSCustomObject]@{ + Inbound = $urlRewriteRules + Outbound = $urlOutboundRewriteRules + } } } diff --git a/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerIISInformation.ps1 b/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerIISInformation.ps1 index a403bea7e6..e0e1cc14c7 100644 --- a/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerIISInformation.ps1 +++ b/Diagnostics/HealthChecker/Analyzer/Invoke-AnalyzerIISInformation.ps1 @@ -416,10 +416,12 @@ function Invoke-AnalyzerIISInformation { WebConfigContent = $iisWebConfigContent } - $urlRewriteRules = $null + $urlRewriteResult = $null $ipFilterSettings = $null $authTypeSettings = $null - Get-URLRewriteRule @ruleParams | Invoke-RemotePipelineHandler -Result ([ref]$urlRewriteRules) + Get-URLRewriteRule @ruleParams | Invoke-RemotePipelineHandler -Result ([ref]$urlRewriteResult) + $urlRewriteRules = $urlRewriteResult.Inbound + $urlOutboundRewriteRules = $urlRewriteResult.Outbound Get-IPFilterSetting -ApplicationHostConfig ([xml]$applicationHostConfig) | Invoke-RemotePipelineHandler -Result ([ref]$ipFilterSettings) Get-IISAuthenticationType -ApplicationHostConfig ([xml]$applicationHostConfig) | Invoke-RemotePipelineHandler -Result ([ref]$authTypeSettings) $failedLocationsForAuth = @() @@ -440,6 +442,7 @@ function Invoke-AnalyzerIISInformation { $epValue = "None" $ep = $extendedProtectionConfiguration | Where-Object { $_.VirtualDirectoryName -eq $location.Path } $currentRewriteRules = $urlRewriteRules[$location.Path] + $currentOutboundRewriteRules = $urlOutboundRewriteRules[$location.Path] $authentication = $authTypeSettings[$location.Path] if ($currentRewriteRules.Count -ne 0) { @@ -455,7 +458,23 @@ function Invoke-AnalyzerIISInformation { } $displayRewriteRules = ($currentRewriteRules.rule | Where-Object { $_.enabled -ne "false" }).name | - Where-Object { $_ -notcontains $excludeRules } + Where-Object { $_ -notin $excludeRules } + } + + $displayOutboundRewriteRules = [string]::Empty + + if ($currentOutboundRewriteRules.Count -ne 0) { + $excludeOutboundRules = @() + foreach ($rule in $currentOutboundRewriteRules) { + $remove = $rule.Remove + + if ($null -ne $remove) { + $excludeOutboundRules += $remove.Name + } + } + + $displayOutboundRewriteRules = ($currentOutboundRewriteRules.rule | Where-Object { $_.enabled -ne "false" }).name | + Where-Object { $_ -notin $excludeOutboundRules } } if ($null -ne $ep) { @@ -484,7 +503,8 @@ function Invoke-AnalyzerIISInformation { ExtendedProtection = $epValue SslFlags = $sslFlag IPFilteringEnabled = $ipFilterEnabled - URLRewrite = $displayRewriteRules + InURLRewrite = $displayRewriteRules + OutURLRewrite = $displayOutboundRewriteRules Authentication = $authentication }) } @@ -635,6 +655,7 @@ function Invoke-AnalyzerIISInformation { IndentSpaces = 8 }) AddHtmlDetailRow = $false + TestingName = "Inbound URL Rewrite Rules" } Add-AnalyzedResultInformation @params @@ -651,6 +672,68 @@ function Invoke-AnalyzerIISInformation { } } + # Display Outbound URL Rewrite Rules. + # Same deduplication pattern as inbound - don't display rules on multiple vDirs by same name. + $alreadyDisplayedOutboundRules = @{} + $outboundDisplayKey = "DisplayKey" + $alreadyDisplayedOutboundRules.Add($outboundDisplayKey, (New-Object System.Collections.Generic.List[object])) + + foreach ($key in $urlOutboundRewriteRules.Keys) { + $currentSection = $urlOutboundRewriteRules[$key] + + if ($currentSection.Count -ne 0) { + foreach ($rule in $currentSection.rule) { + + if ($null -eq $rule) { + Write-Verbose "Outbound rule is NULL skipping." + continue + } elseif ($rule.enabled -eq "false") { + Write-Verbose "skipping over disabled outbound rule: $($rule.Name) for vDir '$key'" + continue + } + + $displayObject = [PSCustomObject]@{ + RewriteRuleName = $rule.name + ServerVariable = $rule.match.serverVariable + MatchPattern = $rule.match.pattern + PreCondition = $rule.preCondition + ActionType = $rule.action.type + } + + if (-not ($alreadyDisplayedOutboundRules.ContainsKey((($displayObject.RewriteRuleName))))) { + $alreadyDisplayedOutboundRules.Add($displayObject.RewriteRuleName, $displayObject) + $alreadyDisplayedOutboundRules[$outboundDisplayKey].Add($displayObject) + } + } + } + } + + if ($alreadyDisplayedOutboundRules[$outboundDisplayKey].Count -gt 0) { + $params = $baseParams + @{ + OutColumns = ([PSCustomObject]@{ + DisplayObject = $alreadyDisplayedOutboundRules[$outboundDisplayKey] + IndentSpaces = 8 + }) + AddHtmlDetailRow = $false + TestingName = "Outbound URL Rewrite Rules" + } + Add-AnalyzedResultInformation @params + } + + $globalRules = ([xml]$applicationHostConfig).configuration.'system.webServer'.rewrite.globalRules + + if ($null -ne $globalRules -and + $null -ne $globalRules.rule) { + $params = $baseParams + @{ + Name = "Global IIS Rewrite Rules Detected" + Details = "Global URL Rewrite rules are defined in applicationHost.config and apply to all sites on this server." + + "`r`n`t`tReview these rules to ensure they are expected and not interfering with Exchange traffic." + DisplayWriteType = "Yellow" + TestingName = "Global IIS Rewrite Rules" + } + Add-AnalyzedResultInformation @params + } + foreach ($webApp in $iisWebApplications) { if ($correctLocations.ContainsKey($webApp.FriendlyName)) { if ($webApp.PhysicalPath -notlike "*$($correctLocations[$webApp.FriendlyName])") { diff --git a/Diagnostics/HealthChecker/Analyzer/Tests/Get-URLRewriteRule.Tests.ps1 b/Diagnostics/HealthChecker/Analyzer/Tests/Get-URLRewriteRule.Tests.ps1 new file mode 100644 index 0000000000..0029955fed --- /dev/null +++ b/Diagnostics/HealthChecker/Analyzer/Tests/Get-URLRewriteRule.Tests.ps1 @@ -0,0 +1,235 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingInvokeExpression', '', Justification = 'Pester testing file')] +[CmdletBinding()] +param() + +BeforeAll { + . $PSScriptRoot\..\..\..\..\Shared\PesterLoadFunctions.NotPublished.ps1 + $scriptContent = Get-PesterScriptContent -FilePath "$PSScriptRoot\..\Get-URLRewriteRule.ps1" + Invoke-Expression $scriptContent + function Invoke-CatchActions { throw "Called Invoke-CatchActions" } + + $Script:mockDataRoot = "$PSScriptRoot\..\..\Tests\DataCollection\E19\Exchange\IIS" + [xml]$Script:appHost = Get-Content "$Script:mockDataRoot\applicationHost.config" -Raw -Encoding UTF8 + + $Script:webConfigContent = @{ + "Default Web Site" = (Get-Content "$Script:mockDataRoot\DefaultWebSite_web.config" -Raw -Encoding UTF8) + "Default Web Site/owa" = (Get-Content "$Script:mockDataRoot\DefaultWebSite-OWA_web.config" -Raw -Encoding UTF8) + "Default Web Site/mapi" = (Get-Content "$Script:mockDataRoot\DefaultWebSite-MAPI_web.config" -Raw -Encoding UTF8) + "Default Web Site/EWS" = (Get-Content "$Script:mockDataRoot\DefaultWebSite-EWS_web.config" -Raw -Encoding UTF8) + } + + $Script:result = Get-URLRewriteRule -ApplicationHostConfig $Script:appHost -WebConfigContent $Script:webConfigContent +} + +Describe "Get-URLRewriteRule" { + + Context "Rule extraction from web.config" { + + It "Should find inbound rule from Default Web Site web.config" { + $siteRules = $Script:result.Inbound["Default Web Site"] + $allRuleNames = @($siteRules.rule.name | Where-Object { $null -ne $_ }) + $allRuleNames | Should -Contain "CVE-2022-41040 Mitigation" + } + + It "Should collect remove entry from MAPI web.config" { + $mapiRules = $Script:result.Inbound["Default Web Site/mapi"] + # First entry is from web.config which contains the element + $removeNames = @($mapiRules[0].remove.name) + $removeNames | Should -Contain "Global Block Bad User Agents" + } + } + + Context "Rule extraction from applicationHost.config per-location" { + + It "Should find disabled inbound rule from appHost Default Web Site location" { + $siteRules = $Script:result.Inbound["Default Web Site"] + $allRuleNames = @($siteRules.rule.name | Where-Object { $null -ne $_ }) + $allRuleNames | Should -Contain "Disable HTTP - Redirect to HTTPS" + } + + It "Should preserve disabled attribute on appHost rule" { + $siteRules = $Script:result.Inbound["Default Web Site"] + $disabledRule = $siteRules | ForEach-Object { $_.rule } | + Where-Object { $_.name -eq "Disable HTTP - Redirect to HTTPS" } + $disabledRule.enabled | Should -Be "false" + } + } + + Context "Rule extraction from applicationHost.config global section" { + + It "Should find global inbound rule" { + $siteRules = $Script:result.Inbound["Default Web Site"] + $allRuleNames = @($siteRules.rule.name | Where-Object { $null -ne $_ }) + $allRuleNames | Should -Contain "Global Block Bad User Agents" + } + } + + Context "Inheritance walk-up" { + + It "Should collect rules from all 3 levels for Default Web Site" { + # web.config (CVE-2022-41040) + appHost location (disabled HTTPS redirect) + global (Block Bad User Agents) + $Script:result.Inbound["Default Web Site"].Count | Should -Be 3 + } + + It "Should inherit parent and global rules for Default Web Site/owa" { + # OWA web.config has no inbound rules (only outbound) + # OWA appHost location has no inbound rules (only outbound) + # Walks up to Default Web Site: web.config has CVE-2022-41040, appHost has disabled HTTPS redirect + # Then global has Block Bad User Agents + $owaRules = $Script:result.Inbound["Default Web Site/owa"] + $allRuleNames = @($owaRules.rule.name | Where-Object { $null -ne $_ }) + $allRuleNames | Should -Contain "CVE-2022-41040 Mitigation" + $allRuleNames | Should -Contain "Disable HTTP - Redirect to HTTPS" + $allRuleNames | Should -Contain "Global Block Bad User Agents" + } + + It "Should inherit rules for vDir with no rewrite config" { + # EWS web.config has no rewrite section at all + # Should inherit from parent Default Web Site and global + $ewsRules = $Script:result.Inbound["Default Web Site/EWS"] + $allRuleNames = @($ewsRules.rule.name | Where-Object { $null -ne $_ }) + $allRuleNames | Should -Contain "CVE-2022-41040 Mitigation" + $allRuleNames | Should -Contain "Global Block Bad User Agents" + } + } + + Context "Clear stops inheritance" { + + It "Should stop at clear in appHost location for Default Web Site/mapi" { + # MAPI web.config has (collected but no clear) + # MAPI appHost location has which stops inheritance + # Should NOT contain parent Default Web Site rules or global rules + $mapiRules = $Script:result.Inbound["Default Web Site/mapi"] + $mapiRules.Count | Should -Be 2 + $allRuleNames = @($mapiRules.rule.name | Where-Object { $null -ne $_ }) + $allRuleNames | Should -Not -Contain "CVE-2022-41040 Mitigation" + $allRuleNames | Should -Not -Contain "Global Block Bad User Agents" + } + + It "Should stop at clear in web.config and not check appHost or parent" { + # Edge case: clear in web.config is a different code path (line 52) than clear in appHost (line 75) + # Our mock data only has clear in appHost, so use inline XML for this specific path + $clearWebConfig = '' + $emptyWebConfig = '' + $webConfigs = @{ + "Default Web Site/owa" = $clearWebConfig + "Default Web Site" = $emptyWebConfig + } + + $result = Get-URLRewriteRule -ApplicationHostConfig $Script:appHost -WebConfigContent $webConfigs + + # Should only have 1 entry (the clear node from web.config) - nothing from appHost or parent + $result.Inbound["Default Web Site/owa"].Count | Should -Be 1 + $null -ne $result.Inbound["Default Web Site/owa"][0].clear | Should -Be $true + } + } + + Context "Outbound rule extraction from web.config" { + + It "Should find outbound rule from OWA web.config" { + $owaOutbound = $Script:result.Outbound["Default Web Site/owa"] + $allRuleNames = @($owaOutbound.rule.name | Where-Object { $null -ne $_ }) + $allRuleNames | Should -Contain "EOMT OWA CSP - outbound" + } + } + + Context "Outbound rule extraction from applicationHost.config per-location" { + + It "Should find outbound rule from appHost OWA location" { + $owaOutbound = $Script:result.Outbound["Default Web Site/owa"] + $allRuleNames = @($owaOutbound.rule.name | Where-Object { $null -ne $_ }) + $allRuleNames | Should -Contain "AppHost OWA Outbound Test" + } + } + + Context "Outbound rule structure" { + + It "Should preserve preCondition attribute on outbound rule" { + $owaOutbound = $Script:result.Outbound["Default Web Site/owa"] + $outboundRule = $owaOutbound | ForEach-Object { $_.rule } | + Where-Object { $_.name -eq "EOMT OWA CSP - outbound" } + $outboundRule.preCondition | Should -Be "EOMT OWA SPA HTML shell - precondition" + } + + It "Should have serverVariable on outbound match" { + $owaOutbound = $Script:result.Outbound["Default Web Site/owa"] + $outboundRule = $owaOutbound | ForEach-Object { $_.rule } | + Where-Object { $_.name -eq "EOMT OWA CSP - outbound" } + $outboundRule.match.serverVariable | Should -Be "RESPONSE_Content_Security_Policy" + } + } + + Context "Outbound inheritance and independent clear tracking" { + + It "Should have empty outbound for vDir with no outbound rules at any level" { + $ewsOutbound = $Script:result.Outbound["Default Web Site/EWS"] + $ewsOutbound.Count | Should -Be 0 + } + + It "Should continue outbound walk-up when only inbound has clear" { + # Inbound clear at child level should not block outbound from inheriting parent rules + $childConfig = '' + $parentConfig = @" + + + + + + +"@ + [xml]$testAppHost = @" + + + + + + + + + + +"@ + $webConfigs = @{ + "Default Web Site/test" = $childConfig + "Default Web Site" = $parentConfig + } + + $result = Get-URLRewriteRule -ApplicationHostConfig $testAppHost -WebConfigContent $webConfigs + + # Inbound: should have 1 entry (the clear node) - walk-up stopped + $result.Inbound["Default Web Site/test"].Count | Should -Be 1 + # Outbound: should have inherited from parent - walk-up NOT blocked by inbound clear + $outboundNames = @($result.Outbound["Default Web Site/test"].rule.name | Where-Object { $null -ne $_ }) + $outboundNames | Should -Contain "Parent Outbound Rule" + } + } + + Context "Empty rewrite sections" { + + It "Should return empty lists when no rewrite rules exist at any level" { + $emptyWebConfig = '' + [xml]$emptyAppHost = @" + + + + + + + +"@ + $webConfigs = @{ + "Default Web Site/test" = $emptyWebConfig + } + + $result = Get-URLRewriteRule -ApplicationHostConfig $emptyAppHost -WebConfigContent $webConfigs + + $result.Inbound.ContainsKey("Default Web Site/test") | Should -Be $true + $result.Inbound["Default Web Site/test"].Count | Should -Be 0 + $result.Outbound.ContainsKey("Default Web Site/test") | Should -Be $true + $result.Outbound["Default Web Site/test"].Count | Should -Be 0 + } + } +} diff --git a/Diagnostics/HealthChecker/Tests/DataCollection/E19/Exchange/IIS/DefaultWebSite-MAPI_web.config b/Diagnostics/HealthChecker/Tests/DataCollection/E19/Exchange/IIS/DefaultWebSite-MAPI_web.config index 06a7ca1990..109ec0045e 100644 --- a/Diagnostics/HealthChecker/Tests/DataCollection/E19/Exchange/IIS/DefaultWebSite-MAPI_web.config +++ b/Diagnostics/HealthChecker/Tests/DataCollection/E19/Exchange/IIS/DefaultWebSite-MAPI_web.config @@ -46,6 +46,11 @@ + + + + + diff --git a/Diagnostics/HealthChecker/Tests/DataCollection/E19/Exchange/IIS/DefaultWebSite-OWA_web.config b/Diagnostics/HealthChecker/Tests/DataCollection/E19/Exchange/IIS/DefaultWebSite-OWA_web.config index 434ba7e695..220dd4807a 100644 --- a/Diagnostics/HealthChecker/Tests/DataCollection/E19/Exchange/IIS/DefaultWebSite-OWA_web.config +++ b/Diagnostics/HealthChecker/Tests/DataCollection/E19/Exchange/IIS/DefaultWebSite-OWA_web.config @@ -93,6 +93,23 @@ + + + + + + + + + + + + + + + + + diff --git a/Diagnostics/HealthChecker/Tests/DataCollection/E19/Exchange/IIS/DefaultWebSite_web.config b/Diagnostics/HealthChecker/Tests/DataCollection/E19/Exchange/IIS/DefaultWebSite_web.config index d79d254d32..7d1d3b446f 100644 --- a/Diagnostics/HealthChecker/Tests/DataCollection/E19/Exchange/IIS/DefaultWebSite_web.config +++ b/Diagnostics/HealthChecker/Tests/DataCollection/E19/Exchange/IIS/DefaultWebSite_web.config @@ -40,6 +40,19 @@ + + + + + + + + + + + + + diff --git a/Diagnostics/HealthChecker/Tests/DataCollection/E19/Exchange/IIS/applicationHost.config b/Diagnostics/HealthChecker/Tests/DataCollection/E19/Exchange/IIS/applicationHost.config index ad1b6ea4f7..27a318bcc9 100644 --- a/Diagnostics/HealthChecker/Tests/DataCollection/E19/Exchange/IIS/applicationHost.config +++ b/Diagnostics/HealthChecker/Tests/DataCollection/E19/Exchange/IIS/applicationHost.config @@ -1191,6 +1191,27 @@ + + + + + + + + + + + + + + + + + + + + + @@ -1304,6 +1325,17 @@ + + + + + + + + + + + @@ -1962,6 +1994,19 @@ + + + + + + + + + + + + + @@ -2369,6 +2414,11 @@ + + + + + diff --git a/Diagnostics/HealthChecker/Tests/HealthChecker.E19.Main.Tests.ps1 b/Diagnostics/HealthChecker/Tests/HealthChecker.E19.Main.Tests.ps1 index 349212f656..3ce3d7e320 100644 --- a/Diagnostics/HealthChecker/Tests/HealthChecker.E19.Main.Tests.ps1 +++ b/Diagnostics/HealthChecker/Tests/HealthChecker.E19.Main.Tests.ps1 @@ -173,6 +173,24 @@ Describe "Testing Health Checker by Mock Data Imports" { SetActiveDisplayGrouping "Exchange IIS Information" $tokenCacheModuleInformation = GetObject "TokenCacheModule loaded" $tokenCacheModuleInformation | Should -Be $null # null because we are loaded and only display if we aren't loaded. + + # Verify inbound URL rewrite rules are displayed (deduplicated across vDirs) + $inboundRules = GetObject "Inbound URL Rewrite Rules" + $inboundRules.Count | Should -Be 2 + $inboundRuleNames = $inboundRules.RewriteRuleName.Value + $inboundRuleNames | Should -Contain "CVE-2022-41040 Mitigation" + $inboundRuleNames | Should -Contain "Global Block Bad User Agents" + + # Verify outbound URL rewrite rules are displayed (deduplicated across vDirs) + $outboundRules = GetObject "Outbound URL Rewrite Rules" + $outboundRules.Count | Should -Be 2 + $outboundRuleNames = $outboundRules.RewriteRuleName.Value + $outboundRuleNames | Should -Contain "EOMT OWA CSP - outbound" + $outboundRuleNames | Should -Contain "AppHost OWA Outbound Test" + + # Verify global IIS rewrite rules warning is displayed + $globalRulesWarning = GetObject "Global IIS Rewrite Rules" + $globalRulesWarning | Should -Not -BeNullOrEmpty } }