Private/security-functions.ps1

function Check-OrphanedSecrets {
    param(
        [int]$PageSize = 10,
        [switch]$Html,
        [switch]$ExcludeNamespaces,
        [switch]$Json,
        [object]$KubeData
    )

    if (-not $Global:MakeReport -and -not $Html -and -not $Json) { Clear-Host }
    Write-Host "`n[🔑 Orphaned Secrets]" -ForegroundColor Cyan
    Write-Host -NoNewline "`n🤖 Fetching Secrets..." -ForegroundColor Yellow

    $excludedSecretPatterns = @(
        "^sh\.helm\.release\.v1\.",
        "^bootstrap-token-",
        "^default-token-",
        "^kube-root-ca\.crt$"
    )

    try {
        $secrets = if ($KubeData -and $KubeData.Secrets) {
            $KubeData.Secrets
        } else {
            kubectl get secrets --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
        }
    } catch {
        Write-Host "`r🤖 ❌ Failed to fetch secrets: $_" -ForegroundColor Red
        return
    }

    $secrets = $secrets | Where-Object { $_.metadata.name -notmatch ($excludedSecretPatterns -join "|") }

    if ($ExcludeNamespaces) {
        $secrets = Exclude-Namespaces -items $secrets
    }

    Write-Host "`r🤖 ✅ Secrets fetched. ($($secrets.Count) total)" -ForegroundColor Green
    Write-Host -NoNewline "`n🤖 Checking Secret usage..." -ForegroundColor Yellow

    $usedSecrets = [System.Collections.Generic.HashSet[string]]::new()

    $pods = if ($KubeData -and $KubeData.Pods) { $KubeData.Pods.items } else {
        kubectl get pods --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
    }

    $workloadTypes = @("deployments", "statefulsets", "daemonsets")
    $workloads = $workloadTypes | ForEach-Object {
        if ($KubeData -and $KubeData[$_]) { $KubeData[$_] } else {
            kubectl get $_ --all-namespaces -o json 2>$null | ConvertFrom-Json | Select-Object -ExpandProperty items
        }
    }

    $ingresses = if ($KubeData -and $KubeData.Ingresses) {
        $KubeData.Ingresses
    } else {
        kubectl get ingress --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
    }

    $serviceAccounts = if ($KubeData -and $KubeData.ServiceAccounts) {
        $KubeData.ServiceAccounts
    } else {
        kubectl get serviceaccounts --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
    }

    foreach ($resource in $pods + $workloads) {
        $resource.spec.volumes | Where-Object { $_.secret } | ForEach-Object {
            $null = $usedSecrets.Add($_.secret.secretName)
        }

        $containers = @()
        $containers += $resource.spec.containers
        $containers += $resource.spec.initContainers
        $containers += $resource.spec.ephemeralContainers

        foreach ($container in $containers) {
            $container.env | Where-Object { $_.valueFrom.secretKeyRef } | ForEach-Object {
                $null = $usedSecrets.Add($_.valueFrom.secretKeyRef.name)
            }
            $container.envFrom | Where-Object { $_.secretRef } | ForEach-Object {
                $null = $usedSecrets.Add($_.secretRef.name)
            }
        }
    }

    $ingresses | ForEach-Object {
        $_.spec.tls | Where-Object { $_.secretName } | ForEach-Object {
            $null = $usedSecrets.Add($_.secretName)
        }
    }

    $serviceAccounts | ForEach-Object {
        $_.secrets | ForEach-Object {
            $null = $usedSecrets.Add($_.name)
        }
    }

    if ($KubeData -and $KubeData.CustomResourcesByKind) {
        foreach ($kind in $KubeData.CustomResourcesByKind.Keys) {
            $resources = $KubeData.CustomResourcesByKind[$kind]
            foreach ($res in $resources) {
                if ($res.spec -and $res.spec.PSObject.Properties.name -contains "secretName") {
                    $null = $usedSecrets.Add($res.spec.secretName)
                }
            }
        }
    }

    Write-Host "`r🤖 ✅ Secret usage checked. ($($usedSecrets.Count) in use)" -ForegroundColor Green

    $orphaned = $secrets | Where-Object { -not $usedSecrets.Contains($_.metadata.name) }

    $items = foreach ($s in $orphaned) {
        $ns = if ($s.metadata.namespace) { $s.metadata.namespace } else { "N/A" }
        $name = if ($s.metadata.name) { $s.metadata.name } else { "N/A" }
    
        [PSCustomObject]@{
            Namespace = $ns
            Type      = "🔑 Secret"
            Name      = $name
        }
    }
    
    if ($items.Count -eq 0) {
        Write-Host "🤖 ✅ No orphaned Secrets found." -ForegroundColor Green
        if ($Global:MakeReport -and -not $Html) {
            Write-ToReport "`n[🔑 Orphaned Secrets]`n"
            Write-ToReport "✅ No orphaned Secrets found."
        }
        if ($Html) { return "<p><strong>✅ No orphaned Secrets found.</strong></p>" }
        if ($Json) { return @{ Total = 0; Items = @() } }
        if (-not $Global:MakeReport -and -not $Html) { Read-Host "🤖 Press Enter to return to the menu" }
        return
    }

    if ($Json) {
        return @{ Total = $items.Count; Items = $items }
    }

    if ($Html) {
        $htmlOutput = $items |
            ConvertTo-Html -Fragment -Property Namespace, Type, Name -PreContent "<h2>Orphaned Secrets</h2>" |
            Out-String
        return "<p><strong>⚠️ Total Orphaned Secrets Found:</strong> $($items.Count)</p>$htmlOutput"
    }

    if ($Global:MakeReport) {
        Write-ToReport "`n[🔑 Orphaned Secrets]`n"
        Write-ToReport "⚠️ Total Orphaned Secrets Found: $($items.Count)"
        $tableString = $items | Format-Table Namespace, Type, Name -AutoSize | Out-String
        Write-ToReport $tableString
        return
    }

    $total = $items.Count
    $currentPage = 0
    $totalPages = [math]::Ceiling($total / $PageSize)

    do {
        Clear-Host
        Write-Host "`n[🔑 Orphaned Secrets - Page $($currentPage + 1) of $totalPages]" -ForegroundColor Cyan

        if ($currentPage -eq 0) {
            $msg = @(
                "🤖 Secrets store sensitive data such as API keys and credentials.",
                "",
                "📌 This check identifies Secrets that are NOT used by:",
                " - Pods, Deployments, StatefulSets, DaemonSets.",
                " - Ingress TLS, ServiceAccounts, and Custom Resources.",
                "",
                "⚠️ Unused Secrets may indicate outdated credentials or misconfigurations.",
                "",
                "⚠️ Total Orphaned Secrets Found: $total"
            )
            Write-SpeechBubble -msg $msg -color "Cyan" -icon "🤖" -lastColor "Red" -delay 50
        }

        $paged = $items | Select-Object -Skip ($currentPage * $PageSize) -First $PageSize
        $paged | Format-Table Namespace, Type, Name -AutoSize | Out-Host

        $newPage = Show-Pagination -currentPage $currentPage -totalPages $totalPages
        if ($newPage -eq -1) { break }
        $currentPage = $newPage

    } while ($true)
}

