private/tests/Test-InactiveAppDontHaveHighPrivGraphPerm.21770.ps1


<#
.SYNOPSIS

#>


function Test-InactiveAppDontHaveHighPrivGraphPerm {
    [CmdletBinding()]
    param(
        $Database
    )

    $sql = @"
    select sp.id, sp.appId, sp.displayName, sp.appOwnerOrganizationId,
    spsi.lastSignInActivity.lastSignInDateTime
    from main.ServicePrincipal sp
        left join main.ServicePrincipalSignIn spsi on spsi.appId = sp.appId
    where sp.id in
        (
            select sp.id
            from main.ServicePrincipal sp
            where sp.oauth2PermissionGrants.scope is not null
        )
        or sp.id in
        (
            select distinct sp.id,
            from (select sp.id, sp.displayName, unnest(sp.appRoleAssignments).AppRoleId as appRoleId
                from main.ServicePrincipal sp) sp
                left join
                    (select unnest(main.ServicePrincipal.appRoles).id as id, unnest(main.ServicePrincipal.appRoles)."value" permissionName
                    from main.ServicePrincipal) spAppRole
                    on sp.appRoleId = spAppRole.id
            where permissionName is not null
        )
    order by spsi.lastSignInActivity.lastSignInDateTime
"@


    $results = Invoke-DatabaseQuery -Database $Database -Sql $sql
    $inactiveRiskyApps = @()
    $otherApps = @()

    foreach($item in $results) {
        $item = Add-DelegatePermissions $item
        $item = Add-AppPermissions $item
        $item = Add-GraphRisk $item
        if([string]::IsNullOrEmpty($item.lastSignInDateTime) -and $item.IsRisky) {
            $inactiveRiskyApps += $item
        }
        else {
            $otherApps += $item
        }
    }

    $passed = $inactiveRiskyApps.Count -eq 0

    if ($passed) {
        $testResultMarkdown += "No inactive applications with high privileges`n`n%TestResult%"
    }
    else {
        $testResultMarkdown += "Inactive Application(s) with high privileges were found`n`n%TestResult%"
    }

    $mdInfo = "`n## Apps with privileged Graph permissions`n`n"
    $mdInfo += "| | Name | Risk | Delegate Permission | Application Permission | App owner tenant | Last sign in|`n"
    $mdInfo += "| :--- | :--- | :--- | :--- | :--- | :--- | :--- |`n"
    $mdInfo += Get-AppList -Apps $inactiveRiskyApps -Icon "❌"
    $mdInfo += Get-AppList -Apps $otherApps -Icon "✅"


    $testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $mdInfo

    Add-ZtTestResultDetail -TestId '21770' -Title 'Inactive applications don''t have highly privileged permissions' `
        -UserImpact Low -Risk High -ImplementationCost Low `
        -AppliesTo Entra -Tag Application `
        -Status $passed -Result $testResultMarkdown
}

function Add-DelegatePermissions($item) {
    $sql = @"
    select sp.id, sp.oauth2PermissionGrants.scope as permissionName,
    from main.ServicePrincipal sp
    where sp.oauth2PermissionGrants.scope is not null
    and sp.id == '{0}'
"@

    $sql = $sql -f $item.id
    $results = Invoke-DatabaseQuery -Database $Database -Sql $sql
    $item.DelegatePermissions = @()
    if($results.permissionName) {
        $perms = $results.permissionName.trim() -replace """", ""
        $item.DelegatePermissions = $perms -split " " | Where-Object { ![string]::IsNullOrEmpty($_)}
    }
    return $item
}

function Add-AppPermissions($item) {
    $sql = @"
    select distinct spAppRole.*
    from (select sp.id, sp.displayName, unnest(sp.appRoleAssignments).AppRoleId as appRoleId
        from main.ServicePrincipal sp) sp
        left join
            (select unnest(main.ServicePrincipal.appRoles).id as id, unnest(main.ServicePrincipal.appRoles)."value" permissionName
            from main.ServicePrincipal) spAppRole
            on sp.appRoleId = spAppRole.id
    where permissionName is not null and sp.id == '{0}'
"@

    $sql = $sql -f $item.id
    $results = Invoke-DatabaseQuery -Database $Database -Sql $sql
    $item.AppPermissions = $results.permissionName
    return $item
}

function Add-GraphRisk($item) {
    $item.Risk = Get-GraphRisk -delegatePermissions $item.DelegatePermissions -applicationPermissions $item.AppPermissions
    $item.IsRisky = $item.Risk -eq "High"
    return $item
}

function Get-AppList($Apps, $Icon) {
    $mdInfo = ""
    foreach ($item in $apps) {
        $tenant = Get-ZtTenant -tenantId $item.appOwnerOrganizationId
        $portalLink = "https://entra.microsoft.com/#view/Microsoft_AAD_IAM/ManagedAppMenuBlade/~/Overview/objectId/$($item.id)/appId/$($item.appId)"
        $risk = $item.Risk
        $delPerm = $item.DelegatePermissions -join ", "
        $appPerm = $item.AppPermissions -join ", "
        $mdInfo += "| $($Icon) | [$(Get-SafeMarkdown($item.displayName))]($portalLink) | $risk | $delPerm | $appPerm | $(Get-SafeMarkdown($tenant.displayName)) | $(Get-FormattedDate($item.lastSignInDateTime)) | `n"
    }
    return $mdInfo
}

function Get-GraphRisk($delegatePermissions, $applicationPermissions) {
    $finalRisk = "Unranked"
    foreach($permission in $applicationPermissions){
        $risk = Get-GraphPermissionRisk -Permission $permission -PermissionType "Application"
        switch($risk){
            "High" { return $risk }
            "Medium" { $finalRisk = $risk }
        }
    }
    foreach($permission in $delegatePermissions){
        $risk = Get-GraphPermissionRisk -Permission $permission -PermissionType "Delegated"
        switch($risk){
            "High" { return $risk }
            "Medium" { $finalRisk = $risk }
        }
    }
    return $finalRisk
}