Core/Set-CiRuleOptions.psm1
Function Set-CiRuleOptions { [CmdletBinding(DefaultParameterSetName = 'All')] [OutputType([System.Void])] param ( [ValidateSet('Base', 'BaseISG', 'BaseKernel', 'Supplemental')] [Parameter(Mandatory = $false, ParameterSetName = 'Template')] [System.String]$Template, [ValidateScript({ Test-CiPolicy -XmlFile $_ })] [Parameter(Mandatory = $true)][System.IO.FileInfo]$FilePath, [ValidateScript({ if ($_ -notin [WDACConfig.RuleOptionsx]::new().GetValidValues()) { throw "Invalid Policy Rule Option: $_" } # Return true if everything is okay $true })] [Parameter(Mandatory = $false)][System.String[]]$RulesToAdd, [ValidateScript({ if ($_ -notin [WDACConfig.RuleOptionsx]::new().GetValidValues()) { throw "Invalid Policy Rule Option: $_" } # Return true if everything is okay $true })] [Parameter(Mandatory = $false)][System.String[]]$RulesToRemove, [Parameter(Mandatory = $false)][System.Boolean]$RequireWHQL, [Parameter(Mandatory = $false)][System.Boolean]$EnableAuditMode, [Parameter(Mandatory = $false)][System.Boolean]$DisableFlightSigning, [Parameter(Mandatory = $false)][System.Boolean]$RequireEVSigners, [Parameter(Mandatory = $false)][System.Boolean]$ScriptEnforcement, [Parameter(Mandatory = $false)][System.Boolean]$TestMode, [Parameter(Mandatory = $false, ParameterSetName = 'RemoveAll')] [System.Management.Automation.SwitchParameter]$RemoveAll ) Begin { [System.Boolean]$Verbose = $PSBoundParameters.Verbose.IsPresent ? $true : $false . "$([WDACConfig.GlobalVars]::ModuleRootPath)\CoreExt\PSDefaultParameterValues.ps1" Import-Module -FullyQualifiedName "$([WDACConfig.GlobalVars]::ModuleRootPath)\XMLOps\Close-EmptyXmlNodes_Semantic.psm1" -Force Write-Verbose -Message "Set-CiRuleOptions: Configuring the policy rule options for: $($FilePath.Name)" [System.Collections.Hashtable]$Intel = ConvertFrom-Json -AsHashtable -InputObject (Get-Content -Path "$([WDACConfig.GlobalVars]::ModuleRootPath)\Resources\PolicyRuleOptions.Json" -Raw) # Load the XML file [System.Xml.XmlDocument]$Xml = Get-Content -Path $FilePath # Define the namespace manager [System.Xml.XmlNamespaceManager]$Ns = New-Object -TypeName System.Xml.XmlNamespaceManager -ArgumentList $Xml.NameTable $Ns.AddNamespace('ns', 'urn:schemas-microsoft-com:sipolicy') # Find the Rules Node [System.Xml.XmlElement]$RulesNode = $Xml.SelectSingleNode('//ns:Rules', $Ns) # an empty hashtable to store the existing rule options in the XML policy file [System.Collections.Hashtable]$ExistingRuleOptions = @{} # The final rule options to implement which contains only unique values $RuleOptionsToImplement = [System.Collections.Generic.HashSet[System.Int32]] @() # Defining the rule options for each policy type and scenario $BaseRules = [System.Collections.Generic.HashSet[System.Int32]] @(0, 2, 5, 6, 11, 12, 16, 17, 19, 20) $BaseISGRules = [System.Collections.Generic.HashSet[System.Int32]] @(0, 2, 5, 6, 11, 12, 14, 15, 16, 17, 19, 20) $BaseKernelModeRules = [System.Collections.Generic.HashSet[System.Int32]] @(2, 5, 6, 16, 17, 20) $SupplementalRules = [System.Collections.Generic.HashSet[System.Int32]] @(6, 18) $RequireWHQLRules = [System.Collections.Generic.HashSet[System.Int32]] @(2) $EnableAuditModeRules = [System.Collections.Generic.HashSet[System.Int32]] @(3) $DisableFlightSigningRules = [System.Collections.Generic.HashSet[System.Int32]] @(4) $RequireEVSignersRules = [System.Collections.Generic.HashSet[System.Int32]] @(8) $ScriptEnforcementRules = [System.Collections.Generic.HashSet[System.Int32]] @(11) $TestModeRules = [System.Collections.Generic.HashSet[System.Int32]] @(9, 10) # A flag to determine whether to clear all the existing rules based on the input parameters if ($PSBoundParameters['Template'] -or $PSBoundParameters['RemoveAll']) { [System.Boolean]$ClearAllRules = $true } else { [System.Boolean]$ClearAllRules = $False } # Iterating through each <Rule> node in the supplied XML file foreach ($RuleNode in $RulesNode.SelectNodes('ns:Rule', $Ns)) { # Get the option text from the <Option> node [System.String]$OptionText = $RuleNode.SelectSingleNode('ns:Option', $Ns).InnerText # Check if the option text exists in the Intel HashTable if ($Intel.ContainsValue($OptionText)) { # Add the option text and its corresponding key to the HashTable [System.Int32]$Key = $Intel.Keys | Where-Object -FilterScript { $Intel[$_] -eq $OptionText } $ExistingRuleOptions[$Key] = [System.String]$OptionText } } if (-NOT $ClearAllRules -and $ExistingRuleOptions.Keys.Count -gt 0) { # Add the existing rule options to the final rule options to implement $RuleOptionsToImplement.UnionWith([System.Collections.Generic.HashSet[System.Int32]]@($ExistingRuleOptions.Keys)) } else { $RuleOptionsToImplement.Clear() } # Process selected templates switch ($Template) { 'Base' { $RuleOptionsToImplement.UnionWith($BaseRules) } 'BaseISG' { $RuleOptionsToImplement.UnionWith($BaseISGRules) } 'BaseKernel' { $RuleOptionsToImplement.UnionWith($BaseKernelModeRules) } 'Supplemental' { $RuleOptionsToImplement.UnionWith($SupplementalRules) } } # Process individual boolean parameters switch ($true) { { $RequireWHQL -eq $true } { $RuleOptionsToImplement.UnionWith($RequireWHQLRules) } { $RequireWHQL -eq $false } { $RuleOptionsToImplement.ExceptWith($RequireWHQLRules) } { $EnableAuditMode -eq $true } { $RuleOptionsToImplement.UnionWith($EnableAuditModeRules) } { $EnableAuditMode -eq $false } { $RuleOptionsToImplement.ExceptWith($EnableAuditModeRules) } { $DisableFlightSigning -eq $true } { $RuleOptionsToImplement.UnionWith($DisableFlightSigningRules) } { $DisableFlightSigning -eq $false } { $RuleOptionsToImplement.ExceptWith($DisableFlightSigningRules) } { $RequireEVSigners -eq $true } { $RuleOptionsToImplement.UnionWith($RequireEVSignersRules) } { $RequireEVSigners -eq $false } { $RuleOptionsToImplement.ExceptWith($RequireEVSignersRules) } { $ScriptEnforcement -eq $false } { $RuleOptionsToImplement.UnionWith($ScriptEnforcementRules) } { $ScriptEnforcement -eq $true } { $RuleOptionsToImplement.ExceptWith($ScriptEnforcementRules) } { $TestMode -eq $true } { $RuleOptionsToImplement.UnionWith($TestModeRules) } { $TestMode -eq $false } { $RuleOptionsToImplement.ExceptWith($TestModeRules) } } # Process individual rules to add foreach ($Item in $RulesToAdd) { [System.Int32]$Key = $Intel.Keys | Where-Object -FilterScript { $Intel[$_] -eq $Item } [System.Void]$RuleOptionsToImplement.Add($Key) } # Process individual rules to remove foreach ($Item in $RulesToRemove) { [System.Int32]$Key = $Intel.Keys | Where-Object -FilterScript { $Intel[$_] -eq $Item } [System.Void]$RuleOptionsToImplement.Remove($Key) } # Make sure Supplemental policies only contain rule options that are applicable to them if (($Template -eq 'Supplemental') -or ($Xml.SiPolicy.PolicyType -eq 'Supplemental Policy')) { foreach ($Rule in $RuleOptionsToImplement) { if ($Rule -notin '18', '14', '13', '7', '5', '6') { [System.Void]$RuleOptionsToImplement.Remove($Rule) } } } } Process { # Compare the existing rule options in the policy XML file with the rule options to implement Compare-Object -ReferenceObject ([System.Int32[]]$RuleOptionsToImplement) -DifferenceObject ([System.Int32[]]$ExistingRuleOptions.Keys) | ForEach-Object -Process { if ($_.SideIndicator -eq '<=') { Write-Verbose -Message "Set-CiRuleOptions: Adding Rule Option: $($Intel[[System.String]$_.InputObject])" } else { Write-Verbose -Message "Set-CiRuleOptions: Removing Rule Option: $($Intel[[System.String]$_.InputObject])" } } # Always remove any existing rule options initially. The calculations determining which # Rules must be included in the policy are all made in the Begin block. if ($null -ne $RulesNode) { $RulesNode.RemoveAll() } # Create new Rule elements foreach ($Rule in ($RuleOptionsToImplement | Sort-Object)) { # Create a new rule element [System.Xml.XmlElement]$NewRuleNode = $Xml.CreateElement('Rule', $RulesNode.NamespaceURI) # Create the Option element inside of the rule element [System.Xml.XmlElement]$OptionNode = $Xml.CreateElement('Option', $RulesNode.NamespaceURI) # Set the value of the Option element $OptionNode.InnerText = $Intel[[System.String]$Rule] # Append the Option element to the Rule element [System.Void]$NewRuleNode.AppendChild($OptionNode) # Add the new Rule element to the Rules node [System.Void]$RulesNode.AppendChild($NewRuleNode) } } End { $Xml.Save($FilePath) # Close the empty XML nodes Close-EmptyXmlNodes_Semantic -XmlFilePath $FilePath # Set the HVCI to Strict Set-HVCIOptions -Strict -FilePath $FilePath # Validate the XML file at the end $null = Test-CiPolicy -XmlFile $FilePath } <# .SYNOPSIS Configures the Policy rule options in a given XML file and sets the HVCI to Strict in the output XML file. This function is completely self-sufficient and does not rely on built-in modules. .DESCRIPTION It offers many ways to configure the policy rule options in a given XML file. All of its various parameters provide the flexibility that ensures only one pass is needed to configure the policy rule options. .LINK https://github.com/HotCakeX/Harden-Windows-Security/wiki/Set-CiRuleOptions .PARAMETER Template Specifies the template to use for the CI policy rules: Base, BaseISG, BaseKernel, or Supplemental .PARAMETER RulesToAdd Specifies the rule options to add to the policy XML file If a rule option is already selected by the RulesToRemove parameter, it won't be suggested by the argument completer of this parameter. .PARAMETER RulesToRemove Specifies the rule options to remove from the policy XML file If a rule option is already selected by the RulesToAdd parameter, it won't be suggested by the argument completer of this parameter. .PARAMETER RemoveAll Removes all the existing rule options from the policy XML file .PARAMETER FilePath Specifies the path to the XML file that contains the CI policy rules .PARAMETER RequireWHQL Specifies whether to require WHQL signatures for all drivers .PARAMETER EnableAuditMode Specifies whether to enable audit mode .PARAMETER DisableFlightSigning Specifies whether to disable flight signing .PARAMETER RequireEVSigners Specifies whether to require EV signers .PARAMETER DisableScriptEnforcement Specifies whether to disable script enforcement .PARAMETER TestMode Specifies whether to enable test mode .NOTES First the template is processed, then the individual boolean parameters, and finally the individual rules to add and remove. .INPUTS System.Management.Automation.SwitchParameter System.IO.FileInfo System.String[] System.String .OUTPUTS System.Void #> } |