function Check-RBACOverexposure {
    param(
        [int]$PageSize = 10,
        [switch]$Html,
        [switch]$ExcludeNamespaces,
        [switch]$Json,
        [object]$KubeData
    )

    if (-not $Global:MakeReport -and -not $Html -and -not $Json) { Clear-Host }
    Write-Host "`n[🔓 RBAC Overexposure Check]" -ForegroundColor Cyan
    Write-Host -NoNewline "`n🤖 Analyzing Roles and Bindings..." -ForegroundColor Yellow

    $findings = @()

    try {
        $roles = if ($KubeData -and $KubeData.Roles) {
            $KubeData.Roles
        } else {
            kubectl get roles --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
        }

        $clusterRoles = if ($KubeData -and $KubeData.ClusterRoles) {
            $KubeData.ClusterRoles
        } else {
            kubectl get clusterroles -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
        }

        $roleBindings = if ($KubeData -and $KubeData.RoleBindings) {
            $KubeData.RoleBindings
        } else {
            kubectl get rolebindings --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
        }

        $clusterRoleBindings = if ($KubeData -and $KubeData.ClusterRoleBindings) {
            $KubeData.ClusterRoleBindings
        } else {
            kubectl get clusterrolebindings -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
        }
    } catch {
        Write-Host "`r🤖 ❌ Failed to fetch RBAC data: $_" -ForegroundColor Red
        return
    }

    if ($ExcludeNamespaces) {
        $roles = Exclude-Namespaces -items $roles
        $roleBindings = Exclude-Namespaces -items $roleBindings
    }

    $wildcardRoles = @{}
    $sensitiveResourceRoles = @{}

    # Define built-in roles to identify
    $builtInClusterRoles = @(
        "cluster-admin",
        "admin",
        "edit",
        "view",
        "system:kube-scheduler",
        "system:kube-controller-manager",
        "system:node",
        "system:node-proxier",
        "system:monitoring",
        "system:service-account-issuer-discovery",
        "system:auth-delegator",
        "system:heapster",
        "system:kube-dns",
        "system:metrics-server",
        "system:public-info-viewer"
    )

    # Check 1: Wildcard Permissions
    foreach ($cr in $clusterRoles) {
        foreach ($rule in $cr.rules) {
            if ($rule.verbs -contains "*" -and $rule.resources -contains "*" -and $rule.apiGroups -contains "*") {
                $wildcardRoles[$cr.metadata.name] = "ClusterRole"
                break
            }
            # Check 2: Sensitive Resources
            $sensitiveResources = @("secrets", "pods/exec", "roles", "clusterroles", "bindings", "clusterrolebindings")
            $dangerousVerbs = @("*", "create", "update", "delete")
            if ($rule.resources | Where-Object { $_ -in $sensitiveResources }) {
                if ($rule.verbs | Where-Object { $_ -in $dangerousVerbs }) {
                    $sensitiveResourceRoles[$cr.metadata.name] = "ClusterRole"
                    break
                }
            }
        }
    }

    foreach ($r in $roles) {
        foreach ($rule in $r.rules) {
            if ($rule.verbs -contains "*" -and $rule.resources -contains "*" -and $rule.apiGroups -contains "*") {
                $key = "$($r.metadata.namespace)/$($r.metadata.name)"
                $wildcardRoles[$key] = "Role"
                break
            }
            # Check 2: Sensitive Resources
            $sensitiveResources = @("secrets", "pods/exec", "roles", "clusterroles", "bindings", "clusterrolebindings")
            $dangerousVerbs = @("*", "create", "update", "delete")
            if ($rule.resources | Where-Object { $_ -in $sensitiveResources }) {
                if ($rule.verbs | Where-Object { $_ -in $dangerousVerbs }) {
                    $key = "$($r.metadata.namespace)/$($r.metadata.name)"
                    $sensitiveResourceRoles[$key] = "Role"
                    break
                }
            }
        }
    }

    # Check 3: ClusterRoleBindings with Overexposure
    foreach ($crb in $clusterRoleBindings) {
        $roleName = $crb.roleRef.name
        $isClusterAdmin = ($roleName -eq "cluster-admin")
        $isWildcard = $wildcardRoles.ContainsKey($roleName)
        $isSensitive = $sensitiveResourceRoles.ContainsKey($roleName)

        # Check if the role is built-in
        $isBuiltIn = $false
        if ($roleName -like "system:*") {
            $isBuiltIn = $true
        }
        elseif ($roleName -in $builtInClusterRoles) {
            $isBuiltIn = $true
        }
        elseif ($clusterRoles | Where-Object { $_.metadata.name -eq $roleName -and $_.metadata.labels.'kubernetes.io/bootstrapping' -eq 'rbac-defaults' }) {
            $isBuiltIn = $true
        }

        if ($isClusterAdmin -or $isWildcard -or $isSensitive) {
            foreach ($subject in $crb.subjects) {
                # Check 4: Default ServiceAccount
                $isDefaultSA = ($subject.kind -eq "ServiceAccount" -and $subject.name -eq "default")
                $finding = [PSCustomObject]@{
                    Namespace     = "🌍 Cluster-Wide"
                    Binding       = $crb.metadata.name
                    Subject       = "$($subject.kind)/$($subject.name)"
                    Role          = $roleName
                    Scope         = "ClusterRoleBinding"
                    Risk          = if ($isClusterAdmin) { "❗ cluster-admin" } elseif ($isWildcard) { "⚠️ wildcard access" } else { "⚠️ sensitive resource access" }
                    Severity      = if ($isClusterAdmin -or $isDefaultSA) { "Critical" } else { "High" }
                    Recommendation = if ($isClusterAdmin) { "Replace with a least-privilege ClusterRole." } elseif ($isWildcard) { "Restrict the ClusterRole to specific verbs, resources, and apiGroups." } else { "Restrict access to sensitive resources like secrets or pods/exec." }
                }
                if ($isBuiltIn) {
                    $finding.Risk += " (built-in role)"
                    $finding.Recommendation += " This is a built-in Kubernetes role; proceed with caution when modifying."
                }
                if ($isDefaultSA) {
                    $finding.Risk += " (default ServiceAccount)"
                    $finding.Recommendation += " Consider using a custom ServiceAccount with limited permissions for pods."
                }
                $findings += $finding
            }
        }
    }

    # Check 5: RoleBindings with Overexposure
    foreach ($rb in $roleBindings) {
        $roleName = $rb.roleRef.name
        $ns = $rb.metadata.namespace
        $key = "$ns/$roleName"
        $isClusterAdmin = ($roleName -eq "cluster-admin")
        $isWildcard = $wildcardRoles.ContainsKey($key)
        $isSensitive = $sensitiveResourceRoles.ContainsKey($key)

        # Check if the role is built-in (for RoleBindings, this is less common, but possible if the roleRef is a ClusterRole)
        $isBuiltIn = $false
        if ($rb.roleRef.kind -eq "ClusterRole") {
            if ($roleName -like "system:*") {
                $isBuiltIn = $true
            }
            elseif ($roleName -in $builtInClusterRoles) {
                $isBuiltIn = $true
            }
            elseif ($clusterRoles | Where-Object { $_.metadata.name -eq $roleName -and $_.metadata.labels.'kubernetes.io/bootstrapping' -eq 'rbac-defaults' }) {
                $isBuiltIn = $true
            }
        }

        if ($isClusterAdmin -or $isWildcard -or $isSensitive) {
            foreach ($subject in $rb.subjects) {
                # Check 4: Default ServiceAccount
                $isDefaultSA = ($subject.kind -eq "ServiceAccount" -and $subject.name -eq "default")
                $finding = [PSCustomObject]@{
                    Namespace     = $ns
                    Binding       = $rb.metadata.name
                    Subject       = "$($subject.kind)/$($subject.name)"
                    Role          = $roleName
                    Scope         = "RoleBinding"
                    Risk          = if ($isClusterAdmin) { "❗ cluster-admin" } elseif ($isWildcard) { "⚠️ wildcard access" } else { "⚠️ sensitive resource access" }
                    Severity      = if ($isClusterAdmin -or $isDefaultSA) { "Critical" } else { "High" }
                    Recommendation = if ($isClusterAdmin) { "Replace with a least-privilege Role." } elseif ($isWildcard) { "Restrict the Role to specific verbs, resources, and apiGroups." } else { "Restrict access to sensitive resources like secrets or pods/exec." }
                }
                if ($isBuiltIn) {
                    $finding.Risk += " (built-in role)"
                    $finding.Recommendation += " This is a built-in Kubernetes role; proceed with caution when modifying."
                }
                if ($isDefaultSA) {
                    $finding.Risk += " (default ServiceAccount)"
                    $finding.Recommendation += " Consider using a custom ServiceAccount with limited permissions for pods."
                }
                $findings += $finding
            }
        }
    }

    $total = $findings.Count
    Write-Host "`r🤖 ✅ Check complete. ($total high-risk bindings found)" -ForegroundColor Green

    if ($total -eq 0) {
        Write-Host "✅ No overexposed roles or bindings found." -ForegroundColor Green
        if ($Html) { return "<p><strong>✅ No RBAC overexposure detected.</strong></p>" }
        if ($Json) { return @{ Total = 0; Items = @() } }
        if ($Global:MakeReport -and -not $Html) {
            Write-ToReport "`n[🔓 RBAC Overexposure Check]`n"
            Write-ToReport "✅ No cluster-admin, wildcard, or sensitive resource access detected."
        }
        if (-not $Global:MakeReport -and -not $Html) {
            Read-Host "🤖 Press Enter to return to the menu"
        }
        return
    }

    if ($Json) {
        return @{ Total = $total; Items = $findings }
    }

    if ($Html) {
        $htmlTable = $findings |
            ConvertTo-Html -Fragment -Property Namespace, Binding, Subject, Role, Scope, Risk, Severity, Recommendation -PreContent "<h2>RBAC Overexposure (cluster-admin, wildcard, or sensitive resources)</h2>" |
            Out-String
        return "<p><strong>⚠️ Total Overexposed Bindings:</strong> $total</p>$htmlTable"
    }

    if ($Global:MakeReport) {
        Write-ToReport "`n[🔓 RBAC Overexposure Check]`n"
        Write-ToReport "⚠️ Total Overexposed Bindings: $total"
        $tableString = $findings | Format-Table Namespace, Binding, Subject, Role, Scope, Risk, Severity, Recommendation -AutoSize | Out-String
        Write-ToReport $tableString
        return
    }

    $currentPage = 0
    $totalPages = [math]::Ceiling($total / $PageSize)

    do {
        Clear-Host
        Write-Host "`n[🔓 RBAC Overexposure - Page $($currentPage + 1) of $totalPages]" -ForegroundColor Cyan

        if ($currentPage -eq 0) {
            $msg = @(
                "🤖 This check identifies risky access via RBAC.",
                "",
                "📌 Included:",
                " - cluster-admin grants (direct bindings)",
                " - Custom Roles with * verbs, * resources, * apiGroups",
                " - Access to sensitive resources (e.g., secrets, pods/exec)",
                " - Default ServiceAccounts with excessive permissions",
                " - Built-in roles are flagged with a note for awareness",
                "",
                "⚠️ These bindings may allow unintended control over your cluster.",
                "",
                "⚠️ Total Overexposed Bindings Found: $total"
            )
            Write-SpeechBubble -msg $msg -color "Cyan" -icon "🤖" -lastColor "Red" -delay 50
        }

        $paged = $findings | Select-Object -Skip ($currentPage * $PageSize) -First $PageSize
        $paged | Format-Table Namespace, Binding, Subject, Role, Scope, Risk, Severity, Recommendation -AutoSize | Out-Host

        $newPage = Show-Pagination -currentPage $currentPage -totalPages $totalPages
        if ($newPage -eq -1) { break }
        $currentPage = $newPage

    } while ($true)
}

