diff --git a/PowerShell/ScubaGear/Modules/CreateReport/CreateReport.psm1 b/PowerShell/ScubaGear/Modules/CreateReport/CreateReport.psm1 index 16baac85d5..4b6ed47e25 100644 --- a/PowerShell/ScubaGear/Modules/CreateReport/CreateReport.psm1 +++ b/PowerShell/ScubaGear/Modules/CreateReport/CreateReport.psm1 @@ -88,6 +88,7 @@ function New-Report { "Warnings" = 0; "Failures" = 0; "Passes" = 0; + "Omits" = 0; "Manual" = 0; "Errors" = 0; "Date" = $SettingsExport.date; @@ -101,6 +102,29 @@ function New-Report { $Test = $TestResults | Where-Object -Property PolicyId -eq $Control.Id if ($null -ne $Test){ + # Check if the config file indicates the control should be omitted + $Config = $SettingsExport.scuba_config + $Omit = Get-OmissionState $Config $Control.Id + if ($Omit) { + $ReportSummary.Omits += 1 + $OmitRationale = $Config.OmitPolicy.$($Control.Id).Rationale + if ([string]::IsNullOrEmpty($OmitRationale)) { + Write-Warning "Config file indicates omitting $($Control.Id), but no rationale provided." + $OmitRationale = "Rationale not provided." + } + else { + $OmitRationale = "`"$($OmitRationale)`"" + } + $Fragment += [pscustomobject]@{ + "Control ID"=$Control.Id + "Requirement"=$Control.Value + "Result"= "Omitted" + "Criticality"= $Test.Criticality + "Details"= "Test omitted by user. $($OmitRationale)" + } + continue + } + $MissingCommands = $Test.Commandlet | Where-Object {$SettingsExport."$($BaselineName)_successful_commands" -notcontains $_} if ($MissingCommands.Count -gt 0) { @@ -277,6 +301,72 @@ function New-Report { $ReportSummary } +function Get-OmissionState { + <# + .Description + Determine if the supplied control was marked for omission in the config file. + .Functionality + Internal + #> + [CmdletBinding()] + param ( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [PSCustomObject] + $Config, + + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $ControlId + ) + $Omit = $false + if ($Config.psobject.properties.name -Contains "OmitPolicy") { + if ($Config.OmitPolicy.psobject.properties.name -Contains $ControlId) { + # The config indicates the control should be omitted + if ($Config.OmitPolicy.$($ControlId).psobject.properties.name -Contains "Expiration") { + # An expiration date for the omission expiration was provided. Evaluate the date + # to see if the control should still be omitted. + if ($Config.OmitPolicy.$($ControlId).Expiration -eq "") { + # If the Expiration date is an empty string, omit the policy + $Omit = $true + } + else { + # An expiration date was provided and it's not an empty string + $Now = Get-Date + $ExpirationString = $Config.OmitPolicy.$($ControlId).Expiration + try { + $ExpirationDate = Get-Date -Date $ExpirationString + if ($ExpirationDate -lt $Now) { + # The expiration date is passed, don't omit the policy + $Warning = "Config file indicates omitting $($ControlId), but the provided " + $Warning += "expiration date, $ExpirationString, has passed. Control will " + $Warning += "not be omitted." + Write-Warning $Warning + } + else { + # The expiration date is in the future, omit the policy + $Omit = $true + } + } + catch { + # Malformed date, don't omit the policy + $Warning = "Config file indicates omitting $($ControlId), but the provided " + $Warning += "expiration date, $ExpirationString, is malformed. The expected " + $Warning += "format is yyyy-mm-dd. Control will not be omitted." + Write-Warning $Warning + } + } + } + else { + # The expiration date was not provided, omit the policy + $Omit = $true + } + } + } + $Omit +} + function Import-SecureBaseline{ <# .Description diff --git a/PowerShell/ScubaGear/Modules/CreateReport/scripts/main.js b/PowerShell/ScubaGear/Modules/CreateReport/scripts/main.js index b0171af273..e3131e24dd 100644 --- a/PowerShell/ScubaGear/Modules/CreateReport/scripts/main.js +++ b/PowerShell/ScubaGear/Modules/CreateReport/scripts/main.js @@ -25,6 +25,9 @@ const colorRows = () => { else if (rows[i].children[statusCol].innerHTML === "Pass") { rows[i].style.background = "var(--test-pass)"; } + else if (rows[i].children[statusCol].innerHTML === "Omitted") { + rows[i].style.background = "var(--test-other)"; + } else if (rows[i].children[criticalityCol].innerHTML.includes("Not-Implemented")) { rows[i].style.background = "var(--test-other)"; } diff --git a/PowerShell/ScubaGear/Modules/CreateReport/styles/ParentReportStyle.css b/PowerShell/ScubaGear/Modules/CreateReport/styles/ParentReportStyle.css index b8f03feaab..bfbcc56cd1 100644 --- a/PowerShell/ScubaGear/Modules/CreateReport/styles/ParentReportStyle.css +++ b/PowerShell/ScubaGear/Modules/CreateReport/styles/ParentReportStyle.css @@ -15,7 +15,7 @@ footer { display: inline-block; padding: 0.313em; border-radius: 0.313em; - min-width: 140px; + min-width: 100px; text-align: center; } diff --git a/PowerShell/ScubaGear/Modules/Orchestrator.psm1 b/PowerShell/ScubaGear/Modules/Orchestrator.psm1 index 8ef74da93d..9aef9a352b 100644 --- a/PowerShell/ScubaGear/Modules/Orchestrator.psm1 +++ b/PowerShell/ScubaGear/Modules/Orchestrator.psm1 @@ -1005,13 +1005,18 @@ function Invoke-ReportCreation { $LinkPath = "$($IndividualReportFolderName)/$($BaselineName)Report.html" $LinkClassName = '"individual_reports"' # uses no escape characters $Link = "$($FullName)" - - $PassesSummary = "
$($Report.Passes) tests passed
" + $PassesSummary = "
" $WarningsSummary = "
" $FailuresSummary = "
" $BaselineURL = "

Baseline Documents

" $ManualSummary = "
" - $ErrorSummary = "
" + $OmitSummary = "
" + $ErrorSummary = "" + + if ($Report.Passes -gt 0) { + $Noun = Pluralize -SingularNoun "pass" -PluralNoun "passes" -Count $Report.Passes + $PassesSummary = "
$($Report.Passes) $($Noun)
" + } if ($Report.Warnings -gt 0) { $Noun = Pluralize -SingularNoun "warning" -PluralNoun "warnings" -Count $Report.Warnings @@ -1019,13 +1024,17 @@ function Invoke-ReportCreation { } if ($Report.Failures -gt 0) { - $Noun = Pluralize -SingularNoun "test" -PluralNoun "tests" -Count $Report.Failures - $FailuresSummary = "
$($Report.Failures) $($Noun) failed
" + $Noun = Pluralize -SingularNoun "failure" -PluralNoun "failures" -Count $Report.Failures + $FailuresSummary = "
$($Report.Failures) $($Noun)
" } if ($Report.Manual -gt 0) { $Noun = Pluralize -SingularNoun "check" -PluralNoun "checks" -Count $Report.Manual - $ManualSummary = "
$($Report.Manual) manual $($Noun) needed
" + $ManualSummary = "
$($Report.Manual) manual $($Noun)
" + } + + if ($Report.Omits -gt 0) { + $OmitSummary = "
$($Report.Omits) omitted
" } if ($Report.Errors -gt 0) { @@ -1035,7 +1044,7 @@ function Invoke-ReportCreation { $Fragment += [pscustomobject]@{ "Baseline Conformance Reports" = $Link; - "Details" = "$($PassesSummary) $($WarningsSummary) $($FailuresSummary) $($ManualSummary) $($ErrorSummary)" + "Details" = "$($PassesSummary) $($WarningsSummary) $($FailuresSummary) $($ManualSummary) $($OmitSummary) $($ErrorSummary)" } } $TenantMetaData += [pscustomobject]@{ diff --git a/PowerShell/ScubaGear/Modules/ScubaConfig/ScubaConfig.psm1 b/PowerShell/ScubaGear/Modules/ScubaConfig/ScubaConfig.psm1 index 32f17d34d2..0d5b5904b3 100644 --- a/PowerShell/ScubaGear/Modules/ScubaConfig/ScubaConfig.psm1 +++ b/PowerShell/ScubaGear/Modules/ScubaConfig/ScubaConfig.psm1 @@ -56,6 +56,29 @@ class ScubaConfig { $this.SetParameterDefaults() [ScubaConfig]::_IsLoaded = $true + # If OmitPolicy was included in the config file, validate the policy IDs included there. + if ($this.Configuration.ContainsKey("OmitPolicy")) { + foreach ($Policy in $this.Configuration.OmitPolicy.Keys) { + if (-not ($Policy -match "^ms\.[a-z]+\.[0-9]+\.[0-9]+v[0-9]+$")) { + # Note that -match is a case insensitive match + # Note that the regex does not validate the product name, this will be done later + $Warning = "Config file indicates omitting $Policy, but $Policy is not a valid control ID. " + $Warning += "Expected format is 'MS.[PRODUCT].[GROUP].[NUMBER]v[VERSION]', " + $Warning += "e.g., 'MS.DEFENDER.1.1v1'. Control will not be omitted." + Write-Warning $Warning + Continue + } + $Product = ($Policy -Split "\.")[1] + # Here's where the product name is validated + if (-not ($this.Configuration.ProductNames -Contains $Product)) { + $Warning = "Config file indicates omitting $Policy, but $Product is not one of the products " + $Warning += "specified in the ProductNames parameter. Control will not be omitted." + Write-Warning $Warning + Continue + } + } + } + return [ScubaConfig]::_IsLoaded } diff --git a/PowerShell/ScubaGear/Modules/Support/Support.psm1 b/PowerShell/ScubaGear/Modules/Support/Support.psm1 index 0a3b21b6c1..a5b4fa7c25 100644 --- a/PowerShell/ScubaGear/Modules/Support/Support.psm1 +++ b/PowerShell/ScubaGear/Modules/Support/Support.psm1 @@ -728,6 +728,9 @@ function New-Config { parameters. Additional parameters and variables not available on the command line can also be included in the file that will be provided to the tool for use in specific tests. + .Parameter OmitPolicy + A comma-separated list of policies to exclude from the ScubaGear report, e.g., MS.DEFENDER.1.1v1. + Note that the rationales will need to be manually added to the resulting config file. .Functionality Public #> @@ -812,7 +815,12 @@ function New-Config { [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string] - $ConfigLocation = "./" + $ConfigLocation = "./", + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [string[]] + $OmitPolicy = @() ) $Config = New-Object ([System.Collections.specialized.OrderedDictionary]) @@ -825,6 +833,12 @@ function New-Config { } } + if ($config.Contains("OmitPolicy")) { + # We don't want to immediately save this parameter to the config, as it's not in the right + # format yet. + $config.Remove("OmitPolicy") + } + $CapExclusionNamespace = @( "MS.AAD.1.1v1", "MS.AAD.2.1v1", @@ -849,6 +863,46 @@ function New-Config { $PartnerDomainImpersonationProtectionNamespace = "MS.DEFENDER.2.3v1" + $OmissionNamespace = "OmitPolicy" + + # List to track which policies the user specified in $OmitPolicies are properly formatted + $OmitPolicyValidated = @() + + # Hashmap to structure the ignored policies template + $config[$OmissionNamespace] = @{} + + foreach ($Policy in $OmitPolicy) { + if (-not ($Policy -match "^ms\.[a-z]+\.[0-9]+\.[0-9]+v[0-9]+$")) { + # Note that -match is a case insensitive match + # Note that the regex does not validate the product name, this will be done + # later + $Warning = "The policy, $Policy, in the OmitPolicy parameter, is not a valid " + $Warning += "policy ID. Expected format 'MS.[PRODUCT].[GROUP].[NUMBER]v[VERSION]', " + $Warning += "e.g., 'MS.DEFENDER.1.1v1'. Skipping." + Write-Warning $Warning + Continue + } + $Product = ($Policy -Split "\.")[1] + # Here's where the product name is validated + if (-not ($ProductNames -Contains $Product)) { + $Warning = "The policy, $Policy, in the OmitPolicy parameter, is not encompassed by " + $Warning += "the products specified in the ProductName parameter. Skipping." + Write-Warning $Warning + Continue + } + # Ensure the policy ID is properly capitalized (i.e., all caps except for the "v1" portion) + $PolicyCapitalized = $Policy.Substring(0, $Policy.Length-2).ToUpper() + $Policy.SubString($Policy.Length-2) + $OmitPolicyValidated += $PolicyCapitalized + $config[$OmissionNamespace][$PolicyCapitalized] = @{ + "Rationale" = ""; + "Expiration" = ""; + } + } + + $Warning = "The following policies have been configured for omission: $($OmitPolicyValidated -Join ', '). " + $Warning += "Note that as the New-Config function does not support providing the rationale for omission via " + $Warning += "the commandline, you will need to open the resulting config file and manually enter the rationales." + Write-Warning $Warning $AadTemplate = New-Object ([System.Collections.specialized.OrderedDictionary]) $AadCapExclusions = New-Object ([System.Collections.specialized.OrderedDictionary]) diff --git a/PowerShell/ScubaGear/Sample-Config-Files/omit_policies.yaml b/PowerShell/ScubaGear/Sample-Config-Files/omit_policies.yaml new file mode 100644 index 0000000000..967c790fbe --- /dev/null +++ b/PowerShell/ScubaGear/Sample-Config-Files/omit_policies.yaml @@ -0,0 +1,29 @@ +Description: | + SCuBAGear YAML Configuration file with custom variables + This configuration shows a standard SCuBAGear set of parameters to run + but also includes examples of configuring specific policies to be omitted + from the ScubaGear output. Any omitted policies should be carefully considered + and documented as part of an organization's cybersecurity risk management + program process and practices. +ProductNames: + - exo + - teams +M365Environment: commercial +OPAPath: . +LogIn: true +DisconnectOnExit: false +OutPath: . +OutFolderName: M365BaselineConformance +OutProviderFileName: ProviderSettingsExport +OutRegoFileName: TestResults +OutReportName: BaselineReports +OmitPolicy: + MS.EXO.2.2v1: + Rationale: "Known false positive; our SPF policy currently cannot to be retrieved via ScubaGear due to a split + horizon setup but is available publicly." + Expiration: "2023-12-31" + MS.TEAMS.6.1v1: + Rationale: &DLPRationale "The DLP capability required for Teams is implemented by third party product, [x], + which ScubaGear does not have the ability to check." + MS.TEAMS.6.2v1: + Rationale: *DLPRationale diff --git a/PowerShell/ScubaGear/Testing/Unit/PowerShell/CreateReport/Get-OmissionState.Tests.ps1 b/PowerShell/ScubaGear/Testing/Unit/PowerShell/CreateReport/Get-OmissionState.Tests.ps1 new file mode 100644 index 0000000000..48dfc75bec --- /dev/null +++ b/PowerShell/ScubaGear/Testing/Unit/PowerShell/CreateReport/Get-OmissionState.Tests.ps1 @@ -0,0 +1,116 @@ +Import-Module (Join-Path -Path $PSScriptRoot -ChildPath '../../../../Modules/CreateReport') + +InModuleScope CreateReport { + Describe -Tag CreateReport -Name 'Get-OmissionState' { + BeforeAll { + Mock -CommandName Write-Warning {} + } + + Context "When no expiration date is provided" { + It 'Returns false if the policy ID is not in the config' { + # If the policy is not in the config, the expriation date is N/A and the function + # should mark the policy as not omitted. + $Config = [PSCustomObject]@{ + "OmitPolicy" = [PSCustomObject]@{ + "MS.EXO.1.1v1" = [PSCustomObject]@{ "Rationale" = "Example rationale" } + } + } + $Result = Get-OmissionState $Config "MS.DEFENDER.1.1v1" + $Result | Should -Be $false + Should -Invoke -CommandName Write-Warning -Exactly -Times 0 + } + + It 'Returns true if the policy ID is in the config' { + # If the policy is in the config and the expriation date is not provided, as the + # expiration date is optional, the function should mark the policy as omitted. + $Config = [PSCustomObject]@{ + "OmitPolicy" = [PSCustomObject]@{ + "MS.DEFENDER.1.1v1" = [PSCustomObject]@{ "Rationale" = "Example rationale" } + } + } + $Result = Get-OmissionState $Config "MS.DEFENDER.1.1v1" + $Result | Should -Be $true + Should -Invoke -CommandName Write-Warning -Exactly -Times 0 + } + } + + Context "When an expiration date is provided" { + BeforeAll { + Mock -CommandName Get-Date { + # Modify the Get-Date function so that it returns a fixed date when + # no date is provided, instead of the current time. + if ($null -eq $Date) { + Get-Date -Date "2024-01-02" + } + else { + # If a specific date is requested, operate as normal + $Date + } + } + } + + It 'Returns true if the expiration is in the future' { + # If the date is in the future, the function should still mark the + # policy as not omitted. + $Config = [PSCustomObject]@{ + "OmitPolicy" = [PSCustomObject]@{ + "MS.DEFENDER.1.1v1" = [PSCustomObject]@{ + "Rationale" = "Example rationale"; + "Expiration" = "2024-01-03" } + } + } + $Result = Get-OmissionState $Config "MS.DEFENDER.1.1v1" + $Result | Should -Be $true + Should -Invoke -CommandName Write-Warning -Exactly -Times 0 + } + + It 'Returns false and warns if the expiration is in the past' { + # If the date is in the past, the functions should warn the user + # and mark the policy as not omitted. + $Config = [PSCustomObject]@{ + "OmitPolicy" = [PSCustomObject]@{ + "MS.DEFENDER.1.1v1" = [PSCustomObject]@{ + "Rationale" = "Example rationale"; + "Expiration" = "2024-01-01" } + } + } + $Result = Get-OmissionState $Config "MS.DEFENDER.1.1v1" + $Result | Should -Be $false + Should -Invoke -CommandName Write-Warning -Exactly -Times 1 + } + + It 'Returns false and warns if the expiration is malformed' { + # The functions should recognize that the date is malformed, warn the user, + # and mark the policy as not omitted. + $Config = [PSCustomObject]@{ + "OmitPolicy" = [PSCustomObject]@{ + "MS.DEFENDER.1.1v1" = [PSCustomObject]@{ + "Rationale" = "Example rationale"; + "Expiration" = "bad date" } + } + } + $Result = Get-OmissionState $Config "MS.DEFENDER.1.1v1" + $Result | Should -Be $false + Should -Invoke -CommandName Write-Warning -Exactly -Times 1 + } + + It 'Returns false if the ID is malformed' { + # The functions should recognize that the ID is malformed + # and mark the policy as not omitted. + $Config = [PSCustomObject]@{ + "OmitPolicy" = [PSCustomObject]@{ + "MSDEFENDER.1.1v1" = [PSCustomObject]@{ + "Rationale" = "Example rationale"; + } + } + } + $Result = Get-OmissionState $Config "MS.DEFENDER.1.1v1" + $Result | Should -Be $false + } + } + } + + AfterAll { + Remove-Module CreateReport -ErrorAction SilentlyContinue + } +} diff --git a/PowerShell/ScubaGear/Testing/Unit/PowerShell/ScubaConfig/ScubaConfigLoadConfig.Tests.ps1 b/PowerShell/ScubaGear/Testing/Unit/PowerShell/ScubaConfig/ScubaConfigLoadConfig.Tests.ps1 index a2963524aa..0ffb372c69 100644 --- a/PowerShell/ScubaGear/Testing/Unit/PowerShell/ScubaConfig/ScubaConfigLoadConfig.Tests.ps1 +++ b/PowerShell/ScubaGear/Testing/Unit/PowerShell/ScubaConfig/ScubaConfigLoadConfig.Tests.ps1 @@ -2,11 +2,12 @@ using module '..\..\..\..\Modules\ScubaConfig\ScubaConfig.psm1' InModuleScope ScubaConfig { Describe -tag "Utils" -name 'ScubaConfigLoadConfig' { + BeforeAll { + Mock -CommandName Write-Warning {} + function Get-ScubaDefault {throw 'this will be mocked'} + Mock -ModuleName ScubaConfig Get-ScubaDefault {"."} + } context 'Handling repeated LoadConfig invocations' { - BeforeAll { - function Get-ScubaDefault {throw 'this will be mocked'} - Mock -ModuleName ScubaConfig Get-ScubaDefault {"."} - } It 'Load valid config file followed by another'{ $cfg = [ScubaConfig]::GetInstance() # Load the first file and check the ProductNames value. @@ -25,10 +26,45 @@ InModuleScope ScubaConfig { } [ScubaConfig]::GetInstance().LoadConfig($PSCommandPath) | Should -BeTrue $cfg.Configuration.ProductNames | Should -Be 'exo' + Should -Invoke -CommandName Write-Warning -Exactly -Times 0 } AfterAll { [ScubaConfig]::ResetInstance() } } + context "Handling policy omissions" { + It 'Does not warn for proper control IDs' { + function global:ConvertFrom-Yaml { + @{ + ProductNames=@('exo'); + OmitPolicy=@{"MS.EXO.1.1v1"=@{"Rationale"="Example rationale"}} + } + } + [ScubaConfig]::GetInstance().LoadConfig($PSCommandPath) | Should -BeTrue + Should -Invoke -CommandName Write-Warning -Exactly -Times 0 + } + + It 'Warns for malformed control IDs' { + function global:ConvertFrom-Yaml { + @{ + ProductNames=@('exo'); + OmitPolicy=@{"MSEXO.1.1v1"=@{"Rationale"="Example rationale"}} + } + } + [ScubaConfig]::GetInstance().LoadConfig($PSCommandPath) | Should -BeTrue + Should -Invoke -CommandName Write-Warning -Exactly -Times 1 + } + + It 'Warns for control IDs not encompassed by ProductNames' { + function global:ConvertFrom-Yaml { + @{ + ProductNames=@('exo'); + OmitPolicy=@{"MS.Gmail.1.1v1"=@{"Rationale"="Example rationale"}} + } + } + [ScubaConfig]::GetInstance().LoadConfig($PSCommandPath) | Should -BeTrue + Should -Invoke -CommandName Write-Warning -Exactly -Times 1 + } + } } } diff --git a/PowerShell/ScubaGear/Testing/Unit/PowerShell/Support/New-Config.Tests.ps1 b/PowerShell/ScubaGear/Testing/Unit/PowerShell/Support/New-Config.Tests.ps1 index 5e8ce0e7b4..595186eb35 100644 --- a/PowerShell/ScubaGear/Testing/Unit/PowerShell/Support/New-Config.Tests.ps1 +++ b/PowerShell/ScubaGear/Testing/Unit/PowerShell/Support/New-Config.Tests.ps1 @@ -47,6 +47,37 @@ InModuleScope Support { Test-Path -Path "$($TestPath)/SampleConfig.yaml" -PathType leaf | Should -Be $true } + + Context "When policy IDs are provided in the OmitPolicy parameter" { + It 'It reminds users to manually add the rationales' { + # Should warn once to for the reminder to manually add the rationales + $OmitArgs = $CMDArgs + $OmitArgs['OmitPolicy'] = @("MS.DEFENDER.1.1v1", "MS.DEFENDER.1.2v1") + { New-Config @OmitArgs } | Should -Not -Throw + Should -Invoke -CommandName Write-Warning -Exactly -Times 1 + Test-Path -Path "$($TestPath)/SampleConfig.yaml" -PathType leaf | Should -Be $true + } + + It 'Warns for malformed policy IDs' { + # The function should recognize that the policy ID does not match the expected + # format and give an additional warning for this + $OmitArgs = $CMDArgs + $OmitArgs['OmitPolicy'] = @("MS.DEFENDER1.1v1") + { New-Config @OmitArgs } | Should -Not -Throw + Should -Invoke -CommandName Write-Warning -Exactly -Times 2 + Test-Path -Path "$($TestPath)/SampleConfig.yaml" -PathType leaf | Should -Be $true + } + + It 'Warns for unexpected product in the policy ID' { + # The function should recognize that EXAMPLE is not a valid product + # and give an additional warning for this + $OmitArgs = $CMDArgs + $OmitArgs['OmitPolicy'] = @("MS.EXAMPLE.1.1v1", "MS.DEFENDER.1.2v1") + { New-Config @OmitArgs } | Should -Not -Throw + Should -Invoke -CommandName Write-Warning -Exactly -Times 2 + Test-Path -Path "$($TestPath)/SampleConfig.yaml" -PathType leaf | Should -Be $true + } + } } AfterAll { diff --git a/docs/configuration/configuration.md b/docs/configuration/configuration.md index 5c3c1059af..462f6857dd 100644 --- a/docs/configuration/configuration.md +++ b/docs/configuration/configuration.md @@ -90,6 +90,18 @@ ScubaGear's support module can generate an empty sample config file. Running the New-Config ``` +## Omit Policies + +In some cases, it may be appropriate to omit specific policies from ScubaGear evaluation. For example: +- When a policy is implemented by a third-party service that ScubaGear does not audit +- When a policy is not applicable to your organization (e.g., policy MS.EXO.4.3v1 is only applicable to federal, executive branch, departments and agencies) + +The `OmitPolicy` top-level key, shown in this [example ScubaGear configuration file](https://github.com/cisagov/ScubaGear/blob/main/PowerShell/ScubaGear/Sample-Config-Files/omit_policies.yaml), allows the user to specify the policies that should be omitted from the ScubaGear report. Omitted policies will show up as "Omitted" in the HTML report and will be colored gray. Omitting policies must only be done if the omissions are approved within an organization's security risk management process. **Exercise care when omitting policies because this can inadvertently introduce blind spots when assessing your system.** + +For each omitted policy, the config file allows you to indicate the following: +- `Rationale`: The reason the policy should be omitted from the report. This value will be displayed in the "Details" column of the report. ScubaGear will output a warning if no rationale is provided. +- `Expiration`: Optional. A date after which the policy should no longer be omitted from the report. The expected format is yyyy-mm-dd. + ## Product-specific Configuration Config files can include a top-level level key for a given product whose values are related to that specific product. For example, look for the value of `Defender` in this [Defender config file](https://github.com/cisagov/ScubaGear/blob/main/PowerShell/ScubaGear/Sample-Config-Files/defender-config.yaml). Currently, only Entra ID and Defender use this extra configuration.