<# .SYNOPSIS This is a sample Private function only visible within the module. .DESCRIPTION This sample function is not exported to the module and only return the data passed as parameter. .EXAMPLE $null = Get-CISExoOutput -PrivateData 'NOTHING TO SEE HERE' .PARAMETER PrivateData The PrivateData parameter is what will be returned without transformation. #> function Get-CISExoOutput { [cmdletBinding()] param( [Parameter(Mandatory = $true)] [String]$Rec ) begin { # Begin Block # <# # Tests 1.2.2 1.3.3 1.3.6 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.9 3.1.1 6.1.1 6.1.2 6.1.3 6.2.1 6.2.2 6.2.3 6.3.1 6.5.1 6.5.2 6.5.3 8.6.1 # Test number array $testNumbers = @('1.2.2', '1.3.3', '1.3.6', '2.1.1', '2.1.2', '2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', '6.5.1', '6.5.2', '6.5.3', '8.6.1') #> } process { try { Write-Verbose "Get-CISExoOutput: Retuning data for Rec: $Rec" switch ($Rec) { '1.2.2' { # Test-BlockSharedMailboxSignIn.ps1 $MBX = Get-EXOMailbox -RecipientTypeDetails SharedMailbox # [object[]] # $MBX mock object: <# $MBX = @( [PSCustomObject]@{ UserPrincipalName = "SMBuser1@domain.com" ExternalDirectoryObjectId = "123e4567-e89b-12d3-a456-426614174000" }, [PSCustomObject]@{ UserPrincipalName = "SMBuser2@domain.com" ExternalDirectoryObjectId = "987e6543-21ba-12d3-a456-426614174000" }, [PSCustomObject]@{ UserPrincipalName = "SMBuser3@domain.com" ExternalDirectoryObjectId = "abcddcba-98fe-76dc-a456-426614174000" } ) #> return $MBX.ExternalDirectoryObjectId } '1.3.3' { # Test-ExternalSharingCalendars.ps1 # Step: Retrieve sharing policies related to calendar sharing # $sharingPolicies Mock Object <# $sharingPolicies = [PSCustomObject]@{ Name = "Default Sharing Policy" Domains = @("Anonymous:CalendarSharingFreeBusySimple") Enabled = $true Default = $true } #> $sharingPolicies = Get-SharingPolicy | Where-Object { $_.Domains -like '*CalendarSharing*' } # [psobject[]] return $sharingPolicies } '1.3.6' { # Test-CustomerLockbox.ps1 # Step: Retrieve the organization configuration (Condition C: Pass/Fail) # $orgConfig Mock Object: <# # return $orgConfig $orgConfig = [PSCustomObject]@{ CustomerLockBoxEnabled = $true } #> $orgConfig = Get-OrganizationConfig | Select-Object CustomerLockBoxEnabled $customerLockboxEnabled = $orgConfig.CustomerLockBoxEnabled # [bool] return $customerLockboxEnabled } '2.1.1' { # Test-SafeLinksOfficeApps.ps1 if (Get-Command Get-SafeLinksPolicy -ErrorAction SilentlyContinue) { # 2.1.1 (L2) Ensure Safe Links for Office Applications is Enabled # Retrieve all Safe Links policies # $policies Mock Object: <# $policies = @( [PSCustomObject]@{ Name = "PolicyOne" EnableSafeLinksForEmail = $true EnableSafeLinksForTeams = $true EnableSafeLinksForOffice = $true TrackClicks = $true AllowClickThrough = $false }, [PSCustomObject]@{ Name = "PolicyTwo" EnableSafeLinksForEmail = $true EnableSafeLinksForTeams = $true EnableSafeLinksForOffice = $true TrackClicks = $true AllowClickThrough = $true }, [PSCustomObject]@{ Name = "PolicyThree" EnableSafeLinksForEmail = $true EnableSafeLinksForTeams = $true EnableSafeLinksForOffice = $true TrackClicks = $true AllowClickThrough = $false } ) #> $policies = Get-SafeLinksPolicy # Initialize the details collection $misconfiguredDetails = @() foreach ($policy in $policies) { # Get the detailed configuration of each policy $policyDetails = $policy #Get-SafeLinksPolicy -Identity $policy.Name # Check each required property and record failures # Condition A: Checking policy settings $failures = @() if ($policyDetails.EnableSafeLinksForEmail -ne $true) { $failures += 'EnableSafeLinksForEmail: False' } # Email: On if ($policyDetails.EnableSafeLinksForTeams -ne $true) { $failures += 'EnableSafeLinksForTeams: False' } # Teams: On if ($policyDetails.EnableSafeLinksForOffice -ne $true) { $failures += 'EnableSafeLinksForOffice: False' } # Office 365 Apps: On if ($policyDetails.TrackClicks -ne $true) { $failures += 'TrackClicks: False' } # Click protection settings: On if ($policyDetails.AllowClickThrough -ne $false) { $failures += 'AllowClickThrough: True' } # Do not track when users click safe links: Off # Only add details for policies that have misconfigurations if ($failures.Count -gt 0) { $misconfiguredDetails += "Policy: $($policy.Name); Failures: $($failures -join ', ')" } } # [object[]] return $misconfiguredDetails } else { return 1 } } '2.1.2' { # Test-CommonAttachmentFilter.ps1 # 2.1.2 (L1) Ensure the Common Attachment Types Filter is enabled # Condition A: The Common Attachment Types Filter is enabled in the Microsoft 365 Security & Compliance Center. # Condition B: Using Exchange Online PowerShell, verify that the `EnableFileFilter` property of the default malware filter policy is set to `True`. # Retrieve the attachment filter policy # $attachmentFilter Mock Object <# $attachmentFilter = [PSCustomObject]@{ EnableFileFilter = $true } #> $attachmentFilter = Get-MalwareFilterPolicy -Identity Default | Select-Object EnableFileFilter $result = $attachmentFilter.EnableFileFilter # [bool] return $result } '2.1.3' { # Test-NotifyMalwareInternal.ps1 # 2.1.3 Ensure notifications for internal users sending malware is Enabled # Retrieve all 'Custom' malware filter policies and check notification settings # $malwareNotifications Mock Object <# $malwareNotifications = @( [PSCustomObject]@{ Identity = "Default" EnableInternalSenderAdminNotifications = $true RecommendedPolicyType = "Custom" }, [PSCustomObject]@{ Identity = "Anti-malware-Policy" EnableInternalSenderAdminNotifications = $true RecommendedPolicyType = "Custom" } ) #> $malwareNotifications = Get-MalwareFilterPolicy | Where-Object { $_.RecommendedPolicyType -eq 'Custom' } # [object[]] return $malwareNotifications } '2.1.4' { # Test-SafeAttachmentsPolicy.ps1 if (Get-Command Get-SafeAttachmentPolicy -ErrorAction SilentlyContinue) { # Retrieve all Safe Attachment policies where Enable is set to True # Check if ErrorAction needed below # $safeAttachmentPolicies Mock Object: <# $safeAttachmentPolicies = @( [PSCustomObject]@{ Policy = "Strict Preset Security Policy" Action = "Block" QuarantineTag = "AdminOnlyAccessPolicy" Redirect = $false Enabled = $true } ) #> $safeAttachmentPolicies = Get-SafeAttachmentPolicy -ErrorAction SilentlyContinue | Where-Object { $_.Enable -eq $true } $safeAttachmentRules = Get-SafeAttachmentRule # [object[]] return $safeAttachmentPolicies, $safeAttachmentRules else { return 1, 1 } } } '2.1.5' { # Test-SafeAttachmentsTeams.ps1 if (Get-Command Get-AtpPolicyForO365 -ErrorAction SilentlyContinue) { # 2.1.5 (L2) Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled # Retrieve the ATP policies for Office 365 and check Safe Attachments settings $atpPolicies = Get-AtpPolicyForO365 # Check if the required ATP policies are enabled # $atpPolicyResult Mock Object: <# $atpPolicyResult = @( [PSCustomObject]@{ Name = "Default" EnableATPForSPOTeamsODB = $true EnableSafeDocs = $true AllowSafeDocsOpen = $false } ) #> $atpPolicyResult = $atpPolicies | Where-Object { $_.EnableATPForSPOTeamsODB -eq $true -and $_.EnableSafeDocs -eq $true -and $_.AllowSafeDocsOpen -eq $false } # [psobject[]] return $atpPolicyResult } else { return 1 } } '2.1.6' { # Test-SpamPolicyAdminNotify.ps1 # Retrieve the hosted outbound spam filter policies <# # Mock data representing multiple spam filter policies $spamPolicies = @( [PSCustomObject]@{ Name = "Default" IsDefault = $true NotifyOutboundSpam = $true BccSuspiciousOutboundMail = $true NotifyOutboundSpamRecipients = "admin@example.com" BccSuspiciousOutboundAdditionalRecipients = "bccadmin@example.com" }, [PSCustomObject]@{ Name = "Custom Policy 1" IsDefault = $false NotifyOutboundSpam = $false BccSuspiciousOutboundMail = $true NotifyOutboundSpamRecipients = "" BccSuspiciousOutboundAdditionalRecipients = "" }, [PSCustomObject]@{ Name = "Custom Policy 2" IsDefault = $false NotifyOutboundSpam = $true BccSuspiciousOutboundMail = $false NotifyOutboundSpamRecipients = "notify@example.com" BccSuspiciousOutboundAdditionalRecipients = "bccnotify@example.com" } ) #> $spamPolicies = Get-HostedOutboundSpamFilterPolicy return $spamPolicies } '2.1.7' { # v4 needs same info. # Test-AntiPhishingPolicy.ps1 <# $antiPhishPolicies = @( [PSCustomObject]@{ Identity = "Strict Preset Security Policy" Enabled = $true PhishThresholdLevel = 4 EnableMailboxIntelligenceProtection = $true EnableMailboxIntelligence = $true EnableSpoofIntelligence = $true TargetedUsersToProtect = "John Doe;jdoe@contoso.net, Jane Does;janedoe@contoso.net" }, [PSCustomObject]@{ Identity = "Office365 AntiPhish Default" Enabled = $true PhishThresholdLevel = 2 EnableMailboxIntelligenceProtection = $true EnableMailboxIntelligence = $true EnableSpoofIntelligence = $true TargetedUsersToProtect = $null # Assuming it targets all users as it's the default }, [PSCustomObject]@{ Identity = "Admin" Enabled = $true PhishThresholdLevel = 2 EnableMailboxIntelligenceProtection = $true EnableMailboxIntelligence = $true EnableSpoofIntelligence = $true TargetedUsersToProtect = $null # Assuming it targets all users }, [PSCustomObject]@{ Identity = "Standard Preset Security Policy" Enabled = $true PhishThresholdLevel = 3 EnableMailboxIntelligenceProtection = $true EnableMailboxIntelligence = $true EnableSpoofIntelligence = $true TargetedUsersToProtect = $null # Assuming it targets all users } ) #> $antiPhishPolicies = Get-AntiPhishPolicy if ($script:Version400) { Write-Verbose 'Retrieving associated AntiPhishRules...' $antiPhishRules = Get-AntiPhishRule return $antiPhishPolicies, $antiPhishRules } else { return $antiPhishPolicies } } '2.1.9' { # Test-EnableDKIM.ps1 # 2.1.9 (L1) Ensure DKIM is enabled for all Exchange Online Domains # Retrieve DKIM configuration for all domains $dkimConfig = Get-DkimSigningConfig | Select-Object Domain, Enabled # [object[]] return $dkimConfig } '2.1.11' { # Test-CommonAttachmentFilter.ps1 for Comprehensive Attachment Filtering Write-Verbose 'Retrieving Malware Filter Policies, Rules, and Extensions for 2.1.11...' # Retrieve all malware filter policies $malwarePolicies = Get-MalwareFilterPolicy # Retrieve all malware filter rules $malwareRules = Get-MalwareFilterRule # Predefined list of L2 extensions from the benchmark $L2Extensions = @( '7z', 'a3x', 'ace', 'ade', 'adp', 'ani', 'app', 'appinstaller', 'applescript', 'application', 'appref-ms', 'appx', 'appxbundle', 'arj', 'asd', 'asx', 'bas', 'bat', 'bgi', 'bz2', 'cab', 'chm', 'cmd', 'com', 'cpl', 'crt', 'cs', 'csh', 'daa', 'dbf', 'dcr', 'deb', 'desktopthemepackfile', 'dex', 'diagcab', 'dif', 'dir', 'dll', 'dmg', 'doc', 'docm', 'dot', 'dotm', 'elf', 'eml', 'exe', 'fxp', 'gadget', 'gz', 'hlp', 'hta', 'htc', 'htm', 'htm', 'html', 'html', 'hwpx', 'ics', 'img', 'inf', 'ins', 'iqy', 'iso', 'isp', 'jar', 'jnlp', 'js', 'jse', 'kext', 'ksh', 'lha', 'lib', 'library-ms', 'lnk', 'lzh', 'macho', 'mam', 'mda', 'mdb', 'mde', 'mdt', 'mdw', 'mdz', 'mht', 'mhtml', 'mof', 'msc', 'msi', 'msix', 'msp', 'msrcincident', 'mst', 'ocx', 'odt', 'ops', 'oxps', 'pcd', 'pif', 'plg', 'pot', 'potm', 'ppa', 'ppam', 'ppkg', 'pps', 'ppsm', 'ppt', 'pptm', 'prf', 'prg', 'ps1', 'ps11', 'ps11xml', 'ps1xml', 'ps2', 'ps2xml', 'psc1', 'psc2', 'pub', 'py', 'pyc', 'pyo', 'pyw', 'pyz', 'pyzw', 'rar', 'reg', 'rev', 'rtf', 'scf', 'scpt', 'scr', 'sct', 'searchConnector-ms', 'service', 'settingcontent-ms', 'sh', 'shb', 'shs', 'shtm', 'shtml', 'sldm', 'slk', 'so', 'spl', 'stm', 'svg', 'swf', 'sys', 'tar', 'theme', 'themepack', 'timer', 'uif', 'url', 'uue', 'vb', 'vbe', 'vbs', 'vhd', 'vhdx', 'vxd', 'wbk', 'website', 'wim', 'wiz', 'ws', 'wsc', 'wsf', 'wsh', 'xla', 'xlam', 'xlc', 'xll', 'xlm', 'xls', 'xlsb', 'xlsm', 'xlt', 'xltm', 'xlw', 'xnk', 'xps', 'xsl', 'xz', 'z' ) # Return all required objects for evaluation return $malwarePolicies, $malwareRules, $L2Extensions } '2.1.12' { # Placeholder - Test-ConnectionFilterIPAllowList } '2.1.13' { # Placeholder - Test-ConnectionFilterSafeList } '2.1.14' { # Placeholder - Test-InboundAntiSpamPolicies } '3.1.1' { # Test-AuditLogSearch.ps1 # 3.1.1 (L1) Ensure Microsoft 365 audit log search is Enabled # Retrieve the audit log configuration $auditLogConfig = Get-AdminAuditLogConfig | Select-Object UnifiedAuditLogIngestionEnabled # $auditLogResult = $auditLogConfig.UnifiedAuditLogIngestionEnabled # [bool] return $auditLogResult } '6.1.1' { # Test-AuditDisabledFalse.ps1 # 6.1.1 (L1) Ensure 'AuditDisabled' organizationally is set to 'False' # Retrieve the AuditDisabled configuration (Condition B) $auditDisabledConfig = Get-OrganizationConfig | Select-Object AuditDisabled # [bool] $auditNotDisabled = -not $auditDisabledConfig.AuditDisabled return $auditNotDisabled } '6.1.2' { # Test-MailboxAuditingE3.ps1 $mailboxes = Get-EXOMailbox -PropertySets Audit # [object[]] return $mailboxes } '6.1.3' { # Test-MailboxAuditingE5.ps1 $mailboxes = Get-EXOMailbox -PropertySets Audit # [object[]] return $mailboxes } '6.1.4' { # Placeholder - Test-AuditBypassEnabled } '6.2.1' { # Test-BlockMailForwarding.ps1 # 6.2.1 (L1) Ensure all forms of mail forwarding are blocked and/or disabled # Step 1: Retrieve the transport rules that redirect messages $transportRules = Get-TransportRule | Where-Object { $null -ne $_.RedirectMessageTo } if ($null -eq $transportRules) { $transportRules = 1 } # Step 2: Check all anti-spam outbound policies $outboundSpamPolicies = Get-HostedOutboundSpamFilterPolicy $nonCompliantSpamPolicies = $outboundSpamPolicies | Where-Object { $_.AutoForwardingMode -ne 'Off' } return $transportRules, $nonCompliantSpamPolicies } '6.2.2' { # Test-NoWhitelistDomains.ps1 # 6.2.2 (L1) Ensure mail transport rules do not whitelist specific domains # Retrieve transport rules that whitelist specific domains # Condition A: Checking for transport rules that whitelist specific domains # [object[]] $whitelistedRules = Get-TransportRule | Where-Object { $_.SetSCL -eq -1 -and $null -ne $_.SenderDomainIs } return $whitelistedRules } '6.2.3' { # Test-IdentifyExternalEmail.ps1 # 6.2.3 (L1) Ensure email from external senders is identified # Retrieve external sender tagging configuration # [object[]] $externalInOutlook = Get-ExternalInOutlook return $externalInOutlook } '6.3.1' { # Test-RestrictOutlookAddins.ps1 # 6.3.1 (L2) Ensure users installing Outlook add-ins is not allowed $customPolicyFailures = @() # Check all mailboxes for custom policies with unallowed add-ins $roleAssignmentPolicies = Get-EXOMailbox | Select-Object -Unique RoleAssignmentPolicy if ($roleAssignmentPolicies.RoleAssignmentPolicy) { foreach ($policy in $roleAssignmentPolicies) { if ($policy.RoleAssignmentPolicy) { $rolePolicyDetails = Get-RoleAssignmentPolicy -Identity $policy.RoleAssignmentPolicy $foundRoles = $rolePolicyDetails.AssignedRoles | Where-Object { $_ -in $relevantRoles } # Condition B: Using PowerShell, verify that MyCustomApps, MyMarketplaceApps, and MyReadWriteMailboxApps are not assigned to users. if ($foundRoles) { $customPolicyFailures += "Policy: $($policy.RoleAssignmentPolicy): Roles: $($foundRoles -join ', ')" } } } } # Check Default Role Assignment Policy $defaultPolicy = Get-RoleAssignmentPolicy 'Default Role Assignment Policy' return $customPolicyFailures, $defaultPolicy } '6.5.1' { # Test-ModernAuthExchangeOnline.ps1 # Ensuring the ExchangeOnlineManagement module is available # 6.5.1 (L1) Ensure modern authentication for Exchange Online is enabled # Check modern authentication setting in Exchange Online configuration (Condition A and B) $orgConfig = Get-OrganizationConfig | Select-Object -Property Name, OAuth2ClientProfileEnabled return $orgConfig } '6.5.2' { # Test-MailTipsEnabled.ps1 # 6.5.2 (L2) Ensure MailTips are enabled for end users # Retrieve organization configuration for MailTips settings # [object] $orgConfig = Get-OrganizationConfig | Select-Object MailTipsAllTipsEnabled, MailTipsExternalRecipientsTipsEnabled, MailTipsGroupMetricsEnabled, MailTipsLargeAudienceThreshold return $orgConfig } '6.5.3' { # Test-RestrictStorageProvidersOutlook.ps1 # 6.5.3 (L2) Ensure additional storage providers are restricted in Outlook on the web # Retrieve all OwaMailbox policies # [object[]] $owaPolicies = Get-OwaMailboxPolicy return $owaPolicies } '8.6.1' { # Test-ReportSecurityInTeams.ps1 # 8.6.1 (L1) Ensure users can report security concerns in Teams # Retrieve the necessary settings for Teams and Exchange Online # Condition B: Verify that 'Monitor reported messages in Microsoft Teams' is checked in the Microsoft 365 Defender portal. # Condition C: Ensure the 'Send reported messages to' setting in the Microsoft 365 Defender portal is set to 'My reporting mailbox only' with the correct report email addresses. # $ReportSubmissionPolicy Mock Object <# $ReportSubmissionPolicy = [PSCustomObject]@{ ReportJunkToCustomizedAddress = $true ReportNotJunkToCustomizedAddress = $true ReportPhishToCustomizedAddress = $true ReportJunkAddresses = @('security@example.com') ReportNotJunkAddresses = @('security@example.com') ReportPhishAddresses = @('security@example.com') ReportChatMessageEnabled = $false ReportChatMessageToCustomizedAddressEnabled = $false } #> $ReportSubmissionPolicy = Get-ReportSubmissionPolicy | Select-Object -Property ReportJunkToCustomizedAddress, ReportNotJunkToCustomizedAddress, ReportPhishToCustomizedAddress, ReportJunkAddresses, ReportNotJunkAddresses, ReportPhishAddresses, ReportChatMessageEnabled, ReportChatMessageToCustomizedAddressEnabled return $ReportSubmissionPolicy } default { throw "No match found for test: $Rec" } } } catch { throw "Get-CISExoOutput: `n$_" } } end { Write-Verbose "Retuning data for Rec: $Rec" } } # end function Get-CISExoOutput