function Check-RBACMisconfigurations {
    param(
        [int]$PageSize = 10,
        [switch]$Html,
        [switch]$ExcludeNamespaces,
        [switch]$Json,
        [object]$KubeData
    )

    if (-not $Global:MakeReport -and -not $Html -and -not $Json) { Clear-Host }
    Write-Host "`n[RBAC Misconfigurations]" -ForegroundColor Cyan
    Write-Host -NoNewline "`n🤖 Fetching RoleBindings & ClusterRoleBindings..." -ForegroundColor Yellow

    try {
        $roleBindings = if ($KubeData -and $KubeData.RoleBindings) {
            $KubeData.RoleBindings
        } else {
            kubectl get rolebindings --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
        }

        $clusterRoleBindings = if ($KubeData -and $KubeData.ClusterRoleBindings) {
            $KubeData.ClusterRoleBindings
        } else {
            kubectl get clusterrolebindings -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
        }

        $roles = if ($KubeData -and $KubeData.Roles) {
            $KubeData.Roles
        } else {
            kubectl get roles --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
        }

        $clusterRoles = if ($KubeData -and $KubeData.ClusterRoles) {
            $KubeData.ClusterRoles
        } else {
            kubectl get clusterroles -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
        }

        $existingNamespaces = if ($KubeData -and $KubeData.Namespaces) {
            $KubeData.Namespaces | ForEach-Object { $_.metadata.name }
        } else {
            kubectl get namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items | ForEach-Object { $_.metadata.name }
        }

        $serviceAccounts = if ($KubeData -and $KubeData.ServiceAccounts) {
            $KubeData.ServiceAccounts
        } else {
            kubectl get serviceaccounts --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
        }
    }
    catch {
        Write-Host "`r🤖 ❌ Error retrieving RBAC data: $_" -ForegroundColor Red
        return
    }

    if ($ExcludeNamespaces) {
        $roleBindings = Exclude-Namespaces -items $roleBindings
        $roles = Exclude-Namespaces -items $roles
        $serviceAccounts = Exclude-Namespaces -items $serviceAccounts
    }

    Write-Host "`r🤖 ✅ Fetched $($roleBindings.Count) RoleBindings, $($clusterRoleBindings.Count) ClusterRoleBindings, $($roles.Count) Roles, $($clusterRoles.Count) ClusterRoles, $($serviceAccounts.Count) ServiceAccounts.`n" -ForegroundColor Green
    Write-Host -NoNewline "🤖 Analyzing RBAC configurations..." -ForegroundColor Yellow

    $invalidRBAC = @()

    # Check 1: Missing RoleRef in Bindings
    foreach ($rb in $roleBindings) {
        if (-not $rb.roleRef) {
            $invalidRBAC += [PSCustomObject]@{
                Namespace     = $rb.metadata.namespace
                Type          = "🔹 Namespace Role"
                RoleBinding   = $rb.metadata.name
                Subject       = "N/A"
                Issue         = "🚩 Missing roleRef in RoleBinding"
                Severity      = "High"
                Recommendation = "Delete the RoleBinding or specify a valid roleRef."
            }
            continue
        }

        $rbNamespace = $rb.metadata.namespace
        $namespaceExists = $rbNamespace -in $existingNamespaces

        # Check 2: Missing Role for RoleBinding
        $roleExists = $roles | Where-Object {
            $_.metadata.name -eq $rb.roleRef.name -and $_.metadata.namespace -eq $rbNamespace
        }

        if (-not $roleExists -and $rb.roleRef.kind -eq "Role") {
            $invalidRBAC += [PSCustomObject]@{
                Namespace     = if ($namespaceExists) { $rbNamespace } else { "🚩 Namespace Missing" }
                Type          = "🔹 Namespace Role"
                RoleBinding   = $rb.metadata.name
                Subject       = "N/A"
                Issue         = "❌ Missing Role: $($rb.roleRef.name)"
                Severity      = "High"
                Recommendation = "Create the missing Role or update the RoleBinding to reference an existing Role."
            }
        }

        # Check 3: RoleBinding Referencing ClusterRole
        if ($rb.roleRef.kind -eq "ClusterRole") {
            $clusterRole = $clusterRoles | Where-Object { $_.metadata.name -eq $rb.roleRef.name }
            if ($clusterRole) {
                $invalidRBAC += [PSCustomObject]@{
                    Namespace     = $rbNamespace
                    Type          = "🔹 Namespace Role"
                    RoleBinding   = $rb.metadata.name
                    Subject       = if ($rb.subjects) { ($rb.subjects | ForEach-Object { "$($_.kind)/$($_.name)" }) -join ", " } else { "N/A" }
                    Issue         = "⚠️ RoleBinding references ClusterRole: $($rb.roleRef.name)"
                    Severity      = "Medium"
                    Recommendation = "Consider using a namespace-scoped Role instead of a ClusterRole to limit permissions to the namespace."
                }
            }
        }

        # Check 4: Missing ServiceAccounts and Namespaces
        foreach ($subject in $rb.subjects) {
            if ($subject.kind -eq "ServiceAccount") {
                $subjectNamespace = if ($subject.namespace) { $subject.namespace } else { $rbNamespace }
                if (-not $namespaceExists) {
                    $invalidRBAC += [PSCustomObject]@{
                        Namespace     = "🚩 Namespace Missing"
                        Type          = "🔹 Namespace Role"
                        RoleBinding   = $rb.metadata.name
                        Subject       = "$($subject.kind)/$($subject.name)"
                        Issue         = "🚩 Namespace does not exist"
                        Severity      = "High"
                        Recommendation = "Delete the RoleBinding or update the namespace to an existing one."
                    }
                } else {
                    $exists = $serviceAccounts | Where-Object {
                        $_.metadata.name -eq $subject.name -and $_.metadata.namespace -eq $subjectNamespace
                    }
                    if (-not $exists) {
                        $invalidRBAC += [PSCustomObject]@{
                            Namespace     = $rbNamespace
                            Type          = "🔹 Namespace Role"
                            RoleBinding   = $rb.metadata.name
                            Subject       = "$($subject.kind)/$($subject.name)"
                            Issue         = "❌ ServiceAccount does not exist in namespace $subjectNamespace"
                            Severity      = "High"
                            Recommendation = "Create the missing ServiceAccount or update the RoleBinding to reference an existing ServiceAccount."
                        }
                    }
                }
            }
        }
    }

    foreach ($crb in $clusterRoleBindings) {
        # Check 5: Missing RoleRef in ClusterRoleBinding
        if (-not $crb.roleRef) {
            $invalidRBAC += [PSCustomObject]@{
                Namespace     = "🌍 Cluster-Wide"
                Type          = "🔸 Cluster Role"
                RoleBinding   = $crb.metadata.name
                Subject       = "N/A"
                Issue         = "🚩 Missing roleRef in ClusterRoleBinding"
                Severity      = "High"
                Recommendation = "Delete the ClusterRoleBinding or specify a valid roleRef."
            }
            continue
        }

        # Check 6: Missing ServiceAccounts and Namespaces in ClusterRoleBindings
        foreach ($subject in $crb.subjects) {
            if ($subject.kind -eq "ServiceAccount") {
                $subjectNamespace = $subject.namespace
                if (-not $subjectNamespace) {
                    $invalidRBAC += [PSCustomObject]@{
                        Namespace     = "🌍 Cluster-Wide"
                        Type          = "🔸 Cluster Role"
                        RoleBinding   = $crb.metadata.name
                        Subject       = "$($subject.kind)/$($subject.name)"
                        Issue         = "🚩 Namespace not specified for ServiceAccount"
                        Severity      = "High"
                        Recommendation = "Specify a valid namespace for the ServiceAccount in the ClusterRoleBinding."
                    }
                }
                elseif ($subjectNamespace -notin $existingNamespaces) {
                    $invalidRBAC += [PSCustomObject]@{
                        Namespace     = "🚩 Namespace Missing"
                        Type          = "🔸 Cluster Role"
                        RoleBinding   = $crb.metadata.name
                        Subject       = "$($subject.kind)/$($subject.name)"
                        Issue         = "🚩 Namespace does not exist: $subjectNamespace"
                        Severity      = "High"
                        Recommendation = "Delete the ClusterRoleBinding or update the namespace to an existing one."
                    }
                } else {
                    $exists = $serviceAccounts | Where-Object {
                        $_.metadata.name -eq $subject.name -and $_.metadata.namespace -eq $subjectNamespace
                    }
                    if (-not $exists) {
                        $invalidRBAC += [PSCustomObject]@{
                            Namespace     = "🌍 Cluster-Wide"
                            Type          = "🔸 Cluster Role"
                            RoleBinding   = $crb.metadata.name
                            Subject       = "$($subject.kind)/$($subject.name)"
                            Issue         = "❌ ServiceAccount does not exist in namespace $subjectNamespace"
                            Severity      = "High"
                            Recommendation = "Create the missing ServiceAccount or update the ClusterRoleBinding to reference an existing ServiceAccount."
                        }
                    }
                }
            }
        }
    }

    Write-Host "`r🤖 ✅ RBAC configurations Checked. " -ForegroundColor Green

    if ($invalidRBAC.Count -eq 0) {
        Write-Host "`r✅ No RBAC misconfigurations found." -ForegroundColor Green
        if ($Html) { return "<p><strong>✅ No RBAC misconfigurations found.</strong></p>" }
        if ($Json) { return @{ Total = 0; Items = @() } }
        if ($Global:MakeReport -and -not $Html) {
            Write-ToReport "`n[RBAC Misconfigurations]`n"
            Write-ToReport "✅ No RBAC misconfigurations found."
        }
        if (-not $Global:MakeReport -and -not $Html) {
            Read-Host "🤖 Press Enter to return to the menu"
        }
        return
    }

    if ($Json) {
        return @{ Total = $invalidRBAC.Count; Items = $invalidRBAC }
    }

    if ($Html) {
        $htmlTable = $invalidRBAC |
            ConvertTo-Html -Fragment -Property Namespace, Type, RoleBinding, Subject, Issue, Severity, Recommendation -PreContent "<h2>RBAC Misconfigurations</h2>" |
            Out-String
        return "<p><strong>⚠️ Total RBAC Misconfigurations Detected:</strong> $($invalidRBAC.Count)</p>$htmlTable"
    }

    if ($Global:MakeReport) {
        Write-ToReport "`n[RBAC Misconfigurations]`n"
        Write-ToReport "⚠️ Total RBAC Misconfigurations Detected: $($invalidRBAC.Count)"
        $tableString = $invalidRBAC | Format-Table Namespace, Type, RoleBinding, Subject, Issue, Severity, Recommendation -AutoSize | Out-String
        Write-ToReport $tableString
        return
    }

    $total = $invalidRBAC.Count
    $currentPage = 0
    $totalPages = [math]::Ceiling($total / $PageSize)

    do {
        Clear-Host
        Write-Host "`n[RBAC Misconfigurations - Page $($currentPage + 1) of $totalPages]" -ForegroundColor Cyan

        if ($currentPage -eq 0) {
            $msg = @(
                "🤖 RBAC (Role-Based Access Control) defines who can do what in your cluster.",
                "",
                "📌 This check identifies:",
                " - 🔍 Misconfigurations in RoleBindings & ClusterRoleBindings.",
                " - ❌ Missing references to ServiceAccounts & Namespaces.",
                " - ⚠️ RoleBindings referencing ClusterRoles.",
                "",
                "⚠️ Total RBAC Misconfigurations Detected: $total"
            )
            Write-SpeechBubble -msg $msg -color "Cyan" -icon "🤖" -lastColor "Red" -delay 50
        }

        $paged = $invalidRBAC | Select-Object -Skip ($currentPage * $PageSize) -First $PageSize
        $paged | Format-Table Namespace, Type, RoleBinding, Subject, Issue, Severity, Recommendation -AutoSize | Out-Host

        $newPage = Show-Pagination -currentPage $currentPage -totalPages $totalPages
        if ($newPage -eq -1) { break }
        $currentPage = $newPage

    } while ($true)
}

