From 0e2e779c82896c78f9e675b31069619608259e2b Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Tue, 2 Jul 2024 09:00:43 -0500 Subject: [PATCH 1/5] fix: up to 7.2.6 SPO formatting and output --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 717dd66..030a3a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,18 @@ The format is based on and uses the types of changes according to [Keep a Change ### Fixed +- SPO tests formatting and output. + +## [0.1.22] - 2024-07-01 + +### Added + +- Added hash and compress steps to `Export-M365SecurityAuditTable` function. + +## [0.1.21] - 2024-07-01 + +### Fixed + - Formatting for MgGraph tests. ## [0.1.20] - 2024-06-30 From 5ef32eb4b8249027f9048fd79cc3934de74cab49 Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Tue, 2 Jul 2024 09:00:54 -0500 Subject: [PATCH 2/5] fix: up to 7.2.6 SPO formatting and output --- source/Private/Get-CISSpoOutput.ps1 | 31 +++++++++++++++++++ source/tests/Test-ModernAuthSharePoint.ps1 | 17 +++++----- .../Test-OneDriveContentRestrictions.ps1 | 18 +++++------ source/tests/Test-RestrictExternalSharing.ps1 | 21 ++++++------- source/tests/Test-SharePointAADB2B.ps1 | 15 ++++----- .../Test-SharePointExternalSharingDomains.ps1 | 14 +++++---- .../Test-SharePointGuestsItemSharing.ps1 | 15 ++++----- 7 files changed, 82 insertions(+), 49 deletions(-) diff --git a/source/Private/Get-CISSpoOutput.ps1 b/source/Private/Get-CISSpoOutput.ps1 index 06c56a4..cd94403 100644 --- a/source/Private/Get-CISSpoOutput.ps1 +++ b/source/Private/Get-CISSpoOutput.ps1 @@ -40,12 +40,24 @@ function Get-CISSpoOutput { switch ($Rec) { '7.2.1' { # Test-ModernAuthSharePoint.ps1 + # $SPOTenant Mock Object + <# + $SPOTenant = [PSCustomObject]@{ + LegacyAuthProtocolsEnabled = $true + } + #> $SPOTenant = Get-SPOTenant | Select-Object -Property LegacyAuthProtocolsEnabled return $SPOTenant } '7.2.2' { # Test-SharePointAADB2B.ps1 # 7.2.2 (L1) Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled + # $SPOTenantAzureADB2B Mock Object + <# + $SPOTenantAzureADB2B = [PSCustomObject]@{ + EnableAzureADB2BIntegration = $false + } + #> $SPOTenantAzureADB2B = Get-SPOTenant | Select-Object EnableAzureADB2BIntegration return $SPOTenantAzureADB2B } @@ -53,17 +65,36 @@ function Get-CISSpoOutput { # Test-RestrictExternalSharing.ps1 # 7.2.3 (L1) Ensure external content sharing is restricted # Retrieve the SharingCapability setting for the SharePoint tenant + # $SPOTenantSharingCapability Mock Object + <# + $SPOTenantSharingCapability = [PSCustomObject]@{ + SharingCapability = "ExternalUserAndGuestSharing" + } + #> $SPOTenantSharingCapability = Get-SPOTenant | Select-Object SharingCapability return $SPOTenantSharingCapability } '7.2.4' { # Test-OneDriveContentRestrictions.ps1 + # 7.2.4 (L2) Ensure OneDrive content sharing is restricted + # $SPOTenant Mock Object + <# + $SPOTenant = [PSCustomObject]@{ + OneDriveSharingCapability = "ExternalUserAndGuestSharing" + } + #> $SPOTenant = Get-SPOTenant | Select-Object OneDriveSharingCapability return $SPOTenant } '7.2.5' { # Test-SharePointGuestsItemSharing.ps1 # 7.2.5 (L2) Ensure that SharePoint guest users cannot share items they don't own + # $SPOTenant Mock Object + <# + $SPOTenant = [PSCustomObject]@{ + PreventExternalUsersFromResharing = $false + } + #> $SPOTenant = Get-SPOTenant | Select-Object PreventExternalUsersFromResharing return $SPOTenant } diff --git a/source/tests/Test-ModernAuthSharePoint.ps1 b/source/tests/Test-ModernAuthSharePoint.ps1 index 64e2598..15fbb81 100644 --- a/source/tests/Test-ModernAuthSharePoint.ps1 +++ b/source/tests/Test-ModernAuthSharePoint.ps1 @@ -12,16 +12,14 @@ function Test-ModernAuthSharePoint { # Initialization code, if needed $recnum = "7.2.1" - + Write-Verbose "Running Test-ModernAuthSharePoint for $recnum..." <# # Conditions for 7.2.1 (L1) Ensure modern authentication for SharePoint applications is required - ## Validate test for a pass: # - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark. # - Specific conditions to check: # - Condition A: The setting "Apps that don't use modern authentication" is set to "Block access" in the SharePoint admin center. # - Condition B: The PowerShell command `Get-SPOTenant | ft LegacyAuthProtocolsEnabled` returns `False`. - ## 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: @@ -29,23 +27,25 @@ function Test-ModernAuthSharePoint { # - Condition B: The PowerShell command `Get-SPOTenant | ft LegacyAuthProtocolsEnabled` returns `True`. #> } - process { try { # 7.2.1 (L1) Ensure modern authentication for SharePoint applications is required + # $SPOTenant Mock Object + <# + $SPOTenant = [PSCustomObject]@{ + LegacyAuthProtocolsEnabled = $true + } + #> $SPOTenant = Get-CISSpoOutput -Rec $recnum $modernAuthForSPRequired = -not $SPOTenant.LegacyAuthProtocolsEnabled - # Prepare failure reasons and details based on compliance $failureReasons = if (-not $modernAuthForSPRequired) { - "Legacy authentication protocols are enabled" # Fail Condition B + "Legacy authentication protocols are enabled. The following command can be used to disable:`nSet-SPOTenant -LegacyAuthProtocolsEnabled `$false" # Fail Condition B } else { "N/A" } - $details = "LegacyAuthProtocolsEnabled: $($SPOTenant.LegacyAuthProtocolsEnabled)" # Details for Condition B - # Create and populate the CISAuditResult object $params = @{ Rec = $recnum @@ -61,7 +61,6 @@ function Test-ModernAuthSharePoint { $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return the audit result return $auditResult diff --git a/source/tests/Test-OneDriveContentRestrictions.ps1 b/source/tests/Test-OneDriveContentRestrictions.ps1 index 44b9d7f..d97da01 100644 --- a/source/tests/Test-OneDriveContentRestrictions.ps1 +++ b/source/tests/Test-OneDriveContentRestrictions.ps1 @@ -5,7 +5,6 @@ function Test-OneDriveContentRestrictions { # Aligned # Define your parameters here ) - begin { # 7.2.4 (L2) Ensure OneDrive content sharing is restricted # @@ -22,36 +21,38 @@ function Test-OneDriveContentRestrictions { # - Condition A: The OneDriveSharingCapability setting is not configured to "Disabled" using the PowerShell cmdlet `Get-SPOTenant | fl OneDriveSharingCapability`. # - Condition B: The OneDriveSharingCapability is not set to "Only people in your organization" in the SharePoint admin center under Policies > Sharing > OneDrive. # - Condition C: OneDrive content sharing is more permissive than SharePoint content sharing. - # Dot source the class script if necessary #. .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed $recnum = "7.2.4" + Write-Verbose "Running Test-OneDriveContentRestrictions for $recnum..." } - process { try { # 7.2.4 (L2) Ensure OneDrive content sharing is restricted - # Retrieve OneDrive sharing capability settings + # $SPOTenant Mock Object + <# + $SPOTenant = [PSCustomObject]@{ + OneDriveSharingCapability = "ExternalUserAndGuestSharing" + } + #> $SPOTenant = Get-CISSpoOutput -Rec $recnum $isOneDriveSharingRestricted = $SPOTenant.OneDriveSharingCapability -eq 'Disabled' - # Prepare failure reasons and details based on compliance $failureReasons = if (-not $isOneDriveSharingRestricted) { - "OneDrive content sharing is not restricted to 'Disabled'. Current setting: $($SPOTenant.OneDriveSharingCapability)" + "OneDrive content sharing is not restricted to 'Disabled'. To remediate this setting, use the Set-SPOTenant command.`n`n" + ` + "Set-SPOTenant -OneDriveSharingCapability Disabled" } else { "N/A" } - $details = if ($isOneDriveSharingRestricted) { "OneDrive content sharing is restricted." } else { "OneDriveSharingCapability: $($SPOTenant.OneDriveSharingCapability)" } - # Create and populate the CISAuditResult object $params = @{ Rec = $recnum @@ -67,7 +68,6 @@ function Test-OneDriveContentRestrictions { $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return auditResult return $auditResult diff --git a/source/tests/Test-RestrictExternalSharing.ps1 b/source/tests/Test-RestrictExternalSharing.ps1 index d508445..a84a53e 100644 --- a/source/tests/Test-RestrictExternalSharing.ps1 +++ b/source/tests/Test-RestrictExternalSharing.ps1 @@ -5,18 +5,15 @@ function Test-RestrictExternalSharing { # Aligned # Define your parameters here ) - begin { <# Conditions for 7.2.3 (L1) Ensure external content sharing is restricted - Validate test for a pass: - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark. - Specific conditions to check: - Condition A: The SharingCapability is set to "ExternalUserSharingOnly" or more restrictive in the SharePoint admin center. - Condition B: Using PowerShell, the SharingCapability property for the SharePoint tenant is set to "ExternalUserSharingOnly", "ExistingExternalUserSharingOnly", or "Disabled". - Condition C: The external sharing settings in SharePoint Online and OneDrive are set to the same or a more restrictive level than the organization’s sharing settings. - 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: @@ -24,34 +21,37 @@ function Test-RestrictExternalSharing { - Condition B: Using PowerShell, the SharingCapability property for the SharePoint tenant is set to "Anyone" or "ExternalUserAndGuestSharing". - Condition C: The external sharing settings in SharePoint Online and OneDrive are set to a more permissive level than the organization’s sharing settings. #> - # Dot source the class script if necessary #. .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed $recnum = "7.2.3" + Write-Verbose "Running Test-RestrictExternalSharing for $recnum..." } - process { try { # 7.2.3 (L1) Ensure external content sharing is restricted - # Retrieve the SharingCapability setting for the SharePoint tenant + <# + $SPOTenantSharingCapability = [PSCustomObject]@{ + SharingCapability = "ExternalUserAndGuestSharing" + } + #> $SPOTenantSharingCapability = Get-CISSpoOutput -Rec $recnum $isRestricted = $SPOTenantSharingCapability.SharingCapability -in @('ExternalUserSharingOnly', 'ExistingExternalUserSharingOnly', 'Disabled') - # Prepare failure reasons and details based on compliance # Condition B: Using PowerShell, the SharingCapability property for the SharePoint tenant is set to "ExternalUserSharingOnly", "ExistingExternalUserSharingOnly", or "Disabled". $failureReasons = if (-not $isRestricted) { - "External content sharing is not adequately restricted. Current setting: $($SPOTenantSharingCapability.SharingCapability)" + "External content sharing is not adequately restricted. Current setting: $($SPOTenantSharingCapability.SharingCapability)`n" + ` + "The acceptable values for SharingCapability are: 'ExternalUserSharingOnly', 'ExistingExternalUserSharingOnly', or 'Disabled'.`n" + ` + "To remediate this setting, use the Set-SPOTenant cmdlet to set the SharingCapability property to an acceptable value:`n`n" + ` + "Set-SPOTenant -SharingCapability " } else { "N/A" } - # Condition A: The SharingCapability is set to "ExternalUserSharingOnly" or more restrictive in the SharePoint admin center. # Condition C: The external sharing settings in SharePoint Online and OneDrive are set to the same or a more restrictive level than the organization’s sharing settings. $details = "SharingCapability: $($SPOTenantSharingCapability.SharingCapability)" - # Create and populate the CISAuditResult object $params = @{ Rec = $recnum @@ -67,7 +67,6 @@ function Test-RestrictExternalSharing { $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return auditResult return $auditResult diff --git a/source/tests/Test-SharePointAADB2B.ps1 b/source/tests/Test-SharePointAADB2B.ps1 index afbc0d9..f39e445 100644 --- a/source/tests/Test-SharePointAADB2B.ps1 +++ b/source/tests/Test-SharePointAADB2B.ps1 @@ -5,7 +5,6 @@ function Test-SharePointAADB2B { # Aligned # Define your parameters here ) - begin { # Conditions for 7.2.2 (L1) Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled # @@ -22,26 +21,29 @@ function Test-SharePointAADB2B { # - Condition A: The `EnableAzureADB2BIntegration` property is set to `False` for the SharePoint tenant. # - Condition B: The integration between SharePoint, OneDrive, and Azure AD B2B is not active. # - Condition C: Guest accounts are not managed in Azure AD and are not subject to access policies. - # Dot source the class script if necessary #. .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed - $recnum = "7.2.2" + Write-Verbose "Running Test-SharePointAADB2B for $recnum..." } - process { try { # 7.2.2 (L1) Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled + # $SPOTenantAzureADB2B Mock Object + <# + $SPOTenantAzureADB2B = [PSCustomObject]@{ + EnableAzureADB2BIntegration = $false + } + #> $SPOTenantAzureADB2B = Get-CISSpoOutput -Rec $recnum - # Populate the auditResult object with the required properties $params = @{ Rec = $recnum Result = $SPOTenantAzureADB2B.EnableAzureADB2BIntegration Status = if ($SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Pass" } else { "Fail" } Details = "EnableAzureADB2BIntegration: $($SPOTenantAzureADB2B.EnableAzureADB2BIntegration)" - FailureReason = if (-not $SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Azure AD B2B integration is not enabled" } else { "N/A" } + FailureReason = if (-not $SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Azure AD B2B integration is not enabled. The following command can be used to enable:`nSet-SPOTenant -EnableAzureADB2BIntegration `$true" } else { "N/A" } } $auditResult = Initialize-CISAuditResult @params } @@ -50,7 +52,6 @@ function Test-SharePointAADB2B { $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return auditResult return $auditResult diff --git a/source/tests/Test-SharePointExternalSharingDomains.ps1 b/source/tests/Test-SharePointExternalSharingDomains.ps1 index 246c41c..1d26a5b 100644 --- a/source/tests/Test-SharePointExternalSharingDomains.ps1 +++ b/source/tests/Test-SharePointExternalSharingDomains.ps1 @@ -5,14 +5,12 @@ function Test-SharePointExternalSharingDomains { # Aligned # Define your parameters here ) - begin { # Dot source the class script if necessary #. .\source\Classes\CISAuditResult.ps1 - # Initialization code, if needed $recnum = "7.2.6" - + Write-Verbose "Running Test-SharePointExternalSharingDomains for $recnum..." # Conditions for 7.2.6 (L2) Ensure SharePoint external sharing is managed through domain whitelist/blacklists # # Validate test for a pass: @@ -29,13 +27,18 @@ function Test-SharePointExternalSharingDomains { # - Condition B: The "SharingDomainRestrictionMode" is not set to "AllowList" using PowerShell. # - Condition C: The "SharingAllowedDomainList" does not contain the domains trusted by the organization for external sharing. } - process { try { # 7.2.6 (L2) Ensure SharePoint external sharing is managed through domain whitelist/blacklists $SPOTenant = Get-CISSpoOutput -Rec $recnum + # $SPOTenant Mock Object + <# + $SPOTenant = [PSCustomObject]@{ + SharingDomainRestrictionMode = "AllowList" + SharingAllowedDomainList = "domain1.com", "domain2.com" + } + #> $isDomainRestrictionConfigured = $SPOTenant.SharingDomainRestrictionMode -eq 'AllowList' - # Populate the auditResult object with the required properties $params = @{ Rec = $recnum @@ -51,7 +54,6 @@ function Test-SharePointExternalSharingDomains { $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return auditResult return $auditResult diff --git a/source/tests/Test-SharePointGuestsItemSharing.ps1 b/source/tests/Test-SharePointGuestsItemSharing.ps1 index 58c834f..f11fc4a 100644 --- a/source/tests/Test-SharePointGuestsItemSharing.ps1 +++ b/source/tests/Test-SharePointGuestsItemSharing.ps1 @@ -5,14 +5,12 @@ function Test-SharePointGuestsItemSharing { # Aligned # Define your parameters here ) - begin { # Dot source the class script if necessary #. .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed - $recnum = "7.2.5" - + Write-Verbose "Running Test-SharePointGuestsItemSharing for $recnum..." # Conditions for 7.2.5 (L2) Ensure that SharePoint guest users cannot share items they don't own # # Validate test for a pass: @@ -29,20 +27,24 @@ function Test-SharePointGuestsItemSharing { # - Condition B: The SharePoint admin center setting "Allow guests to share items they don't own" is checked. # - Condition C: Ensure that external users can re-share items they don't own. } - process { try { # 7.2.5 (L2) Ensure that SharePoint guest users cannot share items they don't own + # $SPOTenant Mock Object + <# + $SPOTenant = [PSCustomObject]@{ + PreventExternalUsersFromResharing = $false + } + #> $SPOTenant = Get-CISSpoOutput -Rec $recnum $isGuestResharingPrevented = $SPOTenant.PreventExternalUsersFromResharing - # Populate the auditResult object with the required properties $params = @{ Rec = $recnum Result = $isGuestResharingPrevented Status = if ($isGuestResharingPrevented) { "Pass" } else { "Fail" } Details = "PreventExternalUsersFromResharing: $isGuestResharingPrevented" - FailureReason = if (-not $isGuestResharingPrevented) { "Guest users can reshare items they don't own." } else { "N/A" } + FailureReason = if (-not $isGuestResharingPrevented) { "Guest users can reshare items they don't own. To prevent external users from resharing content they don't own,`nuse the following command:`nSet-SPOTenant -PreventExternalUsersFromResharing `$True" } else { "N/A" } } $auditResult = Initialize-CISAuditResult @params } @@ -51,7 +53,6 @@ function Test-SharePointGuestsItemSharing { $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return auditResult return $auditResult From 5d11b46d9600eab13983779fd22f782d368c184c Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Tue, 2 Jul 2024 09:47:06 -0500 Subject: [PATCH 3/5] fix: SPO formatting and output --- source/Private/Get-CISSpoOutput.ps1 | 50 +++++++++++++++++++ .../Test-DisallowInfectedFilesDownload.ps1 | 22 ++++---- source/tests/Test-GuestAccessExpiration.ps1 | 20 ++++---- source/tests/Test-LinkSharingRestrictions.ps1 | 18 +++---- .../tests/Test-OneDriveSyncRestrictions.ps1 | 13 ++--- source/tests/Test-ReauthWithCode.ps1 | 19 +++---- source/tests/Test-RestrictCustomScripts.ps1 | 27 +++++----- 7 files changed, 106 insertions(+), 63 deletions(-) diff --git a/source/Private/Get-CISSpoOutput.ps1 b/source/Private/Get-CISSpoOutput.ps1 index cd94403..99362cd 100644 --- a/source/Private/Get-CISSpoOutput.ps1 +++ b/source/Private/Get-CISSpoOutput.ps1 @@ -101,18 +101,39 @@ function Get-CISSpoOutput { '7.2.6' { # Test-SharePointExternalSharingDomains.ps1 # 7.2.6 (L2) Ensure SharePoint external sharing is managed through domain whitelist/blacklists + # Add Authorized Domains? + # $SPOTenant Mock Object + <# + $SPOTenant = [PSCustomObject]@{ + SharingDomainRestrictionMode = "AllowList" + SharingAllowedDomainList = "domain1.com", "domain2.com" + } + #> $SPOTenant = Get-SPOTenant | Select-Object SharingDomainRestrictionMode, SharingAllowedDomainList return $SPOTenant } '7.2.7' { # Test-LinkSharingRestrictions.ps1 # Retrieve link sharing configuration for SharePoint and OneDrive + # $SPOTenantLinkSharing Mock Object + <# + $$SPOTenantLinkSharing = [PSCustomObject]@{ + DefaultSharingLinkType = "Direct" + } + #> $SPOTenantLinkSharing = Get-SPOTenant | Select-Object DefaultSharingLinkType return $SPOTenantLinkSharing } '7.2.9' { # Test-GuestAccessExpiration.ps1 # Retrieve SharePoint tenant settings related to guest access expiration + # $SPOTenantGuestAccess Mock Object + <# + $SPOTenantGuestAccess = [PSCustomObject]@{ + ExternalUserExpirationRequired = "$false" + ExternalUserExpireInDays = "60" + } + #> $SPOTenantGuestAccess = Get-SPOTenant | Select-Object ExternalUserExpirationRequired, ExternalUserExpireInDays return $SPOTenantGuestAccess } @@ -120,24 +141,53 @@ function Get-CISSpoOutput { # Test-ReauthWithCode.ps1 # 7.2.10 (L1) Ensure reauthentication with verification code is restricted # Retrieve reauthentication settings for SharePoint Online + # $SPOTenantReauthentication Mock Object + <# + $SPOTenantReauthentication = [PSCustomObject]@{ + EmailAttestationRequired = "$false" + EmailAttestationReAuthDays = "30" + } + #> $SPOTenantReauthentication = Get-SPOTenant | Select-Object EmailAttestationRequired, EmailAttestationReAuthDays return $SPOTenantReauthentication } '7.3.1' { # Test-DisallowInfectedFilesDownload.ps1 # Retrieve the SharePoint tenant configuration + # $SPOTenantDisallowInfectedFileDownload Mock Object + <# + $SPOTenantDisallowInfectedFileDownload = [PSCustomObject]@{ + DisallowInfectedFileDownload = $false + } + #> $SPOTenantDisallowInfectedFileDownload = Get-SPOTenant | Select-Object DisallowInfectedFileDownload return $SPOTenantDisallowInfectedFileDownload } '7.3.2' { # Test-OneDriveSyncRestrictions.ps1 # Retrieve OneDrive sync client restriction settings + # Add isHybrid paramter? + # $SPOTenantSyncClientRestriction Mock Object + <# + $SPOTenantSyncClientRestriction = [PSCustomObject]@{ + TenantRestrictionEnabled = $true + AllowedDomainList = "786548DD-877B-4760-A749-6B1EFBC1190A", "877564FF-877B-4760-A749-6B1EFBC1190A" + } + #> $SPOTenantSyncClientRestriction = Get-SPOTenantSyncClientRestriction | Select-Object TenantRestrictionEnabled, AllowedDomainList return $SPOTenantSyncClientRestriction } '7.3.4' { # Test-RestrictCustomScripts.ps1 # Retrieve all site collections and select necessary properties + # $SPOSitesCustomScript Mock Object + <# + $SPOSitesCustomScript = [PSCustomObject]@{ + Title = "Site Collection 1" + Url = "https://contoso.sharepoint.com/sites/site1" + DenyAddAndCustomizePages = "Enabled" + } + #> $SPOSitesCustomScript = Get-SPOSite -Limit All | Select-Object Title, Url, DenyAddAndCustomizePages return $SPOSitesCustomScript } diff --git a/source/tests/Test-DisallowInfectedFilesDownload.ps1 b/source/tests/Test-DisallowInfectedFilesDownload.ps1 index 851c90b..8afd659 100644 --- a/source/tests/Test-DisallowInfectedFilesDownload.ps1 +++ b/source/tests/Test-DisallowInfectedFilesDownload.ps1 @@ -5,17 +5,14 @@ function Test-DisallowInfectedFilesDownload { # Aligned # Define your parameters here if needed ) - begin { # Dot source the class script if necessary #. .\source\Classes\CISAuditResult.ps1 - # Initialization code, if needed $recnum = "7.3.1" + Write-Verbose "Running Test-DisallowInfectedFilesDownload for $recnum..." } - process { - try { # 7.3.1 (L2) Ensure Office 365 SharePoint infected files are disallowed for download # @@ -32,28 +29,30 @@ function Test-DisallowInfectedFilesDownload { # - Condition A: The `DisallowInfectedFileDownload` setting is not set to `True`. # - Condition B: The setting does not prevent users from downloading infected files. # - Condition C: Verification using the PowerShell command indicates that the setting is incorrectly configured. - # Retrieve the SharePoint tenant configuration + # $SPOTenantDisallowInfectedFileDownload Mock Object + <# + $SPOTenantDisallowInfectedFileDownload = [PSCustomObject]@{ + DisallowInfectedFileDownload = $false + } + #> $SPOTenantDisallowInfectedFileDownload = Get-CISSpoOutput -Rec $recnum - # Condition A: The `DisallowInfectedFileDownload` setting is set to `True` $isDisallowInfectedFileDownloadEnabled = $SPOTenantDisallowInfectedFileDownload.DisallowInfectedFileDownload - # Prepare failure reasons and details based on compliance $failureReasons = if (-not $isDisallowInfectedFileDownloadEnabled) { - "Downloading infected files is not disallowed." # Condition B: The setting does not prevent users from downloading infected files + "Downloading infected files is not disallowed. To ensure infected files cannot be downloaded, use the following command:`n" + ` # Condition B: The setting does not prevent users from downloading infected files + "Set-SPOTenant -DisallowInfectedFileDownload `$true" } else { "N/A" } - $details = if ($isDisallowInfectedFileDownloadEnabled) { "DisallowInfectedFileDownload: True" # Condition C: Verification confirms the setting is correctly configured } else { "DisallowInfectedFileDownload: False" # Condition C: Verification indicates the setting is incorrectly configured } - # Create and populate the CISAuditResult object $params = @{ Rec = $recnum @@ -69,9 +68,8 @@ function Test-DisallowInfectedFilesDownload { $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return the audit result return $auditResult } -} +} \ No newline at end of file diff --git a/source/tests/Test-GuestAccessExpiration.ps1 b/source/tests/Test-GuestAccessExpiration.ps1 index 92d40a0..666ad61 100644 --- a/source/tests/Test-GuestAccessExpiration.ps1 +++ b/source/tests/Test-GuestAccessExpiration.ps1 @@ -5,17 +5,14 @@ function Test-GuestAccessExpiration { # Aligned # Define your parameters here if needed ) - begin { # Dot source the class script if necessary #. .\source\Classes\CISAuditResult.ps1 - # Initialization code, if needed $recnum = "7.2.9" + Write-Verbose "Running Test-GuestAccessExpiration for $recnum..." } - process { - try { # 7.2.9 (L1) Ensure guest access to a site or OneDrive will expire automatically # @@ -32,21 +29,25 @@ function Test-GuestAccessExpiration { # - Condition A: The ExternalUserExpirationRequired setting in SharePoint is not set to True. # - Condition B: The ExternalUserExpireInDays setting in SharePoint is configured to more than 30 days. # - Condition C: Verification using the SharePoint Admin Center indicates that guest access is not set to expire automatically after the specified number of days. - # Retrieve SharePoint tenant settings related to guest access expiration + # $SPOTenantGuestAccess Mock Object + <# + $SPOTenantGuestAccess = [PSCustomObject]@{ + ExternalUserExpirationRequired = "$false" + ExternalUserExpireInDays = "60" + } + #> $SPOTenantGuestAccess = Get-CISSpoOutput -Rec $recnum $isGuestAccessExpirationConfiguredCorrectly = $SPOTenantGuestAccess.ExternalUserExpirationRequired -and $SPOTenantGuestAccess.ExternalUserExpireInDays -le 30 - # Prepare failure reasons and details based on compliance $failureReasons = if (-not $isGuestAccessExpirationConfiguredCorrectly) { - "Guest access expiration is not configured to automatically expire within 30 days or less." + "Guest access expiration is not configured to automatically expire within 30 days or less. To remediate this setting, use the Set-SPOTenant command:`n`n" + ` + "Set-SPOTenant -ExternalUserExpirationRequired `$true -ExternalUserExpireInDays 30" } else { "N/A" } - $details = "ExternalUserExpirationRequired: $($SPOTenantGuestAccess.ExternalUserExpirationRequired); ExternalUserExpireInDays: $($SPOTenantGuestAccess.ExternalUserExpireInDays)" - # Create and populate the CISAuditResult object $params = @{ Rec = $recnum @@ -62,7 +63,6 @@ function Test-GuestAccessExpiration { $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return the audit result return $auditResult diff --git a/source/tests/Test-LinkSharingRestrictions.ps1 b/source/tests/Test-LinkSharingRestrictions.ps1 index 9816d51..08fdab0 100644 --- a/source/tests/Test-LinkSharingRestrictions.ps1 +++ b/source/tests/Test-LinkSharingRestrictions.ps1 @@ -6,14 +6,13 @@ function Test-LinkSharingRestrictions { # Define your parameters here # Test behavior in prod ) - begin { # Dot source the class script if necessary #. .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed $recnum = "7.2.7" + Write-Verbose "Running Test-LinkSharingRestrictions for $recnum..." } - process { try { # 7.2.7 (L1) Ensure link sharing is restricted in SharePoint and OneDrive @@ -31,21 +30,24 @@ function Test-LinkSharingRestrictions { # - Condition A: The `DefaultSharingLinkType` setting in SharePoint and OneDrive is not set to `Direct`. # - Condition B: The setting `Choose the type of link that's selected by default when users share files and folders in SharePoint and OneDrive` is not set to `Specific people (only the people the user specifies)`. # - Condition C: Verification using the UI indicates that the link sharing settings are not configured as recommended. - # Retrieve link sharing configuration for SharePoint and OneDrive + # $SPOTenantLinkSharing Mock Object + <# + $$SPOTenantLinkSharing = [PSCustomObject]@{ + DefaultSharingLinkType = "Direct" + } + #> $SPOTenantLinkSharing = Get-CISSpoOutput -Rec $recnum $isLinkSharingRestricted = $SPOTenantLinkSharing.DefaultSharingLinkType -eq 'Direct' # Or 'SpecificPeople' as per the recommendation - # Prepare failure reasons and details based on compliance $failureReasons = if (-not $isLinkSharingRestricted) { - "Link sharing is not restricted to 'Specific people'. Current setting: $($SPOTenantLinkSharing.DefaultSharingLinkType)" + "Link sharing is not restricted to 'Specific people'. Current setting: $($SPOTenantLinkSharing.DefaultSharingLinkType). To remediate this setting, use the Set-SPOTenant command:`n`n" + ` + "Set-SPOTenant -DefaultSharingLinkType Direct" } else { "N/A" } - $details = "DefaultSharingLinkType: $($SPOTenantLinkSharing.DefaultSharingLinkType)" - # Create and populate the CISAuditResult object $params = @{ Rec = $recnum @@ -55,14 +57,12 @@ function Test-LinkSharingRestrictions { FailureReason = $failureReasons } $auditResult = Initialize-CISAuditResult @params - } catch { $LastError = $_ $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return the audit result return $auditResult diff --git a/source/tests/Test-OneDriveSyncRestrictions.ps1 b/source/tests/Test-OneDriveSyncRestrictions.ps1 index f689d37..b78eeae 100644 --- a/source/tests/Test-OneDriveSyncRestrictions.ps1 +++ b/source/tests/Test-OneDriveSyncRestrictions.ps1 @@ -5,14 +5,13 @@ function Test-OneDriveSyncRestrictions { # Aligned # Define your parameters here ) - begin { # Dot source the class script if necessary #. .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed $recnum = "7.3.2" + Write-Verbose "Running Test-OneDriveSyncRestrictions for $recnum..." } - process { try { # 7.3.2 (L2) Ensure OneDrive sync is restricted for unmanaged devices @@ -30,20 +29,20 @@ function Test-OneDriveSyncRestrictions { # - Condition A: "Allow syncing only on computers joined to specific domains" is not enabled. # - Condition B: "TenantRestrictionEnabled" is set to False. # - Condition C: "AllowedDomainList" does not contain the trusted domain GUIDs from the on-premises environment. - # Retrieve OneDrive sync client restriction settings $SPOTenantSyncClientRestriction = Get-CISSpoOutput -Rec $recnum $isSyncRestricted = $SPOTenantSyncClientRestriction.TenantRestrictionEnabled -and $SPOTenantSyncClientRestriction.AllowedDomainList - # Condition A: Check if TenantRestrictionEnabled is True # Condition B: Ensure AllowedDomainList contains trusted domains GUIDs $failureReasons = if (-not $isSyncRestricted) { - "OneDrive sync is not restricted to managed devices. TenantRestrictionEnabled should be True and AllowedDomainList should contain trusted domains GUIDs." + "OneDrive sync is not restricted to managed devices. For hybrid devices, TenantRestrictionEnabled should be True and AllowedDomainList should contain trusted domains GUIDs.`n" + ` + "To remediate this setting, edit and use the Set-SPOTenantSyncClientRestriction command below:`n" + ` + "Set-SPOTenantSyncClientRestriction -TenantRestrictionEnabled `$true -AllowedDomainList `"`",`"`"`n`n" + ` + "Note: Utilize the -BlockMacSync:`$true parameter if you are not using conditional access to ensure Macs cannot sync." } else { "N/A" } - # Condition C: Prepare details based on whether sync is restricted $details = if ($isSyncRestricted) { "OneDrive sync is restricted for unmanaged devices." @@ -51,7 +50,6 @@ function Test-OneDriveSyncRestrictions { else { "TenantRestrictionEnabled: $($SPOTenantSyncClientRestriction.TenantRestrictionEnabled); AllowedDomainList: $($SPOTenantSyncClientRestriction.AllowedDomainList -join ', ')" } - # Create and populate the CISAuditResult object $params = @{ Rec = $recnum @@ -67,7 +65,6 @@ function Test-OneDriveSyncRestrictions { $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return auditResult return $auditResult diff --git a/source/tests/Test-ReauthWithCode.ps1 b/source/tests/Test-ReauthWithCode.ps1 index 9e68202..1ec2bb0 100644 --- a/source/tests/Test-ReauthWithCode.ps1 +++ b/source/tests/Test-ReauthWithCode.ps1 @@ -5,7 +5,6 @@ function Test-ReauthWithCode { # Aligned # Define your parameters here ) - begin { <# Conditions for 7.2.10 (L1) Ensure reauthentication with verification code is restricted @@ -22,31 +21,34 @@ function Test-ReauthWithCode { # - Condition A: The `EmailAttestationRequired` property is set to `False`. # - Condition B: The `EmailAttestationReAuthDays` property is set to more than `15`. #> - # Dot source the class script if necessary #. .\source\Classes\CISAuditResult.ps1 # Initialization code, if needed $recnum = "7.2.10" + Write-Verbose "Running Test-ReauthWithCode for $recnum..." } - process { try { # 7.2.10 (L1) Ensure reauthentication with verification code is restricted - # Retrieve reauthentication settings for SharePoint Online + # $SPOTenantReauthentication Mock Object + <# + $SPOTenantReauthentication = [PSCustomObject]@{ + EmailAttestationRequired = "$false" + EmailAttestationReAuthDays = "30" + } + #> $SPOTenantReauthentication = Get-CISSpoOutput -Rec $recnum $isReauthenticationRestricted = $SPOTenantReauthentication.EmailAttestationRequired -and $SPOTenantReauthentication.EmailAttestationReAuthDays -le 15 - # Prepare failure reasons and details based on compliance $failureReasons = if (-not $isReauthenticationRestricted) { - "Reauthentication with verification code does not require reauthentication within 15 days or less." + "Reauthentication with verification code does not require reauthentication within 15 days or less. To remediate this setting, use the Set-SPOTenant command:`n" + ` + "Set-SPOTenant -EmailAttestationRequired `$true -EmailAttestationReAuthDays 15" } else { "N/A" } - $details = "EmailAttestationRequired: $($SPOTenantReauthentication.EmailAttestationRequired); EmailAttestationReAuthDays: $($SPOTenantReauthentication.EmailAttestationReAuthDays)" - # Create and populate the CISAuditResult object $params = @{ Rec = $recnum @@ -62,7 +64,6 @@ function Test-ReauthWithCode { $auditResult = Get-TestError -LastError $LastError -recnum $recnum } } - end { # Return auditResult return $auditResult diff --git a/source/tests/Test-RestrictCustomScripts.ps1 b/source/tests/Test-RestrictCustomScripts.ps1 index 88f1c75..b9f9cda 100644 --- a/source/tests/Test-RestrictCustomScripts.ps1 +++ b/source/tests/Test-RestrictCustomScripts.ps1 @@ -4,15 +4,13 @@ function Test-RestrictCustomScripts { param ( # Define your parameters here if needed ) - begin { # Dot source the class script if necessary # . .\source\Classes\CISAuditResult.ps1 - # Initialization code, if needed $recnum = "7.3.4" + Write-Verbose "Running Test-RestrictCustomScripts for $recnum..." } - process { try { # 7.3.4 (L1) Ensure custom script execution is restricted on site collections @@ -30,23 +28,27 @@ function Test-RestrictCustomScripts { # - Condition A: The `DenyAddAndCustomizePages` setting is not set to `Enabled` for any site collection. # - Condition B: The setting is not validated through PowerShell commands, indicating misconfiguration. # - Condition C: Verification using the SharePoint Admin Center indicates that the `DenyAddAndCustomizePages` setting is not enforced. - # Retrieve all site collections and select necessary properties + # $SPOSitesCustomScript Mock Object + <# + $SPOSitesCustomScript = [PSCustomObject]@{ + Title = "Site Collection 1" + Url = "https://contoso.sharepoint.com/sites/site1" + DenyAddAndCustomizePages = "Enabled" + } + #> $SPOSitesCustomScript = Get-CISSpoOutput -Rec $recnum - # Process URLs to replace 'sharepoint.com' with '' $processedUrls = $SPOSitesCustomScript | ForEach-Object { $_.Url = $_.Url -replace 'sharepoint\.com', '' $_ } - # Find sites where custom scripts are allowed $customScriptAllowedSites = $processedUrls | Where-Object { $_.DenyAddAndCustomizePages -ne 'Enabled' } #$verbosePreference = 'Continue' # Check the total length of URLs $totalUrlLength = ($customScriptAllowedSites.Url -join '').Length Write-Verbose "Total length of URLs: $totalUrlLength" - # Extract hostnames from allowed sites if the total length exceeds the limit $mostUsedHostname = $null if ($totalUrlLength -gt 20000) { @@ -57,7 +59,6 @@ function Test-RestrictCustomScripts { } } Write-Verbose "Extracted hostnames: $($hostnames -join ', ')" - # Find the most used hostname using the Get-MostCommonWord function $mostUsedHostname = Get-MostCommonWord -InputStrings $hostnames Write-Verbose "Most used hostname: $mostUsedHostname" @@ -65,7 +66,6 @@ function Test-RestrictCustomScripts { #$verbosePreference = 'SilentlyContinue' # Compliance is true if no sites allow custom scripts $complianceResult = $customScriptAllowedSites.Count -eq 0 - # Gather details for non-compliant sites (where custom scripts are allowed) $nonCompliantSiteDetails = $customScriptAllowedSites | ForEach-Object { $url = $_.Url @@ -74,32 +74,29 @@ function Test-RestrictCustomScripts { } "$(if ($_.Title) {$_.Title} else {"NoTitle"})|$url" } - # Prepare failure reasons and details based on compliance $failureReasons = if (-not $complianceResult) { - "Some site collections are not restricting custom script execution. Review Details property for sites that are not aligned with the benchmark." + "Some site collections are not restricting custom script execution. Review Details property for sites that are not aligned with the benchmark.`n" + ` + "To remediate this setting, use the following command:`n" + ` + "Set-SPOSite -Identity -DenyAddAndCustomizePages `$true" } else { "N/A" } - $details = if ($complianceResult) { "All site collections have custom script execution restricted" } else { "Title|Url`n" + ($nonCompliantSiteDetails -join "`n") } - # Convert details to PSObject and check length $detailsPSObject = $details | ConvertFrom-Csv -Delimiter '|' $detailsLength = ($detailsPSObject | ForEach-Object { $_.Url }).Length - if ($detailsLength -gt 32767) { # Create a preview of the first 10 results $preview = $detailsPSObject | Select-Object -First 10 | ForEach-Object { "$($_.Title)|$($_.Url)" } $details = "The output is too large. Here is a preview of the first 10 results:`n`n" + ($preview -join "`n") + "`n`nPlease run the test with the following commands to get the full details:`n`nGet-SPOSite -Limit All | Where-Object { `$.DenyAddAndCustomizePages -ne 'Enabled' } | Select-Object Title, Url" } - # Create and populate the CISAuditResult object $params = @{ Rec = $recnum From a80e26f08997ff1c47a974ac78ea1a233bdd714f Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Tue, 2 Jul 2024 09:54:30 -0500 Subject: [PATCH 4/5] fix: Manual rebase export --- .../Public/Export-M365SecurityAuditTable.ps1 | 352 ++++++++++-------- 1 file changed, 190 insertions(+), 162 deletions(-) diff --git a/source/Public/Export-M365SecurityAuditTable.ps1 b/source/Public/Export-M365SecurityAuditTable.ps1 index 29bd75c..1943c0d 100644 --- a/source/Public/Export-M365SecurityAuditTable.ps1 +++ b/source/Public/Export-M365SecurityAuditTable.ps1 @@ -1,47 +1,52 @@ <# .SYNOPSIS - Exports M365 security audit results to a CSV file or outputs a specific test result as an object. + Exports Microsoft 365 security audit results to CSV or Excel files and supports outputting specific test results as objects. .DESCRIPTION - This function exports M365 security audit results from either an array of CISAuditResult objects or a CSV file. - It can export all results to a specified path or output a specific test result as an object. + The Export-M365SecurityAuditTable function exports Microsoft 365 security audit results from an array of CISAuditResult objects or a CSV file. + It can export all results to a specified path, output a specific test result as an object, and includes options for exporting results to Excel. + Additionally, it computes hashes for the exported files and includes them in the zip archive for verification purposes. .PARAMETER AuditResults - An array of CISAuditResult objects containing the audit results. + An array of CISAuditResult objects containing the audit results. This parameter is mandatory when exporting from audit results. .PARAMETER CsvPath - The path to a CSV file containing the audit results. + The path to a CSV file containing the audit results. This parameter is mandatory when exporting from a CSV file. .PARAMETER OutputTestNumber - The test number to output as an object. Valid values are "1.1.1", "1.3.1", "6.1.2", "6.1.3", "7.3.4". + The test number to output as an object. Valid values are "1.1.1", "1.3.1", "6.1.2", "6.1.3", "7.3.4". This parameter is used to output a specific test result. .PARAMETER ExportAllTests - Switch to export all test results. + Switch to export all test results. When specified, all test results are exported to the specified path. .PARAMETER ExportPath - The path where the CSV files will be exported. + The path where the CSV or Excel files will be exported. This parameter is mandatory when exporting all tests. .PARAMETER ExportOriginalTests - Switch to export the original audit results to a CSV file. + Switch to export the original audit results to a CSV file. When specified, the original test results are exported along with the processed results. .PARAMETER ExportToExcel - Switch to export the results to an Excel file. + Switch to export the results to an Excel file. When specified, results are exported in Excel format. .INPUTS - [CISAuditResult[]], [string] + [CISAuditResult[]] - An array of CISAuditResult objects. + [string] - A path to a CSV file. .OUTPUTS - [PSCustomObject] + [PSCustomObject] - A custom object containing the path to the zip file and its hash. .EXAMPLE - Export-M365SecurityAuditTable -AuditResults $object -OutputTestNumber 6.1.2 - # Output object for a single test number from audit results + Export-M365SecurityAuditTable -AuditResults $object -OutputTestNumber 6.1.2 + # Outputs the result of test number 6.1.2 from the provided audit results as an object. .EXAMPLE - Export-M365SecurityAuditTable -ExportAllTests -AuditResults $object -ExportPath "C:\temp" - # Export all results from audit results to the specified path + Export-M365SecurityAuditTable -ExportAllTests -AuditResults $object -ExportPath "C:\temp" + # Exports all audit results to the specified path in CSV format. .EXAMPLE - Export-M365SecurityAuditTable -CsvPath "C:\temp\auditresultstoday1.csv" -OutputTestNumber 6.1.2 - # Output object for a single test number from CSV + Export-M365SecurityAuditTable -CsvPath "C:\temp\auditresultstoday1.csv" -OutputTestNumber 6.1.2 + # Outputs the result of test number 6.1.2 from the CSV file as an object. .EXAMPLE - Export-M365SecurityAuditTable -ExportAllTests -CsvPath "C:\temp\auditresultstoday1.csv" -ExportPath "C:\temp" - # Export all results from CSV to the specified path + Export-M365SecurityAuditTable -ExportAllTests -CsvPath "C:\temp\auditresultstoday1.csv" -ExportPath "C:\temp" + # Exports all audit results from the CSV file to the specified path in CSV format. .EXAMPLE - Export-M365SecurityAuditTable -ExportAllTests -AuditResults $object -ExportPath "C:\temp" -ExportOriginalTests - # Export all results from audit results to the specified path along with the original tests + Export-M365SecurityAuditTable -ExportAllTests -AuditResults $object -ExportPath "C:\temp" -ExportOriginalTests + # Exports all audit results along with the original test results to the specified path in CSV format. .EXAMPLE - Export-M365SecurityAuditTable -ExportAllTests -CsvPath "C:\temp\auditresultstoday1.csv" -ExportPath "C:\temp" -ExportOriginalTests - # Export all results from CSV to the specified path along with the original tests + Export-M365SecurityAuditTable -ExportAllTests -CsvPath "C:\temp\auditresultstoday1.csv" -ExportPath "C:\temp" -ExportOriginalTests + # Exports all audit results from the CSV file along with the original test results to the specified path in CSV format. + .EXAMPLE + Export-M365SecurityAuditTable -ExportAllTests -AuditResults $object -ExportPath "C:\temp" -ExportToExcel + # Exports all audit results to the specified path in Excel format. .LINK - https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Export-M365SecurityAuditTable + https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Export-M365SecurityAuditTable #> function Export-M365SecurityAuditTable { [CmdletBinding()] @@ -50,179 +55,202 @@ function Export-M365SecurityAuditTable { [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "ExportAllResultsFromAuditResults")] [Parameter(Mandatory = $true, Position = 2, ParameterSetName = "OutputObjectFromAuditResultsSingle")] [CISAuditResult[]]$AuditResults, - [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "ExportAllResultsFromCsv")] [Parameter(Mandatory = $true, Position = 2, ParameterSetName = "OutputObjectFromCsvSingle")] [ValidateScript({ (Test-Path $_) -and ((Get-Item $_).PSIsContainer -eq $false) })] [string]$CsvPath, - [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "OutputObjectFromAuditResultsSingle")] [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "OutputObjectFromCsvSingle")] [ValidateSet("1.1.1", "1.3.1", "6.1.2", "6.1.3", "7.3.4")] [string]$OutputTestNumber, - [Parameter(Mandatory = $false, Position = 0, ParameterSetName = "ExportAllResultsFromAuditResults")] [Parameter(Mandatory = $false, Position = 0, ParameterSetName = "ExportAllResultsFromCsv")] [switch]$ExportAllTests, - [Parameter(Mandatory = $true, ParameterSetName = "ExportAllResultsFromAuditResults")] [Parameter(Mandatory = $true, ParameterSetName = "ExportAllResultsFromCsv")] [string]$ExportPath, - [Parameter(Mandatory = $true, ParameterSetName = "ExportAllResultsFromAuditResults")] [Parameter(Mandatory = $true, ParameterSetName = "ExportAllResultsFromCsv")] [switch]$ExportOriginalTests, - [Parameter(Mandatory = $false, ParameterSetName = "ExportAllResultsFromAuditResults")] [Parameter(Mandatory = $false, ParameterSetName = "ExportAllResultsFromCsv")] [switch]$ExportToExcel ) - if ($ExportToExcel) { - Assert-ModuleAvailability -ModuleName ImportExcel -RequiredVersion "7.8.9" - } - if ($PSCmdlet.ParameterSetName -like "ExportAllResultsFromCsv" -or $PSCmdlet.ParameterSetName -eq "OutputObjectFromCsvSingle") { - $AuditResults = Import-Csv -Path $CsvPath | ForEach-Object { - $params = @{ - Rec = $_.Rec - Result = [bool]$_.Result - Status = $_.Status - Details = $_.Details - FailureReason = $_.FailureReason + Begin { + $createdFiles = @() # Initialize an array to keep track of created files + if ($ExportToExcel) { + Assert-ModuleAvailability -ModuleName ImportExcel -RequiredVersion "7.8.9" + } + if ($PSCmdlet.ParameterSetName -like "ExportAllResultsFromCsv" -or $PSCmdlet.ParameterSetName -eq "OutputObjectFromCsvSingle") { + $AuditResults = Import-Csv -Path $CsvPath | ForEach-Object { + $params = @{ + Rec = $_.Rec + Result = [bool]$_.Result + Status = $_.Status + Details = $_.Details + FailureReason = $_.FailureReason + } + Initialize-CISAuditResult @params } - Initialize-CISAuditResult @params } - } - - if ($ExportAllTests) { - $TestNumbers = "1.1.1", "1.3.1", "6.1.2", "6.1.3", "7.3.4" - } - - $results = @() - - $testsToProcess = if ($OutputTestNumber) { @($OutputTestNumber) } else { $TestNumbers } - - foreach ($test in $testsToProcess) { - $auditResult = $AuditResults | Where-Object { $_.Rec -eq $test } - if (-not $auditResult) { - Write-Information "No audit results found for the test number $test." - continue + if ($ExportAllTests) { + $TestNumbers = "1.1.1", "1.3.1", "6.1.2", "6.1.3", "7.3.4" } - - switch ($test) { - "6.1.2" { - $details = $auditResult.Details - if ($details -ne "No M365 E3 licenses found.") { - $csv = $details | ConvertFrom-Csv -Delimiter '|' - } - else { - $csv = $null - } - - if ($null -ne $csv) { - foreach ($row in $csv) { - $row.AdminActionsMissing = (Get-Action -AbbreviatedActions $row.AdminActionsMissing.Split(',') -ReverseActionType Admin | Where-Object { $_ -notin @("MailItemsAccessed", "Send") }) -join ',' - $row.DelegateActionsMissing = (Get-Action -AbbreviatedActions $row.DelegateActionsMissing.Split(',') -ReverseActionType Delegate | Where-Object { $_ -notin @("MailItemsAccessed") }) -join ',' - $row.OwnerActionsMissing = (Get-Action -AbbreviatedActions $row.OwnerActionsMissing.Split(',') -ReverseActionType Owner | Where-Object { $_ -notin @("MailItemsAccessed", "Send") }) -join ',' + $results = @() + $testsToProcess = if ($OutputTestNumber) { @($OutputTestNumber) } else { $TestNumbers } + } + Process { + foreach ($test in $testsToProcess) { + $auditResult = $AuditResults | Where-Object { $_.Rec -eq $test } + if (-not $auditResult) { + Write-Information "No audit results found for the test number $test." + continue + } + switch ($test) { + "6.1.2" { + $details = $auditResult.Details + if ($details -ne "No M365 E3 licenses found.") { + $csv = $details | ConvertFrom-Csv -Delimiter '|' } - $newObjectDetails = $csv - } - else { - $newObjectDetails = $details - } - $results += [PSCustomObject]@{ TestNumber = $test; Details = $newObjectDetails } - } - "6.1.3" { - $details = $auditResult.Details - if ($details -ne "No M365 E5 licenses found.") { - $csv = $details | ConvertFrom-Csv -Delimiter '|' - } - else { - $csv = $null - } - - if ($null -ne $csv) { - foreach ($row in $csv) { - $row.AdminActionsMissing = (Get-Action -AbbreviatedActions $row.AdminActionsMissing.Split(',') -ReverseActionType Admin) -join ',' - $row.DelegateActionsMissing = (Get-Action -AbbreviatedActions $row.DelegateActionsMissing.Split(',') -ReverseActionType Delegate) -join ',' - $row.OwnerActionsMissing = (Get-Action -AbbreviatedActions $row.OwnerActionsMissing.Split(',') -ReverseActionType Owner) -join ',' + else { + $csv = $null } - $newObjectDetails = $csv - } - else { - $newObjectDetails = $details - } - $results += [PSCustomObject]@{ TestNumber = $test; Details = $newObjectDetails } - } - Default { - $details = $auditResult.Details - $csv = $details | ConvertFrom-Csv -Delimiter '|' - $results += [PSCustomObject]@{ TestNumber = $test; Details = $csv } - } - } - } - - if ($ExportPath) { - $timestamp = (Get-Date).ToString("yyyy.MM.dd_HH.mm.ss") - $exportedTests = @() - - foreach ($result in $results) { - $testDef = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $result.TestNumber } - if ($testDef) { - $fileName = "$ExportPath\$($timestamp)_$($result.TestNumber).$($testDef.TestFileName -replace '\.ps1$').csv" - if ($result.Details.Count -eq 0) { - Write-Information "No results found for test number $($result.TestNumber)." -InformationAction Continue - } - else { - if (($result.Details -ne "No M365 E3 licenses found.") -and ($result.Details -ne "No M365 E5 licenses found.")) { - if ($ExportToExcel) { - $xlsxPath = [System.IO.Path]::ChangeExtension($fileName, '.xlsx') - $result.Details | Export-Excel -Path $xlsxPath -WorksheetName Table -TableName Table -AutoSize -TableStyle Medium2 + if ($null -ne $csv) { + foreach ($row in $csv) { + $row.AdminActionsMissing = (Get-Action -AbbreviatedActions $row.AdminActionsMissing.Split(',') -ReverseActionType Admin | Where-Object { $_ -notin @("MailItemsAccessed", "Send") }) -join ',' + $row.DelegateActionsMissing = (Get-Action -AbbreviatedActions $row.DelegateActionsMissing.Split(',') -ReverseActionType Delegate | Where-Object { $_ -notin @("MailItemsAccessed") }) -join ',' + $row.OwnerActionsMissing = (Get-Action -AbbreviatedActions $row.OwnerActionsMissing.Split(',') -ReverseActionType Owner | Where-Object { $_ -notin @("MailItemsAccessed", "Send") }) -join ',' } - else { - $result.Details | Export-Csv -Path $fileName -NoTypeInformation - } - $exportedTests += $result.TestNumber + $newObjectDetails = $csv } + else { + $newObjectDetails = $details + } + $results += [PSCustomObject]@{ TestNumber = $test; Details = $newObjectDetails } + } + "6.1.3" { + $details = $auditResult.Details + if ($details -ne "No M365 E5 licenses found.") { + $csv = $details | ConvertFrom-Csv -Delimiter '|' + } + else { + $csv = $null + } + if ($null -ne $csv) { + foreach ($row in $csv) { + $row.AdminActionsMissing = (Get-Action -AbbreviatedActions $row.AdminActionsMissing.Split(',') -ReverseActionType Admin) -join ',' + $row.DelegateActionsMissing = (Get-Action -AbbreviatedActions $row.DelegateActionsMissing.Split(',') -ReverseActionType Delegate) -join ',' + $row.OwnerActionsMissing = (Get-Action -AbbreviatedActions $row.OwnerActionsMissing.Split(',') -ReverseActionType Owner) -join ',' + } + $newObjectDetails = $csv + } + else { + $newObjectDetails = $details + } + $results += [PSCustomObject]@{ TestNumber = $test; Details = $newObjectDetails } + } + Default { + $details = $auditResult.Details + $csv = $details | ConvertFrom-Csv -Delimiter '|' + $results += [PSCustomObject]@{ TestNumber = $test; Details = $csv } } } } - if ($exportedTests.Count -gt 0) { - Write-Information "The following tests were exported: $($exportedTests -join ', ')" -InformationAction Continue - } - else { + } + End { + if ($ExportPath) { + $timestamp = (Get-Date).ToString("yyyy.MM.dd_HH.mm.ss") + $exportedTests = @() + foreach ($result in $results) { + $testDef = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $result.TestNumber } + if ($testDef) { + $fileName = "$ExportPath\$($timestamp)_$($result.TestNumber).$($testDef.TestFileName -replace '\.ps1$').csv" + if ($result.Details.Count -eq 0) { + Write-Information "No results found for test number $($result.TestNumber)." -InformationAction Continue + } + else { + if (($result.Details -ne "No M365 E3 licenses found.") -and ($result.Details -ne "No M365 E5 licenses found.")) { + if ($ExportToExcel) { + $xlsxPath = [System.IO.Path]::ChangeExtension($fileName, '.xlsx') + $result.Details | Export-Excel -Path $xlsxPath -WorksheetName Table -TableName Table -AutoSize -TableStyle Medium2 + $createdFiles += $xlsxPath # Add the created file to the array + } + else { + $result.Details | Export-Csv -Path $fileName -NoTypeInformation + $createdFiles += $fileName # Add the created file to the array + } + $exportedTests += $result.TestNumber + } + } + } + } + if ($exportedTests.Count -gt 0) { + Write-Information "The following tests were exported: $($exportedTests -join ', ')" -InformationAction Continue + } + else { + if ($ExportOriginalTests) { + Write-Information "Full audit results exported however, none of the following tests had exports: `n1.1.1, 1.3.1, 6.1.2, 6.1.3, 7.3.4" -InformationAction Continue + } + else { + Write-Information "No specified tests were included in the export." -InformationAction Continue + } + } if ($ExportOriginalTests) { - Write-Information "Full audit results exported however, none of the following tests had exports: `n1.1.1, 1.3.1, 6.1.2, 6.1.3, 7.3.4" -InformationAction Continue + # Define the test numbers to check + $TestNumbersToCheck = "1.1.1", "1.3.1", "6.1.2", "6.1.3", "7.3.4" + # Check for large details and update the AuditResults array + $updatedAuditResults = Get-ExceededLengthResultDetail -AuditResults $AuditResults -TestNumbersToCheck $TestNumbersToCheck -ExportedTests $exportedTests -DetailsLengthLimit 30000 -PreviewLineCount 25 + $originalFileName = "$ExportPath\$timestamp`_M365FoundationsAudit.csv" + if ($ExportToExcel) { + $xlsxPath = [System.IO.Path]::ChangeExtension($originalFileName, '.xlsx') + $updatedAuditResults | Export-Excel -Path $xlsxPath -WorksheetName Table -TableName Table -AutoSize -TableStyle Medium2 + $createdFiles += $xlsxPath # Add the created file to the array + } + else { + $updatedAuditResults | Export-Csv -Path $originalFileName -NoTypeInformation + $createdFiles += $originalFileName # Add the created file to the array + } + } + # Hash each file and add it to a dictionary + # Hash each file and save the hashes to a text file + $hashFilePath = "$ExportPath\$timestamp`_Hashes.txt" + $fileHashes = @() + foreach ($file in $createdFiles) { + $hash = Get-FileHash -Path $file -Algorithm SHA256 + $fileHashes += "$($file): $($hash.Hash)" + } + $fileHashes | Set-Content -Path $hashFilePath + $createdFiles += $hashFilePath # Add the hash file to the array + # Create a zip file and add all the created files + $zipFilePath = "$ExportPath\$timestamp`_M365FoundationsAudit.zip" + Compress-Archive -Path $createdFiles -DestinationPath $zipFilePath + # Remove the original files after they have been added to the zip + foreach ($file in $createdFiles) { + Remove-Item -Path $file -Force + } + # Compute the hash for the zip file and rename it + $zipHash = Get-FileHash -Path $zipFilePath -Algorithm SHA256 + $newZipFilePath = "$ExportPath\$timestamp`_M365FoundationsAudit_$($zipHash.Hash.Substring(0, 8)).zip" + Rename-Item -Path $zipFilePath -NewName $newZipFilePath + # Output the zip file path with hash + [PSCustomObject]@{ + ZipFilePath = $newZipFilePath + } + } # End of ExportPath + elseif ($OutputTestNumber) { + if ($results[0].Details) { + return $results[0].Details } else { - Write-Information "No specified tests were included in the export." -InformationAction Continue + Write-Information "No results found for test number $($OutputTestNumber)." -InformationAction Continue } } - - if ($ExportOriginalTests) { - # Define the test numbers to check - $TestNumbersToCheck = "1.1.1", "1.3.1", "6.1.2", "6.1.3", "7.3.4" - - # Check for large details and update the AuditResults array - $updatedAuditResults = Get-ExceededLengthResultDetail -AuditResults $AuditResults -TestNumbersToCheck $TestNumbersToCheck -ExportedTests $exportedTests -DetailsLengthLimit 30000 -PreviewLineCount 25 - $originalFileName = "$ExportPath\$timestamp`_M365FoundationsAudit.csv" - if ($ExportToExcel) { - $xlsxPath = [System.IO.Path]::ChangeExtension($originalFileName, '.xlsx') - $updatedAuditResults | Export-Excel -Path $xlsxPath -WorksheetName Table -TableName Table -AutoSize -TableStyle Medium2 - } - else { - $updatedAuditResults | Export-Csv -Path $originalFileName -NoTypeInformation - } - } - } - elseif ($OutputTestNumber) { - if ($results[0].Details) { - return $results[0].Details - } else { - Write-Information "No results found for test number $($OutputTestNumber)." -InformationAction Continue + Write-Error "No valid operation specified. Please provide valid parameters." } + # Output the created files at the end + #if ($createdFiles.Count -gt 0) { + ########### $createdFiles + #} } - else { - Write-Error "No valid operation specified. Please provide valid parameters." - } -} +} \ No newline at end of file From d68ae22982e9452661be7c3197a19c76fce27b03 Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Tue, 2 Jul 2024 10:02:55 -0500 Subject: [PATCH 5/5] docs: Update CHANGELOG --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1baca84..fff4627 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ The format is based on and uses the types of changes according to [Keep a Change ## [Unreleased] +# Fixed + +- SPO tests formatting and output. + +## [0.1.22] - 2024-07-01 + ### Added - Added hash and compress steps to `Export-M365SecurityAuditTable` function.