From 5125d7f6842e3d4c7c4a72b236dcb2952de8a5f8 Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sun, 30 Jun 2024 09:21:41 -0500 Subject: [PATCH 01/21] fix: Corrected logic for 8.1.1 --- source/tests/Test-TeamsExternalFileSharing.ps1 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/source/tests/Test-TeamsExternalFileSharing.ps1 b/source/tests/Test-TeamsExternalFileSharing.ps1 index 9fe0374..a830713 100644 --- a/source/tests/Test-TeamsExternalFileSharing.ps1 +++ b/source/tests/Test-TeamsExternalFileSharing.ps1 @@ -27,17 +27,18 @@ function Test-TeamsExternalFileSharing { # Assuming that 'approvedProviders' is a list of approved cloud storage service names # This list must be defined according to your organization's approved cloud storage services $clientConfig = Get-CISMSTeamsOutput -Rec $recnum - $approvedProviders = @("AllowDropBox", "AllowBox", "AllowGoogleDrive", "AllowShareFile", "AllowEgnyte") + $unapprovedProviders = @("AllowDropBox", "AllowBox", "AllowGoogleDrive", "AllowShareFile", "AllowEgnyte") $isCompliant = $true $nonCompliantProviders = @() - foreach ($provider in $approvedProviders) { - if (-not $clientConfig.$provider) { + foreach ($provider in $unapprovedProviders) { + if ($clientConfig.$provider) { $isCompliant = $false $nonCompliantProviders += $provider } } + # Create an instance of CISAuditResult and populate it # Create an instance of CISAuditResult and populate it $params = @{ Rec = $recnum From fc9ff5757606cd532bf77041e90ccb960cc7e13e Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sun, 30 Jun 2024 10:11:25 -0500 Subject: [PATCH 02/21] fix: Add parameter for approved storage providers for 8.1.1 --- source/Private/Get-CISMSTeamsOutput.ps1 | 1 + source/Private/Invoke-TestFunction.ps1 | 9 +++-- source/Public/Invoke-M365SecurityAudit.ps1 | 32 ++++++++++----- .../tests/Test-TeamsExternalFileSharing.ps1 | 40 +++++++++++++------ 4 files changed, 58 insertions(+), 24 deletions(-) diff --git a/source/Private/Get-CISMSTeamsOutput.ps1 b/source/Private/Get-CISMSTeamsOutput.ps1 index 6c157cb..58a69e2 100644 --- a/source/Private/Get-CISMSTeamsOutput.ps1 +++ b/source/Private/Get-CISMSTeamsOutput.ps1 @@ -49,6 +49,7 @@ function Get-CISMSTeamsOutput { # Assuming that 'approvedProviders' is a list of approved cloud storage service names # This list must be defined according to your organization's approved cloud storage services + # Add option for approved providers. $clientConfig = Get-CsTeamsClientConfiguration return $clientConfig } diff --git a/source/Private/Invoke-TestFunction.ps1 b/source/Private/Invoke-TestFunction.ps1 index ac5ca2c..f1021ed 100644 --- a/source/Private/Invoke-TestFunction.ps1 +++ b/source/Private/Invoke-TestFunction.ps1 @@ -3,9 +3,10 @@ function Invoke-TestFunction { param ( [Parameter(Mandatory = $true)] [PSObject]$FunctionFile, - [Parameter(Mandatory = $false)] - [string]$DomainName + [string]$DomainName, + [Parameter(Mandatory = $false)] + [string[]]$ApprovedCloudStorageProvider ) $functionName = $FunctionFile.BaseName @@ -16,7 +17,9 @@ function Invoke-TestFunction { if ('DomainName' -in $functionCmd.Parameters.Keys) { $paramList.DomainName = $DomainName } - + if ('ApprovedCloudStorageProvider' -in $functionCmd.Parameters.Keys) { + $paramList.ApprovedCloudStorageProvider = $ApprovedCloudStorageProvider + } # Use splatting to pass parameters Write-Verbose "Running $functionName..." try { diff --git a/source/Public/Invoke-M365SecurityAudit.ps1 b/source/Public/Invoke-M365SecurityAudit.ps1 index feea04b..ca10872 100644 --- a/source/Public/Invoke-M365SecurityAudit.ps1 +++ b/source/Public/Invoke-M365SecurityAudit.ps1 @@ -21,6 +21,8 @@ Specifies specific recommendations to include in the audit. Accepts an array of recommendation numbers. .PARAMETER SkipRecommendation Specifies specific recommendations to exclude from the audit. Accepts an array of recommendation numbers. + .PARAMETER ApprovedCloudStorageProvider + Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names. .PARAMETER DoNotConnect If specified, the cmdlet will not establish a connection to Microsoft 365 services. .PARAMETER DoNotDisconnect @@ -134,26 +136,26 @@ function Invoke-M365SecurityAudit { [string]$DomainName, # E-Level with optional ProfileLevel selection - [Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter')] + [Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter', HelpMessage = "Specifies the E-Level (E3 or E5) for the audit.")] [ValidateSet('E3', 'E5')] [string]$ELevel, - [Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter')] + [Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter', HelpMessage = "Specifies the profile level (L1 or L2) for the audit.")] [ValidateSet('L1', 'L2')] [string]$ProfileLevel, # IG Filters, one at a time - [Parameter(Mandatory = $true, ParameterSetName = 'IG1Filter')] + [Parameter(Mandatory = $true, ParameterSetName = 'IG1Filter', HelpMessage = "Includes tests where IG1 is true.")] [switch]$IncludeIG1, - [Parameter(Mandatory = $true, ParameterSetName = 'IG2Filter')] + [Parameter(Mandatory = $true, ParameterSetName = 'IG2Filter', HelpMessage = "Includes tests where IG2 is true.")] [switch]$IncludeIG2, - [Parameter(Mandatory = $true, ParameterSetName = 'IG3Filter')] + [Parameter(Mandatory = $true, ParameterSetName = 'IG3Filter', HelpMessage = "Includes tests where IG3 is true.")] [switch]$IncludeIG3, # Inclusion of specific recommendation numbers - [Parameter(Mandatory = $true, ParameterSetName = 'RecFilter')] + [Parameter(Mandatory = $true, ParameterSetName = 'RecFilter', HelpMessage = "Specifies specific recommendations to include in the audit. Accepts an array of recommendation numbers.")] [ValidateSet( '1.1.1', '1.1.3', '1.2.1', '1.2.2', '1.3.1', '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', '5.1.2.3', ` @@ -166,7 +168,7 @@ function Invoke-M365SecurityAudit { [string[]]$IncludeRecommendation, # Exclusion of specific recommendation numbers - [Parameter(Mandatory = $true, ParameterSetName = 'SkipRecFilter')] + [Parameter(Mandatory = $true, ParameterSetName = 'SkipRecFilter', HelpMessage = "Specifies specific recommendations to exclude from the audit. Accepts an array of recommendation numbers.")] [ValidateSet( '1.1.1', '1.1.3', '1.2.1', '1.2.2', '1.3.1', '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', '5.1.2.3', ` @@ -179,12 +181,24 @@ function Invoke-M365SecurityAudit { [string[]]$SkipRecommendation, # Common parameters for all parameter sets + [Parameter(Mandatory = $false, HelpMessage = "Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names.")] + [ValidateSet( + 'GoogleDrive', 'ShareFile', 'Box', 'DropBox', 'Egnyte' + )] + [string[]]$ApprovedCloudStorageProvider = @(), + + [Parameter(Mandatory = $false, HelpMessage = "Specifies that the cmdlet will not establish a connection to Microsoft 365 services.")] [switch]$DoNotConnect, + + [Parameter(Mandatory = $false, HelpMessage = "Specifies that the cmdlet will not disconnect from Microsoft 365 services after execution.")] [switch]$DoNotDisconnect, + + [Parameter(Mandatory = $false, HelpMessage = "Specifies that the cmdlet will not check for the presence of required modules.")] [switch]$NoModuleCheck, + + [Parameter(Mandatory = $false, HelpMessage = "Specifies that the cmdlet will not prompt for confirmation before proceeding with established connections and will disconnect from all of them.")] [switch]$DoNotConfirmConnections ) - Begin { if ($script:MaximumFunctionCount -lt 8192) { $script:MaximumFunctionCount = 8192 @@ -286,7 +300,7 @@ function Invoke-M365SecurityAudit { Write-Progress -Activity "Executing Tests" -Status "Executing $($currentTestIndex) of $($totalTests): $($testFunction.Name)" -PercentComplete (($currentTestIndex / $totalTests) * 100) $functionName = $testFunction.BaseName if ($PSCmdlet.ShouldProcess($functionName, "Execute test")) { - $auditResult = Invoke-TestFunction -FunctionFile $testFunction -DomainName $DomainName + $auditResult = Invoke-TestFunction -FunctionFile $testFunction -DomainName $DomainName -ApprovedCloudStorageProvider $ApprovedCloudStorageProvider # Add the result to the collection [void]$allAuditResults.Add($auditResult) } diff --git a/source/tests/Test-TeamsExternalFileSharing.ps1 b/source/tests/Test-TeamsExternalFileSharing.ps1 index a830713..f4e48ec 100644 --- a/source/tests/Test-TeamsExternalFileSharing.ps1 +++ b/source/tests/Test-TeamsExternalFileSharing.ps1 @@ -2,17 +2,15 @@ function Test-TeamsExternalFileSharing { [CmdletBinding()] [OutputType([CISAuditResult])] param ( - # Aligned - # Parameters can be added here if needed + [Parameter(Mandatory = $false)] + [string[]]$ApprovedCloudStorageProvider ) - begin { # Dot source the class script if necessary # . .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed $recnum = "8.1.1" } - process { try { # 8.1.1 (L2) Ensure external file sharing in Teams is enabled for only approved cloud storage services @@ -26,25 +24,44 @@ function Test-TeamsExternalFileSharing { # Assuming that 'approvedProviders' is a list of approved cloud storage service names # This list must be defined according to your organization's approved cloud storage services + # Retrieve the current Teams client configuration $clientConfig = Get-CISMSTeamsOutput -Rec $recnum - $unapprovedProviders = @("AllowDropBox", "AllowBox", "AllowGoogleDrive", "AllowShareFile", "AllowEgnyte") + # Testing + #$clientconfig.AllowGoogleDrive = $false + #$clientconfig.AllowBox = $false + #$clientconfig.AllowShareFile = $false + #$clientconfig.AllowEgnyte = $false + #$clientconfig.AllowDropBox = $false + # Define all possible cloud storage providers + $allProviders = @("AllowDropBox", "AllowBox", "AllowGoogleDrive", "AllowShareFile", "AllowEgnyte") + # If ApprovedCloudStorageProvider is provided, map it to the corresponding settings + if ($PSBoundParameters.ContainsKey('ApprovedCloudStorageProvider')) { + $approvedProviders = @() + foreach ($provider in $ApprovedCloudStorageProvider) { + $approvedProviders += "Allow$provider" + } + } else { + # Default approved providers + $approvedProviders = @() + } $isCompliant = $true $nonCompliantProviders = @() - - foreach ($provider in $unapprovedProviders) { - if ($clientConfig.$provider) { + foreach ($provider in $allProviders) { + if ($clientConfig.$provider -and -not $approvedProviders.Contains($provider)) { $isCompliant = $false $nonCompliantProviders += $provider } } - - # Create an instance of CISAuditResult and populate it + $basePassDetails = "All cloud storage services are approved providers" + if ($ApprovedCloudStorageProvider) { + $basePassDetails = "Approved cloud storage services: $($ApprovedCloudStorageProvider -join ', ')" + } # Create an instance of CISAuditResult and populate it $params = @{ Rec = $recnum Result = $isCompliant Status = if ($isCompliant) { "Pass" } else { "Fail" } - Details = if (-not $isCompliant) { "Non-approved providers enabled: $($nonCompliantProviders -join ', ')" } else { "All cloud storage services are approved providers" } + Details = if (-not $isCompliant) { "Non-approved providers enabled: $($nonCompliantProviders -join ', ')" } else { $basePassDetails } FailureReason = if (-not $isCompliant) { "The following non-approved providers are enabled: $($nonCompliantProviders -join ', ')" } else { "N/A" } } $auditResult = Initialize-CISAuditResult @params @@ -54,7 +71,6 @@ function Test-TeamsExternalFileSharing { $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return auditResult return $auditResult From 20124cdbb57ed4a1fd66602ff5bc95aa3ecbe964 Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sun, 30 Jun 2024 10:17:10 -0500 Subject: [PATCH 03/21] fix: Add plurality to approved storage providers parameter for 8.1.1 --- source/Private/Invoke-TestFunction.ps1 | 6 +++--- source/Public/Invoke-M365SecurityAudit.ps1 | 6 +++--- source/tests/Test-TeamsExternalFileSharing.ps1 | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/source/Private/Invoke-TestFunction.ps1 b/source/Private/Invoke-TestFunction.ps1 index f1021ed..b708652 100644 --- a/source/Private/Invoke-TestFunction.ps1 +++ b/source/Private/Invoke-TestFunction.ps1 @@ -6,7 +6,7 @@ function Invoke-TestFunction { [Parameter(Mandatory = $false)] [string]$DomainName, [Parameter(Mandatory = $false)] - [string[]]$ApprovedCloudStorageProvider + [string[]]$ApprovedCloudStorageProviders ) $functionName = $FunctionFile.BaseName @@ -17,8 +17,8 @@ function Invoke-TestFunction { if ('DomainName' -in $functionCmd.Parameters.Keys) { $paramList.DomainName = $DomainName } - if ('ApprovedCloudStorageProvider' -in $functionCmd.Parameters.Keys) { - $paramList.ApprovedCloudStorageProvider = $ApprovedCloudStorageProvider + if ('ApprovedCloudStorageProviders' -in $functionCmd.Parameters.Keys) { + $paramList.ApprovedCloudStorageProviders = $ApprovedCloudStorageProviders } # Use splatting to pass parameters Write-Verbose "Running $functionName..." diff --git a/source/Public/Invoke-M365SecurityAudit.ps1 b/source/Public/Invoke-M365SecurityAudit.ps1 index ca10872..f9f0235 100644 --- a/source/Public/Invoke-M365SecurityAudit.ps1 +++ b/source/Public/Invoke-M365SecurityAudit.ps1 @@ -21,7 +21,7 @@ Specifies specific recommendations to include in the audit. Accepts an array of recommendation numbers. .PARAMETER SkipRecommendation Specifies specific recommendations to exclude from the audit. Accepts an array of recommendation numbers. - .PARAMETER ApprovedCloudStorageProvider + .PARAMETER ApprovedCloudStorageProviders Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names. .PARAMETER DoNotConnect If specified, the cmdlet will not establish a connection to Microsoft 365 services. @@ -185,7 +185,7 @@ function Invoke-M365SecurityAudit { [ValidateSet( 'GoogleDrive', 'ShareFile', 'Box', 'DropBox', 'Egnyte' )] - [string[]]$ApprovedCloudStorageProvider = @(), + [string[]]$ApprovedCloudStorageProviders = @(), [Parameter(Mandatory = $false, HelpMessage = "Specifies that the cmdlet will not establish a connection to Microsoft 365 services.")] [switch]$DoNotConnect, @@ -300,7 +300,7 @@ function Invoke-M365SecurityAudit { Write-Progress -Activity "Executing Tests" -Status "Executing $($currentTestIndex) of $($totalTests): $($testFunction.Name)" -PercentComplete (($currentTestIndex / $totalTests) * 100) $functionName = $testFunction.BaseName if ($PSCmdlet.ShouldProcess($functionName, "Execute test")) { - $auditResult = Invoke-TestFunction -FunctionFile $testFunction -DomainName $DomainName -ApprovedCloudStorageProvider $ApprovedCloudStorageProvider + $auditResult = Invoke-TestFunction -FunctionFile $testFunction -DomainName $DomainName -ApprovedCloudStorageProviders $ApprovedCloudStorageProviders # Add the result to the collection [void]$allAuditResults.Add($auditResult) } diff --git a/source/tests/Test-TeamsExternalFileSharing.ps1 b/source/tests/Test-TeamsExternalFileSharing.ps1 index f4e48ec..a709c20 100644 --- a/source/tests/Test-TeamsExternalFileSharing.ps1 +++ b/source/tests/Test-TeamsExternalFileSharing.ps1 @@ -3,7 +3,7 @@ function Test-TeamsExternalFileSharing { [OutputType([CISAuditResult])] param ( [Parameter(Mandatory = $false)] - [string[]]$ApprovedCloudStorageProvider + [string[]]$ApprovedCloudStorageProviders ) begin { # Dot source the class script if necessary @@ -34,10 +34,10 @@ function Test-TeamsExternalFileSharing { #$clientconfig.AllowDropBox = $false # Define all possible cloud storage providers $allProviders = @("AllowDropBox", "AllowBox", "AllowGoogleDrive", "AllowShareFile", "AllowEgnyte") - # If ApprovedCloudStorageProvider is provided, map it to the corresponding settings - if ($PSBoundParameters.ContainsKey('ApprovedCloudStorageProvider')) { + # If ApprovedCloudStorageProviders is provided, map it to the corresponding settings + if ($PSBoundParameters.ContainsKey('ApprovedCloudStorageProviders')) { $approvedProviders = @() - foreach ($provider in $ApprovedCloudStorageProvider) { + foreach ($provider in $ApprovedCloudStorageProviders) { $approvedProviders += "Allow$provider" } } else { @@ -53,8 +53,8 @@ function Test-TeamsExternalFileSharing { } } $basePassDetails = "All cloud storage services are approved providers" - if ($ApprovedCloudStorageProvider) { - $basePassDetails = "Approved cloud storage services: $($ApprovedCloudStorageProvider -join ', ')" + if ($ApprovedCloudStorageProviders) { + $basePassDetails = "Approved cloud storage services: $($ApprovedCloudStorageProviders -join ', ')" } # Create an instance of CISAuditResult and populate it $params = @{ From e77d786535b18137b551780342de28d75f00a2b6 Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sun, 30 Jun 2024 10:17:32 -0500 Subject: [PATCH 04/21] docs: Update CHANGELOG --- CHANGELOG.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f5cf4e..337e142 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ The format is based on and uses the types of changes according to [Keep a Change ### Added +- Added `ApprovedCloudStorageProviders` parameter to `Invoke-M365SecurityAudit` to allow for testing of approved cloud storage providers for 8.1.1. + +## [0.1.18] - 2024-06-29 + +### Added + - Added `Get-PhishPolicyDetail` and `Test-PhishPolicyCompliance` private functions to help test for phishing policy compliance. ### Fixed @@ -16,8 +22,6 @@ The format is based on and uses the types of changes according to [Keep a Change - Changed main function parameter for Domain to `DomainName`. - - ## [0.1.17] - 2024-06-28 ### Fixed From bc85fa5fb880da3cfd22dbd55f0fa920e71b436f Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sun, 30 Jun 2024 10:17:58 -0500 Subject: [PATCH 05/21] docs: formatting --- helpers/Build-Help.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers/Build-Help.ps1 b/helpers/Build-Help.ps1 index 0082585..9dd530c 100644 --- a/helpers/Build-Help.ps1 +++ b/helpers/Build-Help.ps1 @@ -4,7 +4,7 @@ Import-Module .\output\module\M365FoundationsCISReport\*\*.psd1 <# - $ver = "v0.1.16" + $ver = "v0.1.18" git checkout main git pull origin main git tag -a $ver -m "Release version $ver refactor Update" From 83177cccc20772641b8ed12e9fb31d0557d2b901 Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sun, 30 Jun 2024 11:27:13 -0500 Subject: [PATCH 06/21] add: ApprovedFederatedDomains parameter for test 8.2.1 --- source/Private/Invoke-TestFunction.ps1 | 7 ++- source/Public/Invoke-M365SecurityAudit.ps1 | 9 +++- source/tests/Test-TeamsExternalAccess.ps1 | 63 ++++++++++++++++------ 3 files changed, 61 insertions(+), 18 deletions(-) diff --git a/source/Private/Invoke-TestFunction.ps1 b/source/Private/Invoke-TestFunction.ps1 index b708652..1aae8bb 100644 --- a/source/Private/Invoke-TestFunction.ps1 +++ b/source/Private/Invoke-TestFunction.ps1 @@ -6,7 +6,9 @@ function Invoke-TestFunction { [Parameter(Mandatory = $false)] [string]$DomainName, [Parameter(Mandatory = $false)] - [string[]]$ApprovedCloudStorageProviders + [string[]]$ApprovedCloudStorageProviders, + [Parameter(Mandatory = $false)] + [string[]]$ApprovedFederatedDomains ) $functionName = $FunctionFile.BaseName @@ -20,6 +22,9 @@ function Invoke-TestFunction { if ('ApprovedCloudStorageProviders' -in $functionCmd.Parameters.Keys) { $paramList.ApprovedCloudStorageProviders = $ApprovedCloudStorageProviders } + if ('ApprovedFederatedDomains' -in $functionCmd.Parameters.Keys) { + $paramList.ApprovedFederatedDomains = $ApprovedFederatedDomains + } # Use splatting to pass parameters Write-Verbose "Running $functionName..." try { diff --git a/source/Public/Invoke-M365SecurityAudit.ps1 b/source/Public/Invoke-M365SecurityAudit.ps1 index f9f0235..e45799e 100644 --- a/source/Public/Invoke-M365SecurityAudit.ps1 +++ b/source/Public/Invoke-M365SecurityAudit.ps1 @@ -23,6 +23,8 @@ Specifies specific recommendations to exclude from the audit. Accepts an array of recommendation numbers. .PARAMETER ApprovedCloudStorageProviders Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names. + .PARAMETER ApprovedFederatedDomains + Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names. .PARAMETER DoNotConnect If specified, the cmdlet will not establish a connection to Microsoft 365 services. .PARAMETER DoNotDisconnect @@ -131,7 +133,7 @@ function Invoke-M365SecurityAudit { [ValidatePattern('^https://[a-zA-Z0-9-]+-admin\.sharepoint\.com$')] [string]$TenantAdminUrl, - [Parameter(Mandatory = $false, HelpMessage = "Specify this to test only the default domain for password expiration policy when '1.3.1' is included in the tests to be run. The domain name of your organization, e.g., 'example.com'.")] + [Parameter(Mandatory = $false, HelpMessage = "Specify this to test only the default domain for password expiration and DKIM Config for tests '1.3.1' and 2.1.9. The domain name of your organization, e.g., 'example.com'.")] [ValidatePattern('^[a-zA-Z0-9-]+\.[a-zA-Z]{2,}$')] [string]$DomainName, @@ -187,6 +189,9 @@ function Invoke-M365SecurityAudit { )] [string[]]$ApprovedCloudStorageProviders = @(), + [Parameter(Mandatory = $false, HelpMessage = "Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names.")] + [string[]]$ApprovedFederatedDomains, + [Parameter(Mandatory = $false, HelpMessage = "Specifies that the cmdlet will not establish a connection to Microsoft 365 services.")] [switch]$DoNotConnect, @@ -300,7 +305,7 @@ function Invoke-M365SecurityAudit { Write-Progress -Activity "Executing Tests" -Status "Executing $($currentTestIndex) of $($totalTests): $($testFunction.Name)" -PercentComplete (($currentTestIndex / $totalTests) * 100) $functionName = $testFunction.BaseName if ($PSCmdlet.ShouldProcess($functionName, "Execute test")) { - $auditResult = Invoke-TestFunction -FunctionFile $testFunction -DomainName $DomainName -ApprovedCloudStorageProviders $ApprovedCloudStorageProviders + $auditResult = Invoke-TestFunction -FunctionFile $testFunction -DomainName $DomainName -ApprovedCloudStorageProviders $ApprovedCloudStorageProviders -ApprovedFederatedDomains $ApprovedFederatedDomains # Add the result to the collection [void]$allAuditResults.Add($auditResult) } diff --git a/source/tests/Test-TeamsExternalAccess.ps1 b/source/tests/Test-TeamsExternalAccess.ps1 index 9c7d320..22e58f4 100644 --- a/source/tests/Test-TeamsExternalAccess.ps1 +++ b/source/tests/Test-TeamsExternalAccess.ps1 @@ -2,8 +2,8 @@ function Test-TeamsExternalAccess { [CmdletBinding()] [OutputType([CISAuditResult])] param ( - # Aligned - # Parameters can be defined here if needed + [Parameter(Mandatory = $false, HelpMessage = "Specifies the approved federated domains for the audit. Accepts an array of allowed domain names.")] + [string[]]$ApprovedFederatedDomains ) begin { @@ -23,33 +23,66 @@ function Test-TeamsExternalAccess { # - Condition A: The `AllowTeamsConsumer` setting is `False`. # - Condition B: The `AllowPublicUsers` setting is `False`. # - Condition C: The `AllowFederatedUsers` setting is `False` or, if `True`, the `AllowedDomains` contains only authorized domain names. - # - # Validate test for a fail: - # - Confirm that the failure conditions in the automated test are consistent with the manual audit results. - # - Specific conditions to check: - # - Condition A: The `AllowTeamsConsumer` setting is not `False`. - # - Condition B: The `AllowPublicUsers` setting is not `False`. - # - Condition C: The `AllowFederatedUsers` setting is `True` and the `AllowedDomains` contains unauthorized domain names or is not configured correctly. # Connect to Teams PowerShell using Connect-MicrosoftTeams - + # $externalAccessConfig Mock Object + <# + $externalAccessConfig = [PSCustomObject]@{ + Identity = 'Global' + AllowedDomains = 'AllowAllKnownDomains' + BlockedDomains = @() + AllowFederatedUsers = $true + AllowPublicUsers = $true + AllowTeamsConsumer = $true + AllowTeamsConsumerInbound = $true + } + $ApprovedFederatedDomains = @('msn.com', 'google.com') + $externalAccessConfig = [PSCustomObject]@{ + Identity = 'Global' + AllowedDomains = @('msn.com', 'google.com') + BlockedDomains = @() + AllowFederatedUsers = $true + AllowPublicUsers = $false + AllowTeamsConsumer = $false + AllowTeamsConsumerInbound = $true + } + #> $externalAccessConfig = Get-CISMSTeamsOutput -Rec $recnum + # Testing + #$externalAccessConfig.AllowedDomains = @("msn.com", "google.com") + #$externalAccessConfig.AllowTeamsConsumer = $false + #$externalAccessConfig.AllowPublicUsers = $false + #$externalAccessConfig.AllowFederatedUsers = $true + # The above is for testing and will be replaced with the actual values from the Teams PowerShell output in production. + $allowedDomainsLimited = $false - if ($externalAccessConfig.AllowFederatedUsers -and $externalAccessConfig.AllowedDomains -and $externalAccessConfig.AllowedDomains.AllowedDomain.Count -gt 0) { - $allowedDomainsLimited = $true + $allowedDomainsMatch = $false + $invalidDomains = @() + + if ($externalAccessConfig.AllowFederatedUsers) { + if ($externalAccessConfig.AllowedDomains -ne 'AllowAllKnownDomains' -and $externalAccessConfig.AllowedDomains.Count -gt 0) { + $allowedDomainsLimited = $true + if ($ApprovedFederatedDomains) { + $invalidDomains = $externalAccessConfig.AllowedDomains | Where-Object { $_ -notin $ApprovedFederatedDomains } + if ($invalidDomains.Count -eq 0) { + $invalidDomains = "None" + } + $allowedDomainsMatch = $invalidDomains.Count -eq 0 + } + } } # Check if the configurations are as recommended - $isCompliant = -not $externalAccessConfig.AllowTeamsConsumer -and -not $externalAccessConfig.AllowPublicUsers -and (-not $externalAccessConfig.AllowFederatedUsers -or $allowedDomainsLimited) + $isCompliant = -not $externalAccessConfig.AllowTeamsConsumer -and -not $externalAccessConfig.AllowPublicUsers -and (-not $externalAccessConfig.AllowFederatedUsers -or ($allowedDomainsLimited -and $allowedDomainsMatch)) # Create an instance of CISAuditResult and populate it $params = @{ Rec = $recnum Result = $isCompliant Status = if ($isCompliant) { "Pass" } else { "Fail" } - Details = "AllowTeamsConsumer: $($externalAccessConfig.AllowTeamsConsumer); AllowPublicUsers: $($externalAccessConfig.AllowPublicUsers); AllowFederatedUsers: $($externalAccessConfig.AllowFederatedUsers); AllowedDomains limited: $allowedDomainsLimited" - FailureReason = if (-not $isCompliant) { "One or more external access configurations are not compliant." } else { "N/A" } + Details = "AllowTeamsConsumer: $($externalAccessConfig.AllowTeamsConsumer); AllowPublicUsers: $($externalAccessConfig.AllowPublicUsers); AllowFederatedUsers: $($externalAccessConfig.AllowFederatedUsers); AllowedDomains limited: $allowedDomainsLimited; AllowedDomains match: $allowedDomainsMatch; Invalid Domains: $($invalidDomains -join ', ')" + FailureReason = if (-not $isCompliant) { "One or more external access configurations are not compliant. Invalid domains found: $($invalidDomains -join ', ')" } else { "N/A" } } $auditResult = Initialize-CISAuditResult @params } From 97fd8127d38301b4ce80b086843180418070e6cf Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sun, 30 Jun 2024 11:34:49 -0500 Subject: [PATCH 07/21] add: ApprovedFederatedDomains parameter for test 8.2.1-Formatting --- source/tests/Test-TeamsExternalAccess.ps1 | 9 --------- 1 file changed, 9 deletions(-) diff --git a/source/tests/Test-TeamsExternalAccess.ps1 b/source/tests/Test-TeamsExternalAccess.ps1 index 22e58f4..ca3b6ae 100644 --- a/source/tests/Test-TeamsExternalAccess.ps1 +++ b/source/tests/Test-TeamsExternalAccess.ps1 @@ -5,14 +5,12 @@ function Test-TeamsExternalAccess { [Parameter(Mandatory = $false, HelpMessage = "Specifies the approved federated domains for the audit. Accepts an array of allowed domain names.")] [string[]]$ApprovedFederatedDomains ) - begin { # Dot source the class script if necessary # . .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed $recnum = "8.2.1" } - process { try { # 8.2.1 (L1) Ensure 'external access' is restricted in the Teams admin center @@ -23,7 +21,6 @@ function Test-TeamsExternalAccess { # - Condition A: The `AllowTeamsConsumer` setting is `False`. # - Condition B: The `AllowPublicUsers` setting is `False`. # - Condition C: The `AllowFederatedUsers` setting is `False` or, if `True`, the `AllowedDomains` contains only authorized domain names. - # Connect to Teams PowerShell using Connect-MicrosoftTeams # $externalAccessConfig Mock Object <# @@ -48,18 +45,15 @@ function Test-TeamsExternalAccess { } #> $externalAccessConfig = Get-CISMSTeamsOutput -Rec $recnum - # Testing #$externalAccessConfig.AllowedDomains = @("msn.com", "google.com") #$externalAccessConfig.AllowTeamsConsumer = $false #$externalAccessConfig.AllowPublicUsers = $false #$externalAccessConfig.AllowFederatedUsers = $true # The above is for testing and will be replaced with the actual values from the Teams PowerShell output in production. - $allowedDomainsLimited = $false $allowedDomainsMatch = $false $invalidDomains = @() - if ($externalAccessConfig.AllowFederatedUsers) { if ($externalAccessConfig.AllowedDomains -ne 'AllowAllKnownDomains' -and $externalAccessConfig.AllowedDomains.Count -gt 0) { $allowedDomainsLimited = $true @@ -72,10 +66,8 @@ function Test-TeamsExternalAccess { } } } - # Check if the configurations are as recommended $isCompliant = -not $externalAccessConfig.AllowTeamsConsumer -and -not $externalAccessConfig.AllowPublicUsers -and (-not $externalAccessConfig.AllowFederatedUsers -or ($allowedDomainsLimited -and $allowedDomainsMatch)) - # Create an instance of CISAuditResult and populate it $params = @{ Rec = $recnum @@ -91,7 +83,6 @@ function Test-TeamsExternalAccess { $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return auditResult return $auditResult From ad5ce2db7f5485d811949647a259eda0a932194e Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sun, 30 Jun 2024 11:36:11 -0500 Subject: [PATCH 08/21] add: remediation info and mock object to 8.5.1 --- source/Private/Get-CISMSTeamsOutput.ps1 | 31 ++++++++++++++++++-- source/tests/Test-NoAnonymousMeetingJoin.ps1 | 16 +++++----- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/source/Private/Get-CISMSTeamsOutput.ps1 b/source/Private/Get-CISMSTeamsOutput.ps1 index 58a69e2..81f055d 100644 --- a/source/Private/Get-CISMSTeamsOutput.ps1 +++ b/source/Private/Get-CISMSTeamsOutput.ps1 @@ -92,9 +92,29 @@ function Get-CISMSTeamsOutput { # - Condition A: The `AllowTeamsConsumer` setting is not `False`. # - Condition B: The `AllowPublicUsers` setting is not `False`. # - Condition C: The `AllowFederatedUsers` setting is `True` and the `AllowedDomains` contains unauthorized domain names or is not configured correctly. - # Connect to Teams PowerShell using Connect-MicrosoftTeams - + # $externalAccessConfig Mock Object + <# + $externalAccessConfig = [PSCustomObject]@{ + Identity = 'Global' + AllowedDomains = 'AllowAllKnownDomains' + BlockedDomains = @() + AllowFederatedUsers = $true + AllowPublicUsers = $true + AllowTeamsConsumer = $true + AllowTeamsConsumerInbound = $true + } + $ApprovedFederatedDomains = @('msn.com', 'google.com') + $externalAccessConfig = [PSCustomObject]@{ + Identity = 'Global' + AllowedDomains = @('msn.com', 'google.com') + BlockedDomains = @() + AllowFederatedUsers = $true + AllowPublicUsers = $false + AllowTeamsConsumer = $false + AllowTeamsConsumerInbound = $true + } + #> $externalAccessConfig = Get-CsTenantFederationConfiguration return $externalAccessConfig } @@ -117,7 +137,12 @@ function Get-CISMSTeamsOutput { # - Condition C: PowerShell command output indicates that anonymous users are allowed to join meetings. # Connect to Teams PowerShell using Connect-MicrosoftTeams - + # $teamsMeetingPolicy Mock Object + <# + $teamsMeetingPolicy = [PSCustomObject]@{ + AllowAnonymousUsersToJoinMeeting = $true + } + #> $teamsMeetingPolicy = Get-CsTeamsMeetingPolicy -Identity Global return $teamsMeetingPolicy } diff --git a/source/tests/Test-NoAnonymousMeetingJoin.ps1 b/source/tests/Test-NoAnonymousMeetingJoin.ps1 index a0ae10f..4dc3cfc 100644 --- a/source/tests/Test-NoAnonymousMeetingJoin.ps1 +++ b/source/tests/Test-NoAnonymousMeetingJoin.ps1 @@ -5,14 +5,12 @@ function Test-NoAnonymousMeetingJoin { # Aligned # Parameters can be defined here if needed ) - begin { # Dot source the class script if necessary #. .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed $recnum = "8.5.1" } - process { try { # 8.5.1 (L2) Ensure anonymous users can't join a meeting @@ -30,22 +28,23 @@ function Test-NoAnonymousMeetingJoin { # - Condition A: `AllowAnonymousUsersToJoinMeeting` is not set to `False`. # - Condition B: Verification using the UI shows that `Anonymous users can join a meeting` is not set to `Off` in the Global meeting policy. # - Condition C: PowerShell command output indicates that anonymous users are allowed to join meetings. - # Connect to Teams PowerShell using Connect-MicrosoftTeams - + # $teamsMeetingPolicy Mock Object + <# + $teamsMeetingPolicy = [PSCustomObject]@{ + AllowAnonymousUsersToJoinMeeting = $true + } + #> $teamsMeetingPolicy = Get-CISMSTeamsOutput -Rec $recnum $allowAnonymousUsersToJoinMeeting = $teamsMeetingPolicy.AllowAnonymousUsersToJoinMeeting - # Prepare failure reasons and details based on compliance $failureReasons = if ($allowAnonymousUsersToJoinMeeting) { - "Anonymous users are allowed to join meetings" + "Anonymous users are allowed to join meetings, remediate with the following command if needed:`nSet-CsTeamsMeetingPolicy -Identity Global -AllowAnonymousUsersToJoinMeeting `$false" } else { "N/A" } - $details = "AllowAnonymousUsersToJoinMeeting is set to $allowAnonymousUsersToJoinMeeting" - # Create and populate the CISAuditResult object $params = @{ Rec = $recnum @@ -61,7 +60,6 @@ function Test-NoAnonymousMeetingJoin { $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return the audit result return $auditResult From 1d2fa9ea3a0d7ca88b230f189ec95e2f435d3dd2 Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sun, 30 Jun 2024 11:40:26 -0500 Subject: [PATCH 09/21] fix: 8.5.1 simplified output and added object comment --- source/Private/Get-CISMSTeamsOutput.ps1 | 8 ++++++-- source/tests/Test-NoAnonymousMeetingStart.ps1 | 8 -------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/source/Private/Get-CISMSTeamsOutput.ps1 b/source/Private/Get-CISMSTeamsOutput.ps1 index 81f055d..39a95f3 100644 --- a/source/Private/Get-CISMSTeamsOutput.ps1 +++ b/source/Private/Get-CISMSTeamsOutput.ps1 @@ -163,9 +163,13 @@ function Get-CISMSTeamsOutput { # - Condition A: The `AllowAnonymousUsersToStartMeeting` setting in the Teams admin center is not set to `False`. # - Condition B: The setting for anonymous users and dial-in callers starting a meeting allows them to bypass the lobby. # - Condition C: Verification using the UI indicates that the setting `Anonymous users and dial-in callers can start a meeting` is not set to `Off`. - # Connect to Teams PowerShell using Connect-MicrosoftTeams - + # $teamsMeetingPolicy Mock Object + <# + $CsTeamsMeetingPolicyAnonymous = [PSCustomObject]@{ + AllowAnonymousUsersToStartMeeting = $true + } + #> # Retrieve the Teams meeting policy for the global scope and check if anonymous users can start meetings $CsTeamsMeetingPolicyAnonymous = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowAnonymousUsersToStartMeeting return $CsTeamsMeetingPolicyAnonymous diff --git a/source/tests/Test-NoAnonymousMeetingStart.ps1 b/source/tests/Test-NoAnonymousMeetingStart.ps1 index 424e756..f1ea675 100644 --- a/source/tests/Test-NoAnonymousMeetingStart.ps1 +++ b/source/tests/Test-NoAnonymousMeetingStart.ps1 @@ -5,14 +5,12 @@ function Test-NoAnonymousMeetingStart { # Aligned # Parameters can be defined here if needed ) - begin { # Dot source the class script if necessary #. .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed $recnum = "8.5.2" } - process { try { # 8.5.2 (L1) Ensure anonymous users and dial-in callers can't start a meeting @@ -30,13 +28,10 @@ function Test-NoAnonymousMeetingStart { # - Condition A: The `AllowAnonymousUsersToStartMeeting` setting in the Teams admin center is not set to `False`. # - Condition B: The setting for anonymous users and dial-in callers starting a meeting allows them to bypass the lobby. # - Condition C: Verification using the UI indicates that the setting `Anonymous users and dial-in callers can start a meeting` is not set to `Off`. - # Connect to Teams PowerShell using Connect-MicrosoftTeams - # Retrieve the Teams meeting policy for the global scope and check if anonymous users can start meetings $CsTeamsMeetingPolicyAnonymous = Get-CISMSTeamsOutput -Rec $recnum $anonymousStartDisabled = -not $CsTeamsMeetingPolicyAnonymous.AllowAnonymousUsersToStartMeeting - # Prepare failure reasons and details based on compliance $failureReasons = if ($anonymousStartDisabled) { "N/A" @@ -44,9 +39,7 @@ function Test-NoAnonymousMeetingStart { else { "Anonymous users and dial-in callers can start a meeting" # Condition A and B } - $details = "AllowAnonymousUsersToStartMeeting is set to $($CsTeamsMeetingPolicyAnonymous.AllowAnonymousUsersToStartMeeting)" # Condition C - # Create and populate the CISAuditResult object $params = @{ Rec = $recnum @@ -62,7 +55,6 @@ function Test-NoAnonymousMeetingStart { $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return the audit result return $auditResult From 540fe11ce97a870459e932b7451ca778aa8f0df8 Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sun, 30 Jun 2024 11:46:53 -0500 Subject: [PATCH 10/21] fix: 8.5.3 simplified output and added object comment --- source/Private/Get-CISMSTeamsOutput.ps1 | 10 +++++++--- source/tests/Test-OrgOnlyBypassLobby.ps1 | 8 +------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/source/Private/Get-CISMSTeamsOutput.ps1 b/source/Private/Get-CISMSTeamsOutput.ps1 index 39a95f3..1423c71 100644 --- a/source/Private/Get-CISMSTeamsOutput.ps1 +++ b/source/Private/Get-CISMSTeamsOutput.ps1 @@ -164,7 +164,7 @@ function Get-CISMSTeamsOutput { # - Condition B: The setting for anonymous users and dial-in callers starting a meeting allows them to bypass the lobby. # - Condition C: Verification using the UI indicates that the setting `Anonymous users and dial-in callers can start a meeting` is not set to `Off`. # Connect to Teams PowerShell using Connect-MicrosoftTeams - # $teamsMeetingPolicy Mock Object + # $CsTeamsMeetingPolicyAnonymous Mock Object <# $CsTeamsMeetingPolicyAnonymous = [PSCustomObject]@{ AllowAnonymousUsersToStartMeeting = $true @@ -191,10 +191,14 @@ function Get-CISMSTeamsOutput { # - Condition A: The `AutoAdmittedUsers` setting in the Teams meeting policy is not set to `EveryoneInCompanyExcludingGuests`. # - Condition B: The setting for "Who can bypass the lobby" is not configured to "People in my org" using the UI. # - Condition C: Verification using the Microsoft Teams admin center indicates that the meeting join & lobby settings are not configured as recommended. - # Connect to Teams PowerShell using Connect-MicrosoftTeams - # Retrieve the Teams meeting policy for lobby bypass settings + # $CsTeamsMeetingPolicyLobby Mock Object + <# + $CsTeamsMeetingPolicyLobby = [PSCustomObject]@{ + AutoAdmittedUsers = "OrganizerOnly" + } + #> $CsTeamsMeetingPolicyLobby = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AutoAdmittedUsers return $CsTeamsMeetingPolicyLobby } diff --git a/source/tests/Test-OrgOnlyBypassLobby.ps1 b/source/tests/Test-OrgOnlyBypassLobby.ps1 index 2f6f208..785a85e 100644 --- a/source/tests/Test-OrgOnlyBypassLobby.ps1 +++ b/source/tests/Test-OrgOnlyBypassLobby.ps1 @@ -30,28 +30,23 @@ function Test-OrgOnlyBypassLobby { # - Condition A: The `AutoAdmittedUsers` setting in the Teams meeting policy is not set to `EveryoneInCompanyExcludingGuests`. # - Condition B: The setting for "Who can bypass the lobby" is not configured to "People in my org" using the UI. # - Condition C: Verification using the Microsoft Teams admin center indicates that the meeting join & lobby settings are not configured as recommended. - # Connect to Teams PowerShell using Connect-MicrosoftTeams - # Retrieve the Teams meeting policy for lobby bypass settings $CsTeamsMeetingPolicyLobby = Get-CISMSTeamsOutput -Rec $recnum $lobbyBypassRestricted = $CsTeamsMeetingPolicyLobby.AutoAdmittedUsers -eq 'EveryoneInCompanyExcludingGuests' - # Prepare failure reasons and details based on compliance $failureReasons = if (-not $lobbyBypassRestricted) { # Condition C: Verification using the Microsoft Teams admin center indicates that the meeting join & lobby settings are not configured as recommended. "AutoAdmittedUsers is set to $($CsTeamsMeetingPolicyLobby.AutoAdmittedUsers)" - }else { "N/A" } - $details = if ($lobbyBypassRestricted) { # Condition B: The setting for "Who can bypass the lobby" is configured to "People in my org" using the UI. "Only people in the organization can bypass the lobby." }else { # Condition A: The `AutoAdmittedUsers` setting in the Teams meeting policy is not set to `EveryoneInCompanyExcludingGuests`. - "External participants can bypass the lobby" + "AutoAdmittedUsers is not set to EveryoneInCompanyExcludingGuests" } # Create and populate the CISAuditResult object $params = @{ @@ -68,7 +63,6 @@ function Test-OrgOnlyBypassLobby { $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return the audit result return $auditResult From 678c287d0380d7e703fa02bee45c1a2443650c0c Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sun, 30 Jun 2024 11:47:07 -0500 Subject: [PATCH 11/21] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 337e142..e0a266d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ The format is based on and uses the types of changes according to [Keep a Change ### Added - Added `ApprovedCloudStorageProviders` parameter to `Invoke-M365SecurityAudit` to allow for testing of approved cloud storage providers for 8.1.1. +- Added `ApprovedFederatedDomains` parameter to `Invoke-M365SecurityAudit` to allow for testing of approved federated domains for 8.5.1. ## [0.1.18] - 2024-06-29 From 80015c78d5e73a2b850179d3ac293e8a25fb4f45 Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sun, 30 Jun 2024 12:10:43 -0500 Subject: [PATCH 12/21] fix: 8.5.4 simplified output and added object comment --- source/Private/Get-CISMSTeamsOutput.ps1 | 7 ++++++- source/tests/Test-DialInBypassLobby.ps1 | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/source/Private/Get-CISMSTeamsOutput.ps1 b/source/Private/Get-CISMSTeamsOutput.ps1 index 1423c71..db01902 100644 --- a/source/Private/Get-CISMSTeamsOutput.ps1 +++ b/source/Private/Get-CISMSTeamsOutput.ps1 @@ -219,8 +219,13 @@ function Get-CISMSTeamsOutput { # - Condition A: The `AllowPSTNUsersToBypassLobby` setting in the Global Teams meeting policy is not set to `False`. # - Condition B: Verification using the UI in the Microsoft Teams admin center shows that "People dialing in can't bypass the lobby" is not set to `Off`. # - Condition C: Individuals who dial in by phone are able to join the meeting directly without waiting in the lobby. - # Retrieve Teams meeting policy for PSTN users + # $CsTeamsMeetingPolicyPSTN Mock Object + <# + $CsTeamsMeetingPolicyPSTN = [PSCustomObject]@{ + AllowPSTNUsersToBypassLobby = $true + } + #> $CsTeamsMeetingPolicyPSTN = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowPSTNUsersToBypassLobby return $CsTeamsMeetingPolicyPSTN } diff --git a/source/tests/Test-DialInBypassLobby.ps1 b/source/tests/Test-DialInBypassLobby.ps1 index f2bf4e6..08cf2af 100644 --- a/source/tests/Test-DialInBypassLobby.ps1 +++ b/source/tests/Test-DialInBypassLobby.ps1 @@ -31,8 +31,13 @@ function Test-DialInBypassLobby { # - Condition A: The `AllowPSTNUsersToBypassLobby` setting in the Global Teams meeting policy is not set to `False`. # - Condition B: Verification using the UI in the Microsoft Teams admin center shows that "People dialing in can't bypass the lobby" is not set to `Off`. # - Condition C: Individuals who dial in by phone are able to join the meeting directly without waiting in the lobby. - # Retrieve Teams meeting policy for PSTN users + # $CsTeamsMeetingPolicyPSTN Mock Object + <# + $CsTeamsMeetingPolicyPSTN = [PSCustomObject]@{ + AllowPSTNUsersToBypassLobby = $true + } + #> $CsTeamsMeetingPolicyPSTN = Get-CISMSTeamsOutput -Rec $recnum $PSTNBypassDisabled = -not $CsTeamsMeetingPolicyPSTN.AllowPSTNUsersToBypassLobby From 1e106f94ba3542d5a12a2e52afd5d37569e7b88c Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sun, 30 Jun 2024 12:15:58 -0500 Subject: [PATCH 13/21] fix: 8.5.5 simplified output and added object comment --- source/Private/Get-CISMSTeamsOutput.ps1 | 7 ++++++- source/tests/Test-MeetingChatNoAnonymous.ps1 | 15 +++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/source/Private/Get-CISMSTeamsOutput.ps1 b/source/Private/Get-CISMSTeamsOutput.ps1 index db01902..806b0a9 100644 --- a/source/Private/Get-CISMSTeamsOutput.ps1 +++ b/source/Private/Get-CISMSTeamsOutput.ps1 @@ -246,8 +246,13 @@ function Get-CISMSTeamsOutput { # - Condition A: The `MeetingChatEnabledType` setting in Teams is not set to `EnabledExceptAnonymous`. # - Condition B: The setting for meeting chat allows chat for anonymous users. # - Condition C: Verification using the Teams Admin Center indicates that the meeting chat settings are not configured as recommended. - # Retrieve the Teams meeting policy for meeting chat + # $CsTeamsMeetingPolicyChat Mock Object + <# + $CsTeamsMeetingPolicyChat = [PSCustomObject]@{ + MeetingChatEnabledType = "Enabled" + } + #> $CsTeamsMeetingPolicyChat = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property MeetingChatEnabledType return $CsTeamsMeetingPolicyChat } diff --git a/source/tests/Test-MeetingChatNoAnonymous.ps1 b/source/tests/Test-MeetingChatNoAnonymous.ps1 index 64e0e2e..c84a2ea 100644 --- a/source/tests/Test-MeetingChatNoAnonymous.ps1 +++ b/source/tests/Test-MeetingChatNoAnonymous.ps1 @@ -5,14 +5,12 @@ function Test-MeetingChatNoAnonymous { # Aligned # Parameters can be defined here if needed ) - begin { # Dot source the class script if necessary #. .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed $recnum = "8.5.5" } - process { try { # 8.5.5 (L2) Ensure meeting chat does not allow anonymous users @@ -30,22 +28,24 @@ function Test-MeetingChatNoAnonymous { # - Condition A: The `MeetingChatEnabledType` setting in Teams is not set to `EnabledExceptAnonymous`. # - Condition B: The setting for meeting chat allows chat for anonymous users. # - Condition C: Verification using the Teams Admin Center indicates that the meeting chat settings are not configured as recommended. - # Retrieve the Teams meeting policy for meeting chat + # $CsTeamsMeetingPolicyChat Mock Object + <# + $CsTeamsMeetingPolicyChat = [PSCustomObject]@{ + MeetingChatEnabledType = "Enabled" + } + #> $CsTeamsMeetingPolicyChat = Get-CISMSTeamsOutput -Rec $recnum # Condition A: Check if the MeetingChatEnabledType is set to 'EnabledExceptAnonymous' $chatAnonDisabled = $CsTeamsMeetingPolicyChat.MeetingChatEnabledType -eq 'EnabledExceptAnonymous' - # Prepare failure reasons and details based on compliance $failureReasons = if ($chatAnonDisabled) { "N/A" } else { - "Meeting chat allows anonymous users" + "Meeting chat allows anonymous users. User the following command to remediate:`nSet-CsTeamsMeetingPolicy -Identity Global -MeetingChatEnabledType `"EnabledExceptAnonymous`"" } - $details = "MeetingChatEnabledType is set to $($CsTeamsMeetingPolicyChat.MeetingChatEnabledType)" - # Create and populate the CISAuditResult object $params = @{ Rec = $recnum @@ -61,7 +61,6 @@ function Test-MeetingChatNoAnonymous { $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return the audit result return $auditResult From 5804ca25c14a43eb3fef3e980be087a7a8940e7e Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sun, 30 Jun 2024 12:19:49 -0500 Subject: [PATCH 14/21] fix: 8.5.6 simplified output and added object comment --- source/Private/Get-CISMSTeamsOutput.ps1 | 7 ++++++- source/tests/Test-OrganizersPresent.ps1 | 15 +++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/source/Private/Get-CISMSTeamsOutput.ps1 b/source/Private/Get-CISMSTeamsOutput.ps1 index 806b0a9..9c439ce 100644 --- a/source/Private/Get-CISMSTeamsOutput.ps1 +++ b/source/Private/Get-CISMSTeamsOutput.ps1 @@ -273,8 +273,13 @@ function Get-CISMSTeamsOutput { # - Condition A: The `DesignatedPresenterRoleMode` setting in the Teams meeting policy is not set to `OrganizerOnlyUserOverride`. # - Condition B: Verification using the Teams admin center indicates that the setting "Who can present" is not configured to "Only organizers and co-organizers". # - Condition C: Verification using PowerShell indicates that the `DesignatedPresenterRoleMode` is not set to `OrganizerOnlyUserOverride`. - # Retrieve the Teams meeting policy for presenters + # $CsTeamsMeetingPolicyPresenters Mock Object + <# + $CsTeamsMeetingPolicyPresenters = [PSCustomObject]@{ + DesignatedPresenterRoleMode = "Enabled" + } + #> $CsTeamsMeetingPolicyPresenters = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property DesignatedPresenterRoleMode return $CsTeamsMeetingPolicyPresenters } diff --git a/source/tests/Test-OrganizersPresent.ps1 b/source/tests/Test-OrganizersPresent.ps1 index d85ff32..df79661 100644 --- a/source/tests/Test-OrganizersPresent.ps1 +++ b/source/tests/Test-OrganizersPresent.ps1 @@ -5,14 +5,12 @@ function Test-OrganizersPresent { # Aligned # Parameters can be defined here if needed ) - begin { # Dot source the class script if necessary #. .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed $recnum = "8.5.6" } - process { try { # 8.5.6 (L2) Ensure only organizers and co-organizers can present @@ -30,26 +28,28 @@ function Test-OrganizersPresent { # - Condition A: The `DesignatedPresenterRoleMode` setting in the Teams meeting policy is not set to `OrganizerOnlyUserOverride`. # - Condition B: Verification using the Teams admin center indicates that the setting "Who can present" is not configured to "Only organizers and co-organizers". # - Condition C: Verification using PowerShell indicates that the `DesignatedPresenterRoleMode` is not set to `OrganizerOnlyUserOverride`. - # Retrieve the Teams meeting policy for presenters + # $CsTeamsMeetingPolicyPresenters Mock Object + <# + $CsTeamsMeetingPolicyPresenters = [PSCustomObject]@{ + DesignatedPresenterRoleMode = "Enabled" + } + #> $CsTeamsMeetingPolicyPresenters = Get-CISMSTeamsOutput -Rec $recnum $presenterRoleRestricted = $CsTeamsMeetingPolicyPresenters.DesignatedPresenterRoleMode -eq 'OrganizerOnlyUserOverride' - # Prepare failure reasons and details based on compliance $failureReasons = if (-not $presenterRoleRestricted) { - "Others besides organizers and co-organizers can present" + "Others besides organizers and co-organizers can present. Use the following command to remediate:`nSet-CsTeamsMeetingPolicy -Identity Global -DesignatedPresenterRoleMode `"OrganizerOnlyUserOverride`"" } else { "N/A" } - $details = if ($presenterRoleRestricted) { "Only organizers and co-organizers can present." } else { "DesignatedPresenterRoleMode is set to $($CsTeamsMeetingPolicyPresenters.DesignatedPresenterRoleMode)" } - # Create and populate the CISAuditResult object $params = @{ Rec = $recnum @@ -65,7 +65,6 @@ function Test-OrganizersPresent { $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return the audit result return $auditResult From e993f186af8facd9f18cbc92d239a5ed6e742af2 Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sun, 30 Jun 2024 12:24:36 -0500 Subject: [PATCH 15/21] fix: formatting --- source/Private/Get-CISMSTeamsOutput.ps1 | 1 - source/tests/Test-BlockChannelEmails.ps1 | 9 +-------- source/tests/Test-DialInBypassLobby.ps1 | 8 +------- source/tests/Test-MeetingChatNoAnonymous.ps1 | 1 + source/tests/Test-NoAnonymousMeetingJoin.ps1 | 1 + source/tests/Test-NoAnonymousMeetingStart.ps1 | 1 + source/tests/Test-OrgOnlyBypassLobby.ps1 | 3 +-- source/tests/Test-OrganizersPresent.ps1 | 1 + source/tests/Test-TeamsExternalAccess.ps1 | 1 + source/tests/Test-TeamsExternalFileSharing.ps1 | 3 +-- 10 files changed, 9 insertions(+), 20 deletions(-) diff --git a/source/Private/Get-CISMSTeamsOutput.ps1 b/source/Private/Get-CISMSTeamsOutput.ps1 index 9c439ce..d07d8f4 100644 --- a/source/Private/Get-CISMSTeamsOutput.ps1 +++ b/source/Private/Get-CISMSTeamsOutput.ps1 @@ -135,7 +135,6 @@ function Get-CISMSTeamsOutput { # - Condition A: `AllowAnonymousUsersToJoinMeeting` is not set to `False`. # - Condition B: Verification using the UI shows that `Anonymous users can join a meeting` is not set to `Off` in the Global meeting policy. # - Condition C: PowerShell command output indicates that anonymous users are allowed to join meetings. - # Connect to Teams PowerShell using Connect-MicrosoftTeams # $teamsMeetingPolicy Mock Object <# diff --git a/source/tests/Test-BlockChannelEmails.ps1 b/source/tests/Test-BlockChannelEmails.ps1 index 0468ed0..4816471 100644 --- a/source/tests/Test-BlockChannelEmails.ps1 +++ b/source/tests/Test-BlockChannelEmails.ps1 @@ -5,16 +5,14 @@ function Test-BlockChannelEmails { # Aligned # Parameters can be added here if needed ) - begin { # Dot source the class script if necessary #. .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed $recnum = "8.1.2" + Write-Verbose "Running Test-BlockChannelEmails for $recnum..." } - process { - try { # 8.1.2 (L1) Ensure users can't send emails to a channel email address # @@ -31,11 +29,9 @@ function Test-BlockChannelEmails { # - Condition A: The `AllowEmailIntoChannel` setting in Teams is not set to `False`. # - Condition B: The setting `Users can send emails to a channel email address` is not set to `Off` in the Teams admin center. # - Condition C: Verification using PowerShell indicates that the `AllowEmailIntoChannel` setting is enabled. - # Retrieve Teams client configuration $teamsClientConfig = Get-CISMSTeamsOutput -Rec $recnum $allowEmailIntoChannel = $teamsClientConfig.AllowEmailIntoChannel - # Prepare failure reasons and details based on compliance $failureReasons = if ($allowEmailIntoChannel) { "Emails can be sent to a channel email address" # Condition A Fail: AllowEmailIntoChannel is True @@ -43,14 +39,12 @@ function Test-BlockChannelEmails { else { "N/A" # Condition A Pass: AllowEmailIntoChannel is False } - $details = if ($allowEmailIntoChannel) { "AllowEmailIntoChannel is set to True" # Condition B Fail: Emails are allowed } else { "AllowEmailIntoChannel is set to False" # Condition B Pass: Emails are blocked } - # Create and populate the CISAuditResult object $params = @{ Rec = $recnum @@ -66,7 +60,6 @@ function Test-BlockChannelEmails { $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return the audit result return $auditResult diff --git a/source/tests/Test-DialInBypassLobby.ps1 b/source/tests/Test-DialInBypassLobby.ps1 index 08cf2af..6526715 100644 --- a/source/tests/Test-DialInBypassLobby.ps1 +++ b/source/tests/Test-DialInBypassLobby.ps1 @@ -5,16 +5,14 @@ function Test-DialInBypassLobby { # Aligned # Parameters can be defined here if needed ) - begin { # Dot source the class script if necessary #. .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed $recnum = "8.5.4" + Write-Verbose "Running Test-DialInBypassLobby for $recnum..." } - process { - try { # 8.5.4 (L1) Ensure users dialing in can't bypass the lobby # @@ -40,7 +38,6 @@ function Test-DialInBypassLobby { #> $CsTeamsMeetingPolicyPSTN = Get-CISMSTeamsOutput -Rec $recnum $PSTNBypassDisabled = -not $CsTeamsMeetingPolicyPSTN.AllowPSTNUsersToBypassLobby - # Prepare failure reasons and details based on compliance $failureReasons = if (-not $PSTNBypassDisabled) { "Users dialing in can bypass the lobby" @@ -48,14 +45,12 @@ function Test-DialInBypassLobby { else { "N/A" } - $details = if ($PSTNBypassDisabled) { "AllowPSTNUsersToBypassLobby is set to False" } else { "AllowPSTNUsersToBypassLobby is set to True" } - # Create and populate the CISAuditResult object $params = @{ Rec = $recnum @@ -71,7 +66,6 @@ function Test-DialInBypassLobby { $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return the audit result return $auditResult diff --git a/source/tests/Test-MeetingChatNoAnonymous.ps1 b/source/tests/Test-MeetingChatNoAnonymous.ps1 index c84a2ea..5e281b7 100644 --- a/source/tests/Test-MeetingChatNoAnonymous.ps1 +++ b/source/tests/Test-MeetingChatNoAnonymous.ps1 @@ -10,6 +10,7 @@ function Test-MeetingChatNoAnonymous { #. .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed $recnum = "8.5.5" + Write-Verbose "Running Test-MeetingChatNoAnonymous for $recnum..." } process { try { diff --git a/source/tests/Test-NoAnonymousMeetingJoin.ps1 b/source/tests/Test-NoAnonymousMeetingJoin.ps1 index 4dc3cfc..d9d82dd 100644 --- a/source/tests/Test-NoAnonymousMeetingJoin.ps1 +++ b/source/tests/Test-NoAnonymousMeetingJoin.ps1 @@ -10,6 +10,7 @@ function Test-NoAnonymousMeetingJoin { #. .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed $recnum = "8.5.1" + Write-Verbose "Running Test-NoAnonymousMeetingJoin for $recnum..." } process { try { diff --git a/source/tests/Test-NoAnonymousMeetingStart.ps1 b/source/tests/Test-NoAnonymousMeetingStart.ps1 index f1ea675..cb92a64 100644 --- a/source/tests/Test-NoAnonymousMeetingStart.ps1 +++ b/source/tests/Test-NoAnonymousMeetingStart.ps1 @@ -10,6 +10,7 @@ function Test-NoAnonymousMeetingStart { #. .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed $recnum = "8.5.2" + Write-Verbose "Running Test-NoAnonymousMeetingStart for $recnum..." } process { try { diff --git a/source/tests/Test-OrgOnlyBypassLobby.ps1 b/source/tests/Test-OrgOnlyBypassLobby.ps1 index 785a85e..b3213a1 100644 --- a/source/tests/Test-OrgOnlyBypassLobby.ps1 +++ b/source/tests/Test-OrgOnlyBypassLobby.ps1 @@ -5,14 +5,13 @@ function Test-OrgOnlyBypassLobby { # Aligned # Parameters can be defined here if needed ) - begin { # Dot source the class script if necessary #. .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed $recnum = "8.5.3" + Write-Verbose "Running Test-OrgOnlyBypassLobby for $recnum..." } - process { try { # 8.5.3 (L1) Ensure only people in my org can bypass the lobby diff --git a/source/tests/Test-OrganizersPresent.ps1 b/source/tests/Test-OrganizersPresent.ps1 index df79661..b4d4867 100644 --- a/source/tests/Test-OrganizersPresent.ps1 +++ b/source/tests/Test-OrganizersPresent.ps1 @@ -10,6 +10,7 @@ function Test-OrganizersPresent { #. .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed $recnum = "8.5.6" + Write-Verbose "Running Test-OrganizersPresent for $recnum..." } process { try { diff --git a/source/tests/Test-TeamsExternalAccess.ps1 b/source/tests/Test-TeamsExternalAccess.ps1 index ca3b6ae..f28262a 100644 --- a/source/tests/Test-TeamsExternalAccess.ps1 +++ b/source/tests/Test-TeamsExternalAccess.ps1 @@ -10,6 +10,7 @@ function Test-TeamsExternalAccess { # . .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed $recnum = "8.2.1" + Write-Verbose "Running Test-TeamsExternalAccess for $recnum..." } process { try { diff --git a/source/tests/Test-TeamsExternalFileSharing.ps1 b/source/tests/Test-TeamsExternalFileSharing.ps1 index a709c20..faca09b 100644 --- a/source/tests/Test-TeamsExternalFileSharing.ps1 +++ b/source/tests/Test-TeamsExternalFileSharing.ps1 @@ -10,18 +10,17 @@ function Test-TeamsExternalFileSharing { # . .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed $recnum = "8.1.1" + Write-Verbose "Running Test-TeamsExternalFileSharing for $recnum..." } process { try { # 8.1.1 (L2) Ensure external file sharing in Teams is enabled for only approved cloud storage services # Connect to Teams PowerShell using Connect-MicrosoftTeams - # Condition A: The `AllowDropbox` setting is set to `False`. # Condition B: The `AllowBox` setting is set to `False`. # Condition C: The `AllowGoogleDrive` setting is set to `False`. # Condition D: The `AllowShareFile` setting is set to `False`. # Condition E: The `AllowEgnyte` setting is set to `False`. - # Assuming that 'approvedProviders' is a list of approved cloud storage service names # This list must be defined according to your organization's approved cloud storage services # Retrieve the current Teams client configuration From 9a6bda9e2e2336f68a45bc3e58e111a9d17694cf Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sun, 30 Jun 2024 12:27:01 -0500 Subject: [PATCH 16/21] fix: 8.5.7 simplified output and added object comment --- source/Private/Get-CISMSTeamsOutput.ps1 | 7 ++++++- source/tests/Test-ExternalNoControl.ps1 | 16 +++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/source/Private/Get-CISMSTeamsOutput.ps1 b/source/Private/Get-CISMSTeamsOutput.ps1 index d07d8f4..7818a3a 100644 --- a/source/Private/Get-CISMSTeamsOutput.ps1 +++ b/source/Private/Get-CISMSTeamsOutput.ps1 @@ -299,8 +299,13 @@ function Get-CISMSTeamsOutput { # - Condition A: The `AllowExternalParticipantGiveRequestControl` setting in Teams is not set to `False`. # - Condition B: The setting is verified through the Microsoft Teams admin center or via PowerShell command. # - Condition C: Verification using the UI indicates that external participants can give or request control. - # Retrieve Teams meeting policy for external participant control + # $CsTeamsMeetingPolicyControl Mock Object + <# + $CsTeamsMeetingPolicyControl = [PSCustomObject]@{ + AllowExternalParticipantGiveRequestControl = $true + } + #> $CsTeamsMeetingPolicyControl = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowExternalParticipantGiveRequestControl return $CsTeamsMeetingPolicyControl } diff --git a/source/tests/Test-ExternalNoControl.ps1 b/source/tests/Test-ExternalNoControl.ps1 index 3b377ab..deb78d9 100644 --- a/source/tests/Test-ExternalNoControl.ps1 +++ b/source/tests/Test-ExternalNoControl.ps1 @@ -5,17 +5,14 @@ function Test-ExternalNoControl { # Aligned # Parameters can be defined here if needed ) - begin { # Dot source the class script if necessary # . .\source\Classes\CISAuditResult.ps1 - # Initialization code, if needed $recnum = "8.5.7" + Write-Verbose "Running Test-ExternalNoControl for $recnum..." } - process { - try { # 8.5.7 (L1) Ensure external participants can't give or request control # @@ -32,12 +29,16 @@ function Test-ExternalNoControl { # - Condition A: The `AllowExternalParticipantGiveRequestControl` setting in Teams is not set to `False`. # - Condition B: The setting is verified through the Microsoft Teams admin center or via PowerShell command. # - Condition C: Verification using the UI indicates that external participants can give or request control. - # Retrieve Teams meeting policy for external participant control + # $CsTeamsMeetingPolicyControl Mock Object + <# + $CsTeamsMeetingPolicyControl = [PSCustomObject]@{ + AllowExternalParticipantGiveRequestControl = $true + } + #> $CsTeamsMeetingPolicyControl = Get-CISMSTeamsOutput -Rec $recnum # Check if external participants can give or request control $externalControlRestricted = -not $CsTeamsMeetingPolicyControl.AllowExternalParticipantGiveRequestControl - # Prepare failure reasons and details based on compliance $failureReasons = if (-not $externalControlRestricted) { "External participants can give or request control" @@ -45,14 +46,12 @@ function Test-ExternalNoControl { else { "N/A" } - $details = if ($externalControlRestricted) { "AllowExternalParticipantGiveRequestControl is set to False" } else { "AllowExternalParticipantGiveRequestControl is set to True" } - # Create and populate the CISAuditResult object $params = @{ Rec = $recnum @@ -68,7 +67,6 @@ function Test-ExternalNoControl { $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return the audit result return $auditResult From 355c12b450f3a5626fdcbd49a1639e79324cf98d Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sun, 30 Jun 2024 12:33:20 -0500 Subject: [PATCH 17/21] fix: 8.6.1 simplified output and added object comment --- source/Private/Get-CISExoOutput.ps1 | 13 +++++++++++++ source/Private/Get-CISMSTeamsOutput.ps1 | 7 ++++++- source/tests/Test-ReportSecurityInTeams.ps1 | 19 +++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/source/Private/Get-CISExoOutput.ps1 b/source/Private/Get-CISExoOutput.ps1 index 615c421..e2994b3 100644 --- a/source/Private/Get-CISExoOutput.ps1 +++ b/source/Private/Get-CISExoOutput.ps1 @@ -513,6 +513,19 @@ function Get-CISExoOutput { # 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 } diff --git a/source/Private/Get-CISMSTeamsOutput.ps1 b/source/Private/Get-CISMSTeamsOutput.ps1 index 7818a3a..2122797 100644 --- a/source/Private/Get-CISMSTeamsOutput.ps1 +++ b/source/Private/Get-CISMSTeamsOutput.ps1 @@ -312,9 +312,14 @@ function Get-CISMSTeamsOutput { '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 A: Ensure the 'Report a security concern' setting in the Teams admin center is set to 'On'. + # $CsTeamsMessagingPolicy Mock Object + <# + $CsTeamsMessagingPolicy = [PSCustomObject]@{ + AllowSecurityEndUserReporting = $true + } + #> $CsTeamsMessagingPolicy = Get-CsTeamsMessagingPolicy -Identity Global | Select-Object -Property AllowSecurityEndUserReporting return $CsTeamsMessagingPolicy } diff --git a/source/tests/Test-ReportSecurityInTeams.ps1 b/source/tests/Test-ReportSecurityInTeams.ps1 index 2080cb1..961f341 100644 --- a/source/tests/Test-ReportSecurityInTeams.ps1 +++ b/source/tests/Test-ReportSecurityInTeams.ps1 @@ -18,9 +18,28 @@ function Test-ReportSecurityInTeams { # 8.6.1 (L1) Ensure users can report security concerns in Teams # Retrieve the necessary settings for Teams and Exchange Online # Condition A: Ensure the 'Report a security concern' setting in the Teams admin center is set to 'On'. + # $CsTeamsMessagingPolicy Mock Object + <# + $CsTeamsMessagingPolicy = [PSCustomObject]@{ + AllowSecurityEndUserReporting = $true + } + #> $CsTeamsMessagingPolicy = Get-CISMSTeamsOutput -Rec $recnum # 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-CISExoOutput -Rec $recnum # Check if all the required settings are enabled $securityReportEnabled = $CsTeamsMessagingPolicy.AllowSecurityEndUserReporting -and From eecda359d75a9a36d0853db1a0c994bfbdd4000e Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sun, 30 Jun 2024 12:36:44 -0500 Subject: [PATCH 18/21] docs: Update README and HTML Help --- README.md | Bin 41524 -> 43530 bytes docs/index.html | Bin 111548 -> 114692 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/README.md b/README.md index d1fb1899666265927ce0f64e6719ce96b9eac202..e3d4e2297c4890edc2b96f0c77e978a1922818bf 100644 GIT binary patch delta 1708 zcmdmTgsJNa(}q=SllOR|XmW}Ms<>M(@dJb8b>=E)8`ESool3GrB>7fVE^=gAYNncF9#6_yGN z$?)P*0a%717nln0VijIwPJU=FJUJ$sZ}PJQ4O3EdDgaB!M21|3)X4`0v^gCZVi+`l zSaWiqgQPyXk8l*Uu#5!qhcTu<(lCl=1z;8g7SM2g5Vv!QDu9b`kY5!TEEx0{jDXm1 z^1}pO-b7$nfGUr2kXe(pBPAyv6409bAVOxcK^hOR6zAA{Cy7T3QXhTmwG#mV|FC!0 delta 222 zcmeCW!nEZG(}q=Sn_sZaV%;oYz{j|G3U7tdW{x)O%j7PfkjXyLQIicKXH1R>kisgyCJ4LupAhWgKH+Xy%(;Ok gmb7`_6re+o#Qflz{2`WQ^Q79OtljM0CAyK!~g&Q diff --git a/docs/index.html b/docs/index.html index f940d701f8f759fb9b5ef5ec6c8f6dcaedf1c51a..3c65f0fd041ab7d04ea31baec56c1cbc5ded82f5 100644 GIT binary patch delta 1534 zcmdn=2*4?g@{1EYRd=23-bp zlia}Ofz&1fO#+$i0yHld$jfBNL+GD8>jBSXzx!;H18z@X=4Ig8eE)VMj7ShH##%CxbgM)nQL11DW1nPMkluA{LU~7@zJQ zgwC40@b31_7dkGmY;JioN!A=a&l4G&lNoL9Pmg0^6q?Rc%E)1#h*kzEFeJlEAq9qF zlyXP`URohaqK97;r(a=V)G#GQn*y*9N(2^1shjI}%QLb%FlaDnPIfdc*G2ae-f|4p zC&rjQNy8`w6@Vonayj_$i>#;uxP%1xRDr>QK@V6u>oFKkKhMIbtC|Q53Q&<;4mJo? OGpZl9FW_X9Sp@(m(|xx9 delta 175 zcmZo^VBhncZNeFOBL+(beFh^210XhGPyn;6fILG6D+W^ri;b_NZ%ux1&uOy49hS*< zPvkalcGICAkyTiAAog||i Date: Sun, 30 Jun 2024 12:38:58 -0500 Subject: [PATCH 19/21] docs: Update WIKI and xml help --- help/Invoke-M365SecurityAudit.md | 57 ++++-- .../en-US/M365FoundationsCISReport-help.xml | 168 ++++++++++++++++++ 2 files changed, 214 insertions(+), 11 deletions(-) diff --git a/help/Invoke-M365SecurityAudit.md b/help/Invoke-M365SecurityAudit.md index 21ad90a..886c5d2 100644 --- a/help/Invoke-M365SecurityAudit.md +++ b/help/Invoke-M365SecurityAudit.md @@ -14,47 +14,52 @@ Invokes a security audit for Microsoft 365 environments. ### Default (Default) ``` -Invoke-M365SecurityAudit [-TenantAdminUrl ] [-DomainName ] [-DoNotConnect] [-DoNotDisconnect] - [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] [] +Invoke-M365SecurityAudit [-TenantAdminUrl ] [-DomainName ] + [-ApprovedCloudStorageProviders ] [-ApprovedFederatedDomains ] [-DoNotConnect] + [-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] [] ``` ### ELevelFilter ``` Invoke-M365SecurityAudit [-TenantAdminUrl ] [-DomainName ] -ELevel - -ProfileLevel [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] - [-WhatIf] [-Confirm] [] + -ProfileLevel [-ApprovedCloudStorageProviders ] [-ApprovedFederatedDomains ] + [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] + [] ``` ### IG1Filter ``` -Invoke-M365SecurityAudit [-TenantAdminUrl ] [-DomainName ] [-IncludeIG1] [-DoNotConnect] +Invoke-M365SecurityAudit [-TenantAdminUrl ] [-DomainName ] [-IncludeIG1] + [-ApprovedCloudStorageProviders ] [-ApprovedFederatedDomains ] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] [] ``` ### IG2Filter ``` -Invoke-M365SecurityAudit [-TenantAdminUrl ] [-DomainName ] [-IncludeIG2] [-DoNotConnect] +Invoke-M365SecurityAudit [-TenantAdminUrl ] [-DomainName ] [-IncludeIG2] + [-ApprovedCloudStorageProviders ] [-ApprovedFederatedDomains ] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] [] ``` ### IG3Filter ``` -Invoke-M365SecurityAudit [-TenantAdminUrl ] [-DomainName ] [-IncludeIG3] [-DoNotConnect] +Invoke-M365SecurityAudit [-TenantAdminUrl ] [-DomainName ] [-IncludeIG3] + [-ApprovedCloudStorageProviders ] [-ApprovedFederatedDomains ] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] [] ``` ### RecFilter ``` Invoke-M365SecurityAudit [-TenantAdminUrl ] [-DomainName ] -IncludeRecommendation - [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] - [] + [-ApprovedCloudStorageProviders ] [-ApprovedFederatedDomains ] [-DoNotConnect] + [-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] [] ``` ### SkipRecFilter ``` Invoke-M365SecurityAudit [-TenantAdminUrl ] [-DomainName ] -SkipRecommendation - [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] - [] + [-ApprovedCloudStorageProviders ] [-ApprovedFederatedDomains ] [-DoNotConnect] + [-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -300,6 +305,36 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -ApprovedCloudStorageProviders +Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names. + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: @() +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ApprovedFederatedDomains +Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names. + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -DoNotConnect If specified, the cmdlet will not establish a connection to Microsoft 365 services. diff --git a/source/en-US/M365FoundationsCISReport-help.xml b/source/en-US/M365FoundationsCISReport-help.xml index 507ad97..dce77ce 100644 --- a/source/en-US/M365FoundationsCISReport-help.xml +++ b/source/en-US/M365FoundationsCISReport-help.xml @@ -908,6 +908,30 @@ Retrieves the MFA status for the specified user with the UPN "example@domain.com None + + ApprovedCloudStorageProviders + + Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names. + + String[] + + String[] + + + @() + + + ApprovedFederatedDomains + + Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names. + + String[] + + String[] + + + None + DoNotConnect @@ -1012,6 +1036,30 @@ Retrieves the MFA status for the specified user with the UPN "example@domain.com False + + ApprovedCloudStorageProviders + + Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names. + + String[] + + String[] + + + @() + + + ApprovedFederatedDomains + + Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names. + + String[] + + String[] + + + None + DoNotConnect @@ -1116,6 +1164,30 @@ Retrieves the MFA status for the specified user with the UPN "example@domain.com False + + ApprovedCloudStorageProviders + + Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names. + + String[] + + String[] + + + @() + + + ApprovedFederatedDomains + + Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names. + + String[] + + String[] + + + None + DoNotConnect @@ -1220,6 +1292,30 @@ Retrieves the MFA status for the specified user with the UPN "example@domain.com False + + ApprovedCloudStorageProviders + + Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names. + + String[] + + String[] + + + @() + + + ApprovedFederatedDomains + + Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names. + + String[] + + String[] + + + None + DoNotConnect @@ -1325,6 +1421,30 @@ Retrieves the MFA status for the specified user with the UPN "example@domain.com None + + ApprovedCloudStorageProviders + + Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names. + + String[] + + String[] + + + @() + + + ApprovedFederatedDomains + + Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names. + + String[] + + String[] + + + None + DoNotConnect @@ -1430,6 +1550,30 @@ Retrieves the MFA status for the specified user with the UPN "example@domain.com None + + ApprovedCloudStorageProviders + + Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names. + + String[] + + String[] + + + @() + + + ApprovedFederatedDomains + + Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names. + + String[] + + String[] + + + None + DoNotConnect @@ -1607,6 +1751,30 @@ Retrieves the MFA status for the specified user with the UPN "example@domain.com None + + ApprovedCloudStorageProviders + + Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names. + + String[] + + String[] + + + @() + + + ApprovedFederatedDomains + + Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names. + + String[] + + String[] + + + None + DoNotConnect From 416d387c205aacccdcabfbfae0269e9f2eea09ac Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sun, 30 Jun 2024 12:39:44 -0500 Subject: [PATCH 20/21] docs: Update WIKI and xml help --- help/M365FoundationsCISReport.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/help/M365FoundationsCISReport.md b/help/M365FoundationsCISReport.md index 3c8e111..37fff5a 100644 --- a/help/M365FoundationsCISReport.md +++ b/help/M365FoundationsCISReport.md @@ -11,24 +11,24 @@ Locale: en-US The `M365FoundationsCISReport` module provides a set of cmdlets to audit and report on the security compliance of Microsoft 365 environments based on CIS (Center for Internet Security) benchmarks. It enables administrators to generate detailed reports, sync data with CIS Excel sheets, and perform security audits to ensure compliance. ## M365FoundationsCISReport Cmdlets -### [Export-M365SecurityAuditTable](Export-M365SecurityAuditTable.md) +### [Export-M365SecurityAuditTable](Export-M365SecurityAuditTable) Exports M365 security audit results to a CSV file or outputs a specific test result as an object. -### [Get-AdminRoleUserLicense](Get-AdminRoleUserLicense.md) +### [Get-AdminRoleUserLicense](Get-AdminRoleUserLicense) Retrieves user licenses and roles for administrative accounts from Microsoft 365 via the Graph API. -### [Get-MFAStatus](Get-MFAStatus.md) +### [Get-MFAStatus](Get-MFAStatus) Retrieves the MFA (Multi-Factor Authentication) status for Azure Active Directory users. -### [Grant-M365SecurityAuditConsent](Grant-M365SecurityAuditConsent.md) +### [Grant-M365SecurityAuditConsent](Grant-M365SecurityAuditConsent) Grants Microsoft Graph permissions for an auditor. -### [Invoke-M365SecurityAudit](Invoke-M365SecurityAudit.md) +### [Invoke-M365SecurityAudit](Invoke-M365SecurityAudit) Invokes a security audit for Microsoft 365 environments. -### [Remove-RowsWithEmptyCSVStatus](Remove-RowsWithEmptyCSVStatus.md) +### [Remove-RowsWithEmptyCSVStatus](Remove-RowsWithEmptyCSVStatus) Removes rows from an Excel worksheet where the 'CSV_Status' column is empty and saves the result to a new file. -### [Sync-CISExcelAndCsvData](Sync-CISExcelAndCsvData.md) +### [Sync-CISExcelAndCsvData](Sync-CISExcelAndCsvData) Synchronizes and updates data in an Excel worksheet with new information from a CSV file, including audit dates. From 0da06288f1cb7eeffbe4a5dad42b132a1721b8b3 Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sun, 30 Jun 2024 12:41:15 -0500 Subject: [PATCH 21/21] docs: Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0a266d..2549db1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ The format is based on and uses the types of changes according to [Keep a Change - Added `ApprovedCloudStorageProviders` parameter to `Invoke-M365SecurityAudit` to allow for testing of approved cloud storage providers for 8.1.1. - Added `ApprovedFederatedDomains` parameter to `Invoke-M365SecurityAudit` to allow for testing of approved federated domains for 8.5.1. +### Fixed + +- Fixed various MSTeams tests to be more accurate and include more properties in the output. + ## [0.1.18] - 2024-06-29 ### Added