function Check-HostPidAndNetwork {
    param(
        [int]$PageSize = 10,
        [switch]$Html,
        [switch]$ExcludeNamespaces,
        [switch]$Json,
        [object]$KubeData
    )

    if (-not $Global:MakeReport -and -not $Html -and -not $Json) { Clear-Host }
    Write-Host "`n[🔌 Pods with hostPID / hostNetwork]" -ForegroundColor Cyan
    Write-Host -NoNewline "`n🤖 Fetching Pods..." -ForegroundColor Yellow

    try {
        $pods = if ($KubeData -and $KubeData.Pods) {
            $KubeData.Pods.items
        } else {
            kubectl get pods --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
        }
    } catch {
        Write-Host "`r🤖 ❌ Error retrieving pod data: $_" -ForegroundColor Red
        return
    }

    if ($ExcludeNamespaces) {
        $pods = Exclude-Namespaces -items $pods
    }

    Write-Host "`r🤖 ✅ Pods fetched. ($($pods.Count) total)" -ForegroundColor Green
    Write-Host -NoNewline "`n🤖 Scanning for hostPID / hostNetwork usage..." -ForegroundColor Yellow

    $flaggedPods = foreach ($pod in $pods) {
        $hostPID = $pod.spec.hostPID
        $hostNetwork = $pod.spec.hostNetwork

        if ($hostPID -or $hostNetwork) {
            [PSCustomObject]@{
                Namespace   = $pod.metadata.namespace
                Pod         = $pod.metadata.name
                hostPID     = if ($hostPID -eq $true) { "❌ true" } else { "✅ false" }
                hostNetwork = if ($hostNetwork -eq $true) { "❌ true" } else { "✅ false" }
            }
        }
    }

    Write-Host "`r🤖 ✅ Scan complete. ($($flaggedPods.Count) flagged) " -ForegroundColor Green

    if ($flaggedPods.Count -eq 0) {
        Write-Host "✅ No pods with hostPID or hostNetwork found." -ForegroundColor Green
        if ($Html) {
            return "<p><strong>✅ No pods with hostPID or hostNetwork found.</strong></p>"
        }
        if ($Json) {
            return @{ Total = 0; Items = @() }
        }
        if ($Global:MakeReport -and -not $Html) {
            Write-ToReport "`n[🔌 Pods with hostPID / hostNetwork]`n"
            Write-ToReport "✅ No pods with hostPID or hostNetwork found."
        }
        if (-not $Global:MakeReport -and -not $Html) {
            Read-Host "🤖 Press Enter to return to the menu"
        }
        return
    }

    if ($Json) {
        return @{ Total = $flaggedPods.Count; Items = $flaggedPods }
    }

    if ($Html) {
        $htmlTable = $flaggedPods |
            ConvertTo-Html -Fragment -Property Namespace, Pod, hostPID, hostNetwork -PreContent "<h2>Pods with hostPID / hostNetwork</h2>" |
            Out-String

        return "<p><strong>⚠️ Total Flagged Pods:</strong> $($flaggedPods.Count)</p>$htmlTable"
    }

    if ($Global:MakeReport) {
        Write-ToReport "`n[🔌 Pods with hostPID / hostNetwork]`n"
        Write-ToReport "⚠️ Total Flagged Pods: $($flaggedPods.Count)"
        $tableString = $flaggedPods | Format-Table Namespace, Pod, hostPID, hostNetwork -AutoSize | Out-String
        Write-ToReport $tableString
        return
    }

    $totalItems = $flaggedPods.Count
    $currentPage = 0
    $totalPages = [math]::Ceiling($totalItems / $PageSize)

    do {
        Clear-Host
        Write-Host "`n[🔌 Pods with hostPID / hostNetwork - Page $($currentPage + 1) of $totalPages]" -ForegroundColor Cyan

        if ($currentPage -eq 0) {
            $msg = @(
                "🤖 Some pods use host-level process or network namespaces.",
                "",
                "📌 This check identifies pods with:",
                " - hostPID = true",
                " - hostNetwork = true",
                "",
                "⚠️ These settings can bypass isolation and expose the node.",
                "",
                "⚠️ Total Flagged Pods: $($flaggedPods.Count)"
            )
            Write-SpeechBubble -msg $msg -color "Cyan" -icon "🤖" -lastColor "Red" -delay 50
        }

        $startIndex = $currentPage * $PageSize
        $endIndex = [math]::Min($startIndex + $PageSize, $totalItems)
        $flaggedPods[$startIndex..($endIndex - 1)] | Format-Table Namespace, Pod, hostPID, hostNetwork -AutoSize | Out-Host

        $newPage = Show-Pagination -currentPage $currentPage -totalPages $totalPages
        if ($newPage -eq -1) { break }
        $currentPage = $newPage
    } while ($true)
}

function Check-PodsRunningAsRoot {
    param(
        [int]$PageSize = 10,
        [switch]$Html,
        [switch]$ExcludeNamespaces,
        [switch]$Json,
        [object]$KubeData
    )

    if (-not $Global:MakeReport -and -not $Html -and -not $Json) { Clear-Host }
    Write-Host "`n[👑 Pods Running as Root]" -ForegroundColor Cyan
    Write-Host -NoNewline "`n🤖 Fetching Pods..." -ForegroundColor Yellow

    try {
        $pods = if ($KubeData -and $KubeData.Pods) {
            $KubeData.Pods.items
        } else {
            kubectl get pods --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
        }
    } catch {
        Write-Host "`r🤖 ❌ Error retrieving pod data: $_" -ForegroundColor Red
        return
    }

    if ($ExcludeNamespaces) {
        $pods = Exclude-Namespaces -items $pods
    }

    Write-Host "`r🤖 ✅ Pods fetched. ($($pods.Count) total)" -ForegroundColor Green
    Write-Host -NoNewline "`n🤖 Scanning for root user usage..." -ForegroundColor Yellow

    $rootPods = @()

    foreach ($pod in $pods) {
        $podUser = $pod.spec.securityContext.runAsUser

        foreach ($container in $pod.spec.containers) {
            $containerUser = $container.securityContext.runAsUser
            $isRoot = -not $containerUser -and -not $podUser

            if (($containerUser -eq 0) -or ($podUser -eq 0) -or $isRoot) {
                $rootPods += [PSCustomObject]@{
                    Namespace = $pod.metadata.namespace
                    Pod       = $pod.metadata.name
                    Container = $container.name
                    runAsUser = if ($containerUser) {
                        $containerUser
                    } elseif ($podUser) {
                        $podUser
                    } else {
                        "Not Set (Defaults to root)"
                    }
                }
            }
        }
    }

    Write-Host "`r🤖 ✅ Scan complete. ($($rootPods.Count) flagged) " -ForegroundColor Green

    if ($rootPods.Count -eq 0) {
        Write-Host "✅ No pods running as root." -ForegroundColor Green
        if ($Global:MakeReport -and -not $Html) {
            Write-ToReport "`n[👑 Pods Running as Root]`n"
            Write-ToReport "✅ No pods running as root."
        }
        if ($Html) { return "<p><strong>✅ No pods running as root.</strong></p>" }
        if ($Json) { return @{ Total = 0; Items = @() } }
        if (-not $Global:MakeReport -and -not $Html) {
            Read-Host "🤖 Press Enter to return to the menu"
        }
        return
    }

    if ($Html) {
        $htmlTable = $rootPods |
            ConvertTo-Html -Fragment -Property Namespace, Pod, Container, runAsUser -PreContent "<h2>Pods Running as Root</h2>" |
            Out-String

        return "<p><strong>⚠️ Total Pods Running as Root:</strong> $($rootPods.Count)</p>$htmlTable"
    }

    if ($Json) {
        return @{ Total = $rootPods.Count; Items = $rootPods }
    }

    if ($Global:MakeReport) {
        Write-ToReport "`n[👑 Pods Running as Root]`n"
        Write-ToReport "⚠️ Total Pods Running as Root: $($rootPods.Count)"
        $tableString =$rootPods | Format-Table Namespace, Pod, Container, runAsUser -AutoSize | Out-String
        Write-ToReport $tableString
        return
    }

    $totalItems = $rootPods.Count
    $currentPage = 0
    $totalPages = [math]::Ceiling($totalItems / $PageSize)

    do {
        Clear-Host
        Write-Host "`n[👑 Pods Running as Root - Page $($currentPage + 1) of $totalPages]" -ForegroundColor Cyan

        if ($currentPage -eq 0) {
            $msg = @(
                "🤖 Some pods are running as root (UID 0) or without explicit user settings.",
                "",
                "📌 This check looks for:",
                " - container or pod runAsUser = 0",
                " - runAsUser not set (defaults to root)",
                "",
                "⚠️ Running as root bypasses container security boundaries.",
                "",
                "⚠️ Total Flagged Pods: $($rootPods.Count)"
            )
            Write-SpeechBubble -msg $msg -color "Cyan" -icon "🤖" -lastColor "Red" -delay 50
        }

        $startIndex = $currentPage * $PageSize
        $endIndex = [math]::Min($startIndex + $PageSize, $totalItems)

        $rootPods[$startIndex..($endIndex - 1)] |
            Format-Table Namespace, Pod, Container, runAsUser -AutoSize | Out-Host

        $newPage = Show-Pagination -currentPage $currentPage -totalPages $totalPages
        if ($newPage -eq -1) { break }
        $currentPage = $newPage
    } while ($true)
}

function Check-PrivilegedContainers {
    param(
        [int]$PageSize = 10,
        [switch]$Html,
        [switch]$ExcludeNamespaces,
        [switch]$Json,
        [object]$KubeData
    )

    if (-not $Global:MakeReport -and -not $Html -and -not $Json) { Clear-Host }
    Write-Host "`n[🔓 Privileged Containers]" -ForegroundColor Cyan
    Write-Host -NoNewline "`n🤖 Fetching Pods..." -ForegroundColor Yellow

    try {
        $pods = if ($KubeData -and $KubeData.Pods) {
            $KubeData.Pods.items
        } else {
            kubectl get pods --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
        }
    } catch {
        Write-Host "`r🤖 ❌ Error retrieving pod data: $_" -ForegroundColor Red
        return
    }

    if ($ExcludeNamespaces) {
        $pods = Exclude-Namespaces -items $pods
    }

    Write-Host "`r🤖 ✅ Pods fetched. ($($pods.Count) total)" -ForegroundColor Green
    Write-Host -NoNewline "`n🤖 Scanning for privileged containers..." -ForegroundColor Yellow

    $privileged = @()

    foreach ($pod in $pods) {
        foreach ($container in $pod.spec.containers) {
            if ($container.securityContext.privileged -eq $true) {
                $privileged += [PSCustomObject]@{
                    Namespace = $pod.metadata.namespace
                    Pod       = $pod.metadata.name
                    Container = $container.name
                }
            }
        }
    }

    Write-Host "`r🤖 ✅ Scan complete. ($($privileged.Count) flagged) " -ForegroundColor Green

    if ($privileged.Count -eq 0) {
        Write-Host "✅ No privileged containers found." -ForegroundColor Green
        if ($Global:MakeReport -and -not $Html) {
            Write-ToReport "`n[🔓 Privileged Containers]`n"
            Write-ToReport "✅ No privileged containers found."
        }
        if ($Html) { return "<p><strong>✅ No privileged containers found.</strong></p>" }
        if ($Json) { return @{ Total = 0; Items = @() } }
        if (-not $Global:MakeReport -and -not $Html) {
            Read-Host "🤖 Press Enter to return to the menu"
        }
        return
    }

    if ($Html) {
        $htmlTable = $privileged |
            ConvertTo-Html -Fragment -Property Namespace, Pod, Container -PreContent "<h2>Privileged Containers</h2>" |
            Out-String

        return "<p><strong>⚠️ Total Privileged Containers Found:</strong> $($privileged.Count)</p>$htmlTable"
    }

    if ($Json) {
        return @{ Total = $privileged.Count; Items = $privileged }
    }

    if ($Global:MakeReport) {
        Write-ToReport "`n[🔓 Privileged Containers]`n"
        Write-ToReport "⚠️ Total Privileged Containers Found: $($privileged.Count)"
        $tableString = $privileged | Format-Table Namespace, Pod, Container -AutoSize | Out-String
        Write-ToReport $tableString
        return
    }

    $totalItems = $privileged.Count
    $currentPage = 0
    $totalPages = [math]::Ceiling($totalItems / $PageSize)

    do {
        Clear-Host
        Write-Host "`n[🔓 Privileged Containers - Page $($currentPage + 1) of $totalPages]" -ForegroundColor Cyan

        if ($currentPage -eq 0) {
            $msg = @(
                "🤖 Privileged containers run with extended access to the host.",
                "",
                "📌 This check flags containers where:",
                " - securityContext.privileged = true",
                "",
                "⚠️ This setting grants broad capabilities and should be avoided.",
                "",
                "⚠️ Total Privileged Containers Found: $($privileged.Count)"
            )
            Write-SpeechBubble -msg $msg -color "Cyan" -icon "🤖" -lastColor "Red" -delay 50
        }

        $startIndex = $currentPage * $PageSize
        $endIndex = [math]::Min($startIndex + $PageSize, $totalItems)

        $privileged[$startIndex..($endIndex - 1)] |
            Format-Table Namespace, Pod, Container -AutoSize | Out-Host

        $newPage = Show-Pagination -currentPage $currentPage -totalPages $totalPages
        if ($newPage -eq -1) { break }
        $currentPage = $newPage
    } while ($true)
}

function Check-OrphanedServiceAccounts {
    param(
        [int]$PageSize = 10,
        [switch]$Html,
        [switch]$ExcludeNamespaces,
        [switch]$Json,
        [object]$KubeData
    )

    if (-not $Global:MakeReport -and -not $Html -and -not $Json) { Clear-Host }
    Write-Host "`n[🧾 Orphaned ServiceAccounts]" -ForegroundColor Cyan
    Write-Host -NoNewline "`n🤖 Fetching ServiceAccount data..." -ForegroundColor Yellow

    try {
        $sas = if ($KubeData -and $KubeData.ServiceAccounts) {
            $KubeData.ServiceAccounts
        } else {
            kubectl get serviceaccounts --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
        }

        $roleBindings = if ($KubeData -and $KubeData.RoleBindings) {
            $KubeData.RoleBindings
        } else {
            kubectl get rolebindings --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
        }

        $clusterRoleBindings = if ($KubeData -and $KubeData.ClusterRoleBindings) {
            $KubeData.ClusterRoleBindings
        } else {
            kubectl get clusterrolebindings -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
        }

        $pods = if ($KubeData -and $KubeData.Pods) {
            $KubeData.Pods.items
        } else {
            kubectl get pods --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
        }
    }
    catch {
        Write-Host "`r🤖 ❌ Failed to fetch RBAC or Pod data: $_" -ForegroundColor Red
        return
    }

    if ($ExcludeNamespaces) {
        $excludedSet = (Get-ExcludedNamespaces) | ForEach-Object { $_.ToLowerInvariant() }
    
        $sas = $sas | Where-Object { $_.metadata.namespace.ToLowerInvariant() -notin $excludedSet }
        $roleBindings = $roleBindings | Where-Object { $_.metadata.namespace.ToLowerInvariant() -notin $excludedSet }
        $pods = $pods | Where-Object { $_.metadata.namespace.ToLowerInvariant() -notin $excludedSet }
        $clusterRoleBindings = $clusterRoleBindings | Where-Object {
            $_.subjects | Where-Object {
                $_.kind -eq "ServiceAccount" -and $_.namespace -and ($_.namespace.ToLowerInvariant() -notin $excludedSet)
            }
        }
    }    

    Write-Host "`r🤖 ✅ Resources fetched. Analyzing usage..." -ForegroundColor Green

    $usedSAs = [System.Collections.Generic.HashSet[string]]::new()

    # Pods using SAs
    foreach ($pod in $pods) {
        $sa = $pod.spec.serviceAccountName
        if ($sa) {
            $null = $usedSAs.Add("$($pod.metadata.namespace)/$sa")
        }
    }

    # RoleBindings referencing SAs
    foreach ($rb in $roleBindings) {
        foreach ($s in $rb.subjects) {
            if ($s.kind -eq "ServiceAccount" -and $s.name) {
                $ns = if ($s.namespace) { $s.namespace } else { $rb.metadata.namespace }
                $null = $usedSAs.Add("$ns/$($s.name)")
            }
        }
    }

    # ClusterRoleBindings referencing SAs
    foreach ($crb in $clusterRoleBindings) {
        foreach ($s in $crb.subjects) {
            if ($s.kind -eq "ServiceAccount" -and $s.namespace -and $s.name) {
                $null = $usedSAs.Add("$($s.namespace)/$($s.name)")
            }
        }
    }

    # Find unused SAs
    $orphaned = $sas | Where-Object {
        -not $usedSAs.Contains("$($_.metadata.namespace)/$($_.metadata.name)")
    }

    $items = foreach ($sa in $orphaned) {
        [PSCustomObject]@{
            Namespace = $sa.metadata.namespace
            Name      = $sa.metadata.name
        }
    }

    $total = $items.Count
    if ($total -eq 0) {
        Write-Host "`r🤖 ✅ No orphaned ServiceAccounts found." -ForegroundColor Green
        if ($Html) { return "<p><strong>✅ No orphaned ServiceAccounts found.</strong></p>" }
        if ($Json) { return @{ Total = 0; Items = @() } }
        if ($Global:MakeReport -and -not $Html) {
            Write-ToReport "`n[🧾 Orphaned ServiceAccounts]`n✅ None found."
        }
        if (-not $Global:MakeReport -and -not $Html) {
            Read-Host "🤖 Press Enter to return to the menu"
        }
        return
    }

    Write-Host "`r🤖 ✅ Orphaned ServiceAccounts found: $total" -ForegroundColor Green

    if ($Json) {
        return @{ Total = $total; Items = $items }
    }

    if ($Html) {
        $htmlOutput = $items |
            ConvertTo-Html -Fragment -Property Namespace, Name |
            Out-String
        return "<p><strong>⚠️ Orphaned ServiceAccounts:</strong> $total</p>" + $htmlOutput
    }

    if ($Global:MakeReport) {
        Write-ToReport "`n[🧾 Orphaned ServiceAccounts]`n⚠️ Total: $total"
        $tableString = $items | Format-Table Namespace, Name -AutoSize | Out-String
        Write-ToReport $tableString
        return
    }

    $currentPage = 0
    $totalPages = [math]::Ceiling($total / $PageSize)

    do {
        Clear-Host
        Write-Host "`n[🧾 Orphaned ServiceAccounts - Page $($currentPage + 1) of $totalPages]" -ForegroundColor Cyan

        if ($currentPage -eq 0) {
            $msg = @(
                "🤖 These ServiceAccounts aren't used in RoleBindings, ClusterRoleBindings, or Pods.",
                "",
                "📌 Why this matters:",
                " - Unused SAs might be leftover from old workloads.",
                " - Could indicate stale or misconfigured access paths.",
                "",
                "⚠️ Total: $total"
            )
            Write-SpeechBubble -msg $msg -color "Cyan" -icon "🤖" -lastColor "Red" -delay 50
        }

        $paged = $items | Select-Object -Skip ($currentPage * $PageSize) -First $PageSize
        $paged | Format-Table Namespace, Name -AutoSize | Out-Host

        $newPage = Show-Pagination -currentPage $currentPage -totalPages $totalPages
        if ($newPage -eq -1) { break }
        $currentPage = $newPage
    } while ($true)
}

function Check-OrphanedRoles {
    param(
        [object]$KubeData,
        [int]$PageSize = 10,
        [switch]$Html,
        [switch]$Json,
        [switch]$ExcludeNamespaces
    )

    if (-not $Global:MakeReport -and -not $Html -and -not $Json) { Clear-Host }
    Write-Host "`n[🗂️ Unused Roles & ClusterRoles]" -ForegroundColor Cyan
    Write-Host -NoNewline "`n🤖 Fetching RBAC data..." -ForegroundColor Yellow

    try {
        $roles = if ($KubeData -and $KubeData.Roles) { $KubeData.Roles } else { kubectl get roles --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items }
        $clusterRoles = if ($KubeData -and $KubeData.ClusterRoles) { $KubeData.ClusterRoles } else { kubectl get clusterroles -o json | ConvertFrom-Json | Select-Object -ExpandProperty items }
        $roleBindings = if ($KubeData -and $KubeData.RoleBindings) { $KubeData.RoleBindings } else { kubectl get rolebindings --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items }
        $clusterRoleBindings = if ($KubeData -and $KubeData.ClusterRoleBindings) { $KubeData.ClusterRoleBindings } else { kubectl get clusterrolebindings -o json | ConvertFrom-Json | Select-Object -ExpandProperty items }
    }
    catch {
        Write-Host "`r🤖 ❌ Error fetching RBAC data: $_" -ForegroundColor Red
        return
    }

    $usedRoleNames = [System.Collections.Generic.HashSet[string]]::new()
    $usedClusterRoleNames = [System.Collections.Generic.HashSet[string]]::new()

    $results = @()

    # Check 1: Bindings with No Subjects
    foreach ($rb in $roleBindings) {
        if (-not $rb.subjects -or $rb.subjects.Count -eq 0) {
            $results += [PSCustomObject]@{
                Namespace     = $rb.metadata.namespace
                Role          = $rb.roleRef.name
                Type          = "RoleBinding"
                Issue         = "🚩 No subjects defined"
                Severity      = "Low"
                Recommendation = "Delete the RoleBinding as it has no effect."
            }
        }
        if ($rb.roleRef.kind -eq "Role") {
            $usedRoleNames.Add("$($rb.metadata.namespace)/$($rb.roleRef.name)") | Out-Null
        }
        elseif ($rb.roleRef.kind -eq "ClusterRole") {
            $usedClusterRoleNames.Add($rb.roleRef.name) | Out-Null
        }
    }

    foreach ($crb in $clusterRoleBindings) {
        if (-not $crb.subjects -or $crb.subjects.Count -eq 0) {
            $results += [PSCustomObject]@{
                Namespace     = "🌍 Cluster-Wide"
                Role          = $crb.roleRef.name
                Type          = "ClusterRoleBinding"
                Issue         = "🚩 No subjects defined"
                Severity      = "Low"
                Recommendation = "Delete the ClusterRoleBinding as it has no effect."
            }
        }
        if ($crb.roleRef.kind -eq "ClusterRole") {
            $usedClusterRoleNames.Add($crb.roleRef.name) | Out-Null
        }
    }

    # Define built-in roles to exclude
    $builtInClusterRoles = @(
        "cluster-admin",
        "admin",
        "edit",
        "view",
        "system:kube-scheduler",
        "system:kube-controller-manager",
        "system:node",
        "system:node-proxier",
        "system:monitoring",
        "system:service-account-issuer-discovery",
        "system:auth-delegator",
        "system:heapster",
        "system:kube-dns",
        "system:metrics-server",
        "system:public-info-viewer"
    )

    # Filter Roles by excluded namespaces, if applicable
    if ($ExcludeNamespaces) {
        $excludedSet = (Get-ExcludedNamespaces) | ForEach-Object { $_.ToLowerInvariant() }
        Write-Host "`n🤖 Excluding namespaces for Roles: $($excludedSet -join ', ')" -ForegroundColor Yellow
        $roles = $roles | Where-Object { $_.metadata.namespace.ToLowerInvariant() -notin $excludedSet }
    }

    # Check 2: Unused Roles
    foreach ($r in $roles) {
        $key = "$($r.metadata.namespace)/$($r.metadata.name)"
        if (-not $usedRoleNames.Contains($key)) {
            $results += [PSCustomObject]@{
                Namespace     = $r.metadata.namespace
                Role          = $r.metadata.name
                Type          = "Role"
                Issue         = "⚠️ Unused Role"
                Severity      = "Low"
                Recommendation = "Delete the unused Role to reduce clutter."
            }
        }
        # Check 3: Roles with No Rules (Zero-Effect)
        if (-not $r.rules -or $r.rules.Count -eq 0) {
            $results += [PSCustomObject]@{
                Namespace     = $r.metadata.namespace
                Role          = $r.metadata.name
                Type          = "Role"
                Issue         = "🚩 No rules defined"
                Severity      = "Low"
                Recommendation = "Delete the Role or define rules to make it effective."
            }
        }
    }

    # Check 4: Unused ClusterRoles, excluding built-in roles
    foreach ($cr in $clusterRoles) {
        $isBuiltIn = $false
        # Check for system: prefix
        if ($cr.metadata.name -like "system:*") {
            $isBuiltIn = $true
        }
        # Check for well-known built-in roles
        elseif ($cr.metadata.name -in $builtInClusterRoles) {
            $isBuiltIn = $true
        }
        # Check for kubernetes.io/bootstrapping label
        elseif ($cr.metadata.labels -and $cr.metadata.labels.'kubernetes.io/bootstrapping' -eq 'rbac-defaults') {
            $isBuiltIn = $true
        }

        if (-not $isBuiltIn -and -not $usedClusterRoleNames.Contains($cr.metadata.name)) {
            $results += [PSCustomObject]@{
                Namespace     = "🌍 Cluster-Wide"
                Role          = $cr.metadata.name
                Type          = "ClusterRole"
                Issue         = "⚠️ Unused ClusterRole"
                Severity      = "Low"
                Recommendation = "Delete the unused ClusterRole to reduce clutter."
            }
        }
        # Check 5: ClusterRoles with No Rules (Zero-Effect)
        if (-not $cr.rules -or $cr.rules.Count -eq 0) {
            $results += [PSCustomObject]@{
                Namespace     = "🌍 Cluster-Wide"
                Role          = $cr.metadata.name
                Type          = "ClusterRole"
                Issue         = "🚩 No rules defined"
                Severity      = "Low"
                Recommendation = "Delete the ClusterRole or define rules to make it effective."
            }
        }
    }

    $total = $results.Count
    Write-Host "`r🤖 ✅ RBAC analysis complete. ($total unused or ineffective roles/bindings found)" -ForegroundColor Green

    if ($total -eq 0) {
        if ($Html) { return "<p><strong>✅ No unused or ineffective roles/bindings found.</strong></p>" }
        if ($Json) { return @{ Total = 0; Items = @() } }
        if ($Global:MakeReport -and -not $Html) { Write-ToReport "`n[🗂️ Unused Roles & ClusterRoles]`n✅ No unused or ineffective roles/bindings." }
        if (-not $Global:MakeReport -and -not $Html) { Read-Host "🤖 Press Enter to return to the menu" }
        return
    }

    if ($Json) { return @{ Total = $total; Items = $results } }
    if ($Html) {
        $htmlOutput = $results | ConvertTo-Html -Fragment -Property Namespace, Role, Type, Issue, Severity, Recommendation | Out-String
        return "<p><strong>⚠️ Unused or Ineffective Roles/Bindings:</strong> $total</p>" + $htmlOutput
    }
    if ($Global:MakeReport) {
        Write-ToReport "`n[🗂️ Unused Roles & ClusterRoles]`n⚠️ Total: $total"
        $tableString = $results | Format-Table Namespace, Role, Type, Issue, Severity, Recommendation -AutoSize | Out-String
        Write-ToReport $tableString
        return
    }

    $currentPage = 0
    $totalPages = [math]::Ceiling($total / $PageSize)
    do {
        Clear-Host
        Write-Host "`n[🗂️ Unused Roles - Page $($currentPage + 1) of $totalPages]" -ForegroundColor Cyan
        if ($currentPage -eq 0) {
            Write-SpeechBubble -msg @(
                "🤖 These Roles, ClusterRoles, and Bindings are unused or ineffective.",
                "",
                "📌 Why this matters:",
                " - Unused roles/bindings add clutter and confusion.",
                " - May be leftovers from uninstalled apps.",
                " - Bindings with no subjects or roles with no rules have no effect.",
                " - Built-in Kubernetes roles (e.g., system:*, cluster-admin) are excluded.",
                "",
                "⚠️ Total unused or ineffective roles/bindings: $total"
            ) -color "Cyan" -icon "🤖" -lastColor "Red" -delay 50
        }
        $paged = $results | Select-Object -Skip ($currentPage * $PageSize) -First $PageSize
        $paged | Format-Table Namespace, Role, Type, Issue, Severity, Recommendation -AutoSize | Out-Host
        $newPage = Show-Pagination -currentPage $currentPage -totalPages $totalPages
        if ($newPage -eq -1) { break }
        $currentPage = $newPage
    } while ($true)
}