diff --git a/containers/clearview/src/clearview_app/scanners/exo_scripts/get-permissions.ps1 b/containers/clearview/src/clearview_app/scanners/exo_scripts/get-permissions.ps1 new file mode 100644 index 0000000..72fe24d --- /dev/null +++ b/containers/clearview/src/clearview_app/scanners/exo_scripts/get-permissions.ps1 @@ -0,0 +1,135 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory=$true)][string]$TenantId, + [Parameter(Mandatory=$true)][string]$ClientId, + [Parameter(Mandatory=$true)][string]$Organization, + [Parameter(Mandatory=$true)][string]$Mailbox, + [Parameter(Mandatory=$true)][string]$CertPath +) + +$ErrorActionPreference = 'Stop' +$ProgressPreference = 'SilentlyContinue' + +function Write-JsonResult { + param($Payload) + Write-Output ($Payload | ConvertTo-Json -Depth 6 -Compress) +} + +try { + Import-Module ExchangeOnlineManagement -ErrorAction Stop +} catch { + Write-JsonResult @{ ok = $false; error = "ExchangeOnlineManagement module not available: $($_.Exception.Message)" } + exit 0 +} + +try { + $pfxPwd = $env:CLEARVIEW_PFX_PASSWORD + if ([string]::IsNullOrEmpty($pfxPwd)) { + Write-JsonResult @{ ok = $false; error = "CLEARVIEW_PFX_PASSWORD not set in environment" } + exit 0 + } + $securePwd = ConvertTo-SecureString -String $pfxPwd -AsPlainText -Force + Connect-ExchangeOnline ` + -AppId $ClientId ` + -Organization $Organization ` + -CertificateFilePath $CertPath ` + -CertificatePassword $securePwd ` + -ShowBanner:$false ` + -ShowProgress:$false ` + -ErrorAction Stop | Out-Null +} catch { + Write-JsonResult @{ ok = $false; error = "Connect-ExchangeOnline failed: $($_.Exception.Message)" } + exit 0 +} + +$entries = New-Object System.Collections.Generic.List[object] +$warnings = New-Object System.Collections.Generic.List[string] + +try { + $mb = Get-EXOMailbox -Identity $Mailbox -PropertySets All -ErrorAction Stop + + # 1) Full Access (and other mailbox-level permissions) + try { + $perms = Get-EXOMailboxPermission -Identity $mb.UserPrincipalName -ErrorAction Stop | + Where-Object { $_.User -notlike 'NT AUTHORITY\SELF' -and $_.User -notlike 'S-1-5-*' -and -not $_.IsInherited -and $_.Deny -eq $false } + foreach ($p in $perms) { + $rights = @($p.AccessRights) -join ', ' + $entries.Add([pscustomobject]@{ + permission_type = 'FullAccess' + object_type = 'Mailbox' + object = $mb.UserPrincipalName + principal = [string]$p.User + role_name = $rights + }) + } + } catch { + $warnings.Add("MailboxPermission: $($_.Exception.Message)") + } + + # 2) Send As + try { + $sendAs = Get-EXORecipientPermission -Identity $mb.UserPrincipalName -ErrorAction Stop | + Where-Object { $_.Trustee -notlike 'NT AUTHORITY\SELF' -and $_.Trustee -notlike 'S-1-5-*' -and $_.AccessControlType -eq 'Allow' } + foreach ($p in $sendAs) { + $rights = @($p.AccessRights) -join ', ' + $entries.Add([pscustomobject]@{ + permission_type = 'SendAs' + object_type = 'Mailbox' + object = $mb.UserPrincipalName + principal = [string]$p.Trustee + role_name = $rights + }) + } + } catch { + $warnings.Add("RecipientPermission: $($_.Exception.Message)") + } + + # 3) Send on Behalf — from mailbox property + try { + if ($mb.GrantSendOnBehalfTo) { + foreach ($t in $mb.GrantSendOnBehalfTo) { + $entries.Add([pscustomobject]@{ + permission_type = 'SendOnBehalf' + object_type = 'Mailbox' + object = $mb.UserPrincipalName + principal = [string]$t + role_name = 'SendOnBehalf' + }) + } + } + } catch { + $warnings.Add("GrantSendOnBehalfTo: $($_.Exception.Message)") + } + + # 4) Folder-level delegations on Calendar and Inbox + foreach ($folder in 'Calendar', 'Inbox') { + try { + $folderPath = "{0}:\{1}" -f $mb.UserPrincipalName, $folder + $fp = Get-EXOMailboxFolderPermission -Identity $folderPath -ErrorAction Stop | + Where-Object { $_.User.DisplayName -notin @('Default', 'Anonymous') -and $_.AccessRights -notcontains 'None' } + foreach ($p in $fp) { + $rights = @($p.AccessRights) -join ', ' + $entries.Add([pscustomobject]@{ + permission_type = "Folder:$folder" + object_type = 'MailboxFolder' + object = "$($mb.UserPrincipalName)/$folder" + principal = [string]$p.User.DisplayName + role_name = $rights + }) + } + } catch { + $warnings.Add("FolderPermission ${folder}: $($_.Exception.Message)") + } + } + + Write-JsonResult @{ + ok = $true + mailbox = $mb.UserPrincipalName + entries = $entries + warnings = $warnings + } +} catch { + Write-JsonResult @{ ok = $false; error = $_.Exception.Message } +} finally { + try { Disconnect-ExchangeOnline -Confirm:$false -InformationAction SilentlyContinue -ErrorAction SilentlyContinue | Out-Null } catch {} +} diff --git a/containers/clearview/src/clearview_app/scanners/exo_scripts/list-mailboxes.ps1 b/containers/clearview/src/clearview_app/scanners/exo_scripts/list-mailboxes.ps1 new file mode 100644 index 0000000..0a7d163 --- /dev/null +++ b/containers/clearview/src/clearview_app/scanners/exo_scripts/list-mailboxes.ps1 @@ -0,0 +1,67 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory=$true)][string]$TenantId, + [Parameter(Mandatory=$true)][string]$ClientId, + [Parameter(Mandatory=$true)][string]$Organization, + [Parameter(Mandatory=$true)][string]$CertPath, + [Parameter(Mandatory=$false)][int]$MaxMailboxes = 50000 +) + +$ErrorActionPreference = 'Stop' +$ProgressPreference = 'SilentlyContinue' + +function Write-JsonResult { + param($Payload) + Write-Output ($Payload | ConvertTo-Json -Depth 4 -Compress) +} + +try { + Import-Module ExchangeOnlineManagement -ErrorAction Stop +} catch { + Write-JsonResult @{ ok = $false; error = "ExchangeOnlineManagement module not available: $($_.Exception.Message)" } + exit 0 +} + +try { + $pfxPwd = $env:CLEARVIEW_PFX_PASSWORD + if ([string]::IsNullOrEmpty($pfxPwd)) { + Write-JsonResult @{ ok = $false; error = "CLEARVIEW_PFX_PASSWORD not set in environment" } + exit 0 + } + $securePwd = ConvertTo-SecureString -String $pfxPwd -AsPlainText -Force + Connect-ExchangeOnline ` + -AppId $ClientId ` + -Organization $Organization ` + -CertificateFilePath $CertPath ` + -CertificatePassword $securePwd ` + -ShowBanner:$false ` + -ShowProgress:$false ` + -ErrorAction Stop | Out-Null +} catch { + Write-JsonResult @{ ok = $false; error = "Connect-ExchangeOnline failed: $($_.Exception.Message)" } + exit 0 +} + +try { + $boxes = Get-EXOMailbox -ResultSize Unlimited -PropertySets Minimum -ErrorAction Stop | + Select-Object -ExpandProperty UserPrincipalName + + if ($boxes.Count -gt $MaxMailboxes) { + Write-JsonResult @{ + ok = $false + error = "Mailbox count $($boxes.Count) exceeds MaxMailboxes=$MaxMailboxes" + count = $boxes.Count + } + exit 0 + } + + Write-JsonResult @{ + ok = $true + count = $boxes.Count + mailboxes = $boxes + } +} catch { + Write-JsonResult @{ ok = $false; error = $_.Exception.Message } +} finally { + try { Disconnect-ExchangeOnline -Confirm:$false -InformationAction SilentlyContinue -ErrorAction SilentlyContinue | Out-Null } catch {} +} diff --git a/containers/clearview/src/clearview_app/scanners/exo_scripts/probe.ps1 b/containers/clearview/src/clearview_app/scanners/exo_scripts/probe.ps1 new file mode 100644 index 0000000..253d2cd --- /dev/null +++ b/containers/clearview/src/clearview_app/scanners/exo_scripts/probe.ps1 @@ -0,0 +1,57 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory=$true)][string]$TenantId, + [Parameter(Mandatory=$true)][string]$ClientId, + [Parameter(Mandatory=$true)][string]$Organization, + [Parameter(Mandatory=$true)][string]$Mailbox, + [Parameter(Mandatory=$true)][string]$CertPath +) + +$ErrorActionPreference = 'Stop' +$ProgressPreference = 'SilentlyContinue' + +function Write-Result { + param([bool]$Ok, [string]$Message) + $obj = [pscustomobject]@{ ok = $Ok; message = $Message } + Write-Output ($obj | ConvertTo-Json -Compress) +} + +try { + Import-Module ExchangeOnlineManagement -ErrorAction Stop +} catch { + Write-Result -Ok $false -Message "ExchangeOnlineManagement module not available: $($_.Exception.Message)" + exit 0 +} + +try { + $pfxPwd = $env:CLEARVIEW_PFX_PASSWORD + if ([string]::IsNullOrEmpty($pfxPwd)) { + Write-Result -Ok $false -Message "CLEARVIEW_PFX_PASSWORD not set in environment" + exit 0 + } + $securePwd = ConvertTo-SecureString -String $pfxPwd -AsPlainText -Force + Connect-ExchangeOnline ` + -AppId $ClientId ` + -Organization $Organization ` + -CertificateFilePath $CertPath ` + -CertificatePassword $securePwd ` + -ShowBanner:$false ` + -ShowProgress:$false ` + -ErrorAction Stop | Out-Null +} catch { + Write-Result -Ok $false -Message "Connect-ExchangeOnline failed: $($_.Exception.Message)" + exit 0 +} + +try { + $box = Get-EXOMailbox -Identity $Mailbox -ErrorAction Stop -PropertySets Minimum + if ($null -eq $box) { + Write-Result -Ok $false -Message "Mailbox '$Mailbox' not found" + } else { + Write-Result -Ok $true -Message "OK" + } +} catch { + Write-Result -Ok $false -Message "Get-EXOMailbox failed: $($_.Exception.Message)" +} finally { + try { Disconnect-ExchangeOnline -Confirm:$false -InformationAction SilentlyContinue -ErrorAction SilentlyContinue | Out-Null } catch {} +}