private/tests-shared/Get-ZtAppWithUnsafeRedirectUris.ps1
<# .SYNOPSIS Checking App registrations must use safe redirect URIs #> function Get-ZtAppWithUnsafeRedirectUris { [CmdletBinding()] param($Database, # 'ServicePrincipal' or 'Application' $Type) Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose # Get current tenantid $currentTenantId = (Get-MgContext).TenantId $filter = "appOwnerOrganizationId = '$currentTenantId'" if ($Type -eq 'ServicePrincipal') { $filter = "appOwnerOrganizationId <> '$currentTenantId'" } $sql = @" select id, appid, displayName, replyUrls, accountEnabled, appOwnerOrganizationId from main."ServicePrincipal" WHERE $filter order by displayName "@ $results = Invoke-DatabaseQuery -Database $Database -Sql $sql $riskyApps = @() $resolvedDomainsCache = @{} $tenantsToIgnore = @( 'f8cdef31-a31e-4b4a-93e4-5f571e91255a', # Microsoft Services '33e01921-4d64-4f8c-a055-5bdaffd5e33d' # AME ) foreach ($item in $results) { $riskyUrls = @() if($tenantsToIgnore -contains $item.appOwnerOrganizationId) { continue } foreach ($url in $item.replyUrls) { # skip localhost and non http(s) urls if ($url -like "*localhost*") { $riskyUrls += "1️⃣ $url" continue } if ($url -like "http:*") { #Should use https $riskyUrls += "2️⃣ $url" continue } if ($url -like "*azurewebsites.net*") { $riskyUrls += "3️⃣ $url" continue } try { # skip invalid urls $uri = [System.Uri]::new($url) } catch { $riskyUrls += "4️⃣ $url" continue } # Skip non http(s) urls if ($uri.Scheme -ne 'http' -and $uri.Scheme -ne 'https') { continue } # Get domain from $uri $domain = $uri.Host Write-ZtProgress -Activity 'Checking redirect uri' -Status $url if ($resolvedDomainsCache.ContainsKey($domain)) { $isDnsResolved = $resolvedDomainsCache[$domain] } else { # Cache domain resolution results to avoid multiple DNS queries $isDnsResolved = Test-DomainResolves -Domain $domain $resolvedDomainsCache[$domain] = $isDnsResolved } if (!$isDnsResolved) { $riskyUrls += "5️⃣ $url" } # # Check if the url redirects to a different domain : # NOTE: This has been taken out of the spec for now. # $safeRedirect = Test-UriRedirectsToSameDomain -Url $url # if (!$safeRedirect) { # $riskyUrls += "6️⃣ $url" # } } if ($riskyUrls.Count -gt 0) { $riskyApps += $item } # Add the risky URLs as a property to the item $item | Add-Member -MemberType NoteProperty -Name "riskyUrls" -Value $riskyUrls } $passed = $riskyApps.Count -eq 0 if ($passed) { $testResultMarkdown += "No unsafe redirect URIs found" } else { $testResultMarkdown += "Unsafe redirect URIs found`n`n" $testResultMarkdown += "1️⃣ → Use of localhost, 2️⃣ → Use of http(s) instead of https, 3️⃣ → Use of *.azurewebsites.net, 4️⃣ → Invalid URL, 5️⃣ → Domain not resolved`n`n" $testResultMarkdown += Get-RiskyAppList -Apps $riskyApps -Type $Type } # create and return a pscustomobject with testResultMarkdown and passed $result = [PSCustomObject]@{ TestResultMarkdown = $testResultMarkdown Passed = $passed } return $result } function Get-RiskyAppList($Apps, $Type) { $mdInfo = "" $mdInfo += "| | Name | Unsafe Redirect URIs |" # Only add the app owner tenant column for ServicePrincipal if($Type -eq 'ServicePrincipal') { $mdInfo += "App owner tenant |`n" } else { $mdInfo += "`n" } $mdInfo += "| :--- | :--- | :--- |" if($Type -eq 'ServicePrincipal') { $mdInfo += " :--- |`n" } else { $mdInfo += "`n" } foreach ($item in $Apps) { if($Type -eq 'ServicePrincipal') { $tenantName = Get-ZtTenantName -tenantId $item.appOwnerOrganizationId $tenantName = Get-SafeMarkdown $tenantName $portalLink = "https://entra.microsoft.com/#view/Microsoft_AAD_IAM/ManagedAppMenuBlade/~/Overview/objectId/$($item.id)/appId/$($item.appId)" } else { $portalLink = "https://entra.microsoft.com/#view/Microsoft_AAD_IAM/ManagedAppMenuBlade/~/SignOn/objectId/$($item.id)/appId/$($item.appId)/preferredSingleSignOnMode/saml/servicePrincipalType/Application/fromNav/" } $riskyReplyUrls = $item.riskyUrls | ForEach-Object { '`' + $_ + '`' } $riskyReplyUrls = $riskyReplyUrls -join ', ' $mdInfo += "| $($Icon) | [$(Get-SafeMarkdown($item.displayName))]($portalLink) | $riskyReplyUrls | $tenantName |`n" } return $mdInfo } |