diff --git a/CHANGELOG.md b/CHANGELOG.md index f4459028b..1343a1d7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## Unreleased +* Fixed [#259](https://github.com/microsoft/PowerStig/issues/259): Checklist .ckl file fails XML validation in Stig Viewer 2.8. +* Fixed [#527](https://github.com/microsoft/PowerStig/issues/527): Checklist is not using manualcheckfile when using DscResult. +* Fixed [#548](https://github.com/microsoft/PowerStig/issues/548): Target/host data is blank when creating a new checklist. +* Fixed [#546](https://github.com/microsoft/PowerStig/issues/546): Typecast causing an issue when trying to generate checklist using New-StigChecklist function. +* Fixed [#401](https://github.com/microsoft/PowerStig/issues/401): Checklists generated by New-StigChecklist do not provide finding details. +* Update to PowerSTIG to show duplicate rule status matching in a checklist: [#257](https://github.com/microsoft/PowerStig/issues/257). With this update, duplicate rules will show the same status as the original rule when added to a newly-generated checklist. This will boost the number of STIGs that show as processed by PowerSTIG. + ## 4.2.0 * Update PowerSTIG parsing for IIS 8.5 STIG - Ver 1, Rel 9: [#530](https://github.com/microsoft/PowerStig/issues/530) diff --git a/Module/STIG/Convert/Functions.XccdfXml.ps1 b/Module/Common/Functions.XccdfXml.ps1 similarity index 52% rename from Module/STIG/Convert/Functions.XccdfXml.ps1 rename to Module/Common/Functions.XccdfXml.ps1 index 94fb1b577..cab9fe289 100644 --- a/Module/STIG/Convert/Functions.XccdfXml.ps1 +++ b/Module/Common/Functions.XccdfXml.ps1 @@ -1,189 +1,6 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. #region Main Function -<# - .SYNOPSIS - Identifies the type of STIG that has been input and selects the proper private function to - further convert the STIG strings into usable objects. - - .DESCRIPTION - This function enables the core translation of the raw xccdf file by reading the benchmark - title property to determine where to send the data for processing. - - When a ruleset match is found, the xccdf data is sent to private functions that are - dedicated to processing individual STIG setting types, such as registry settings or - security policy. - - If the function is unable to find a rule set match, an error is returned. - - .PARAMETER Path - The path to the xccdf file to be processed. - - .PARAMETER IncludeRawString - This will add the 'Check-Content' from the xcccdf to the output for any additional validation - or spot checking that may be needed. - - .PARAMETER RuleIdFilter - Filters the list rules that are converted to simplify debugging the conversion process. - - .EXAMPLE - ConvertFrom-StigXccdf -Path C:\Stig\U_Windows_2012_and_2012_R2_MS_STIG_V2R8_Manual-xccdf.xml - - .OUTPUTS - Custom objects are created from the STIG base class that are provided in the module - - .NOTES - This is an ongoing project that should be retested with each iteration of the STIG. This is - due to the non-standard way, the content is published. Each version of the STIG may require - a rule to be updated to account for a new string format. All the formatting rules are heavily - tested, so making changes is a simple task. - - .LINK - http://iase.disa.mil/stigs/Lists/stigs-masterlist/AllItems.aspx -#> -function ConvertFrom-StigXccdf -{ - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [string] - $Path, - - [Parameter()] - [string[]] - $RuleIdFilter - ) - - # Get the xml data from the file path provided. - $stigBenchmarkXml = Get-StigXccdfBenchmarkContent -Path $path - - # Global variable needed to distinguish between the IIS server and site stigs. Server Stig needs xIISLogging resource, Site Stig needs xWebsite - $global:stigTitle = $stigBenchmarkXml.title - - # Global variable needed to set and get specific logic needed for filtering and parsing FileContentRules - switch ($true) - { - {$global:stigXccdfName -and -join ((Split-Path -Path $path -Leaf).Split('_') | Select-Object -Index (1, 2)) -eq ''} - { - break; - } - {!$global:stigXccdfName -or $global:stigXccdfName -ne -join ((Split-Path -Path $path -Leaf).Split('_') | Select-Object -Index (1, 2))} - { - $global:stigXccdfName = -join ((Split-Path -Path $path -Leaf).Split('_') | Select-Object -Index (1, 2)) - break; - } - } - # Read in the root stig data from the xml additional functions will dig in deeper - $stigRuleParams = @{ - StigGroupListChangeLog = Get-RuleChangeLog -Path $Path - } - - if ($RuleIdFilter) - { - $stigRuleParams.StigGroupList = $stigBenchmarkXml.Group | Where-Object {$RuleIdFilter -contains $PSItem.Id} - } - else - { - $stigRuleParams.StigGroupList = $stigBenchmarkXml.Group - } - - # The benchmark title drives the rest of the function and must exist to continue. - if ( $null -eq $stigBenchmarkXml.title ) - { - Write-Error -Message 'The Benchmark title property is null. Unable to determine ruleset target.' - return - } - - Get-RegistryRuleExpressions -Path $Path -StigBenchmarkXml $stigBenchmarkXml - - return Get-StigRuleList @stigRuleParams -} - -<# - .SYNOPSIS - Loads the regular expressions files - - .DESCRIPTION - This function loads the regular expression sets to process registry rules in the xccdf file. - - .PARAMETER Path - The path to the xccdf file to be processed. - - .PARAMETER StigBenchmarkXml - The xml for the xccdf file to be processed. -#> -function Get-RegistryRuleExpressions -{ - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [string] - $Path, - - [Parameter(Mandatory = $true)] - [object] - $StigBenchmarkXml - ) - - Begin - { - # Use $stigBenchmarkXml.id to determine the stig file - $benchmarkId = Split-BenchmarkId $stigBenchmarkXml.id - if ([string]::IsNullOrEmpty($benchmarkId.TechnologyRole)) - { - $benchmarkId.TechnologyRole = $stigBenchmarkXml.id - } - - # Handles testing and production - $xccdfFileName = Split-Path $Path -Leaf - $spInclude = @('Data.Core.ps1') - if ($xccdfFileName -eq 'TextData.xml') - { - # Query TechnologyRole and map to file - $officeApps = @('Outlook', 'Excel', 'PowerPoint', 'Word') - $spExclude = @($MyInvocation.MyCommand.Name, 'Template.*.txt', 'Data.ps1', 'Functions.*.ps1', 'Methods.ps1') - - switch ($benchmarkId.TechnologyRole) - { - { $null -ne ($officeApps | Where-Object { $benchmarkId.TechnologyRole -match $_ }) } - { - $spInclude += "Data.Office.ps1" - } - } - } - else - { - # Query directory of xccdf file - $spResult = Split-Path (Split-Path $Path -Parent) -Leaf - if ($spResult) - { - $spInclude += "Data." + $spResult + ".ps1" - } - } - } - - Process - { - # Load specific and core expression sets - $childItemParams = @{ - Path = "$PSScriptRoot\..\..\Rule\Convert" - Exclude = $spExclude - Include = $spInclude - Recurse = $true - } - - $spSupportFileList = Get-ChildItem @childItemParams | Sort-Object -Descending - Clear-Variable SingleLine* -Scope Global - foreach ($supportFile in $spSupportFileList) - { - Write-Verbose "Loading $($supportFile.FullName)" - . $supportFile.FullName - } - } -} - <# .SYNOPSIS Splits the XCCDF of the 2016 STIG into the MS and DC files @@ -218,19 +35,19 @@ function Split-StigXccdf $Destination ) - Begin + begin { - $CurrentVerbosePreference = $global:VerbosePreference + $currentVerbosePreference = $global:VerbosePreference if ($PSBoundParameters.ContainsKey('Verbose')) { $global:VerbosePreference = 'Continue' } } - Process + process { # Get the raw xccdf xml to pull additional details from the root node. - [xml] $msStig = Get-Content -Path $path + [xml] $msStig = Get-Content -Path $Path [xml] $dcStig = $msStig.Clone() # Update the benchmark ID to reflect the STIG content @@ -239,6 +56,7 @@ function Split-StigXccdf # Remove DC and Core settings from the MS xml Write-Information -MessageData "Removing Domain Controller and Core settings from Member Server STIG" + foreach ($group in $msStig.Benchmark.Group) { # Remove DC only settings from the MS xml @@ -261,6 +79,7 @@ function Split-StigXccdf # Remove Core and MS only settings from the DC xml Write-Information -MessageData "Removing Member Server settings from Domain Controller STIG" + foreach ($group in $dcStig.Benchmark.Group) { # Remove MS only settings from DC XML @@ -283,21 +102,21 @@ function Split-StigXccdf if ([string]::IsNullOrEmpty($Destination)) { - $Destination = Split-Path -Path $path -Parent + $Destination = Split-Path -Path $Path -Parent } else { $Destination = $Destination.TrimEnd("\") } - $FilePath = "$Destination\$(Split-Path -Path $path -Leaf)" + $filePath = "$Destination\$(Split-Path -Path $Path -Leaf)" - $msStig.Save(($FilePath -replace '_STIG_', '_MS_STIG_')) - $dcStig.Save(($FilePath -replace '_STIG_', '_DC_STIG_')) + $msStig.Save(($filePath -replace '_STIG_', '_MS_STIG_')) + $dcStig.Save(($filePath -replace '_STIG_', '_DC_STIG_')) } - End + end { - $global:VerbosePreference = $CurrentVerbosePreference + $global:VerbosePreference = $currentVerbosePreference } } @@ -308,15 +127,19 @@ function Split-StigXccdf .SYNOPSIS Get-StigRuleList determines what type of STIG setting is being processed and sends it to a specalized function for additional processing. + .DESCRIPTION Get-StigRuleList pre-sorts the STIG rules that is recieves and tries to determine what type of object it should create. For example if the check content has the string HKEY, it assumes that the setting is a registry object and sends the check to the registry sub functions to further break down the string into a registry object. + .PARAMETER StigGroupList An array of the child STIG Group elements from the parent Benchmark element in the xccdf. + .PARAMETER IncludeRawString A flag that returns the unaltered Check-Content with the converted object. + .NOTES General notes #> @@ -341,7 +164,7 @@ function Get-StigRuleList [int] $stigGroupCount = @($StigGroupList).Count [int] $stigProcessedCounter = 1 - # Global added so that the stig rule can be referenced later + # Global added so that the stig rule can be referenced later. if (-not $exclusionRuleList) { $exclusionFile = Resolve-Path -Path $PSScriptRoot\..\Common\Data.ps1 @@ -363,10 +186,10 @@ function Get-StigRuleList foreach ($correction in $StigGroupListChangeLog[$stigRule.Id]) { - # If the logfile contains a single * as the OldText, treat it as replacing everything with the newText value + # If the logfile contains a single * as the OldText, treat it as replacing everything with the newText value. if ($correction.OldText -eq '*') { - # Resetting OldText '' to the original check-content so the processed xml includes original check-content + # Resetting OldText '' to the original check-content so the processed xml includes original check-content. $correction.OldText = $stigRule.rule.Check.('check-content') $stigRule.rule.Check.('check-content') = $correction.newText } @@ -375,6 +198,7 @@ function Get-StigRuleList $stigRule.rule.Check.('check-content') = $stigRule.rule.Check.('check-content').Replace($correction.oldText, $correction.newText) } } + $rules = [ConvertFactory]::Rule($stigRule) foreach ($rule in $rules) @@ -407,6 +231,7 @@ function Get-StigRuleList { [void] $global:stigSettings.Add($rule) } + } $stigProcessedCounter ++ } @@ -415,71 +240,270 @@ function Get-StigRuleList { $global:stigSettings } -} -#endregion +} <# .SYNOPSIS - Looks up the change log for a given xccdf file and loads the changes + Creates the file name to create from the xccdf content + + .PARAMETER StigDetails + A reference to the in memory xml document. + + .NOTES + This function should only be called from the public ConvertTo-DscStigXml function. + #> -function Get-RuleChangeLog +function Get-PowerStigFileList { [CmdletBinding()] - [OutputType([hashtable])] + [OutputType([Hashtable[]])] param ( [Parameter(Mandatory = $true)] + [xml] + $StigDetails, + + [Parameter()] [string] - $Path + $Destination ) - $path = $Path -replace '\.xml', '.log' + $id = Split-BenchmarkId -Id $stigDetails.Benchmark.id -FilePath $Path + + $fileNameBase = "$($id.Technology)-$($id.TechnologyVersion)" - try + # If there is a technology role add it to the output name + if ($id.TechnologyRole) { - $updateLog = Get-Content -Path $path -Encoding UTF8 -Raw -ErrorAction Stop + $fileNameBase = $fileNameBase + "-$($id.TechnologyRole)" } - catch + + $fileNameBase = $fileNameBase + "-$(Get-StigVersionNumber -StigDetails $StigDetails)" + + if ($Destination) + { + $Destination = Resolve-Path -Path $Destination + } + else { - Write-Warning "$path not found. Please create it if needed." - return @{} + $Destination = "$(Split-Path -Path (Split-Path -Path $PSScriptRoot))\StigData\Processed" + } + + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Destination: $Destination" + + return @{ + Settings = [System.IO.FileInfo]::new("$Destination\$fileNameBase.xml") + OrgSettings = [System.IO.FileInfo]::new("$Destination\$fileNameBase.org.default.xml") } +} + +<# + .SYNOPSIS + Splits the Xccdf benchmark ID into an object. + + .PARAMETER Id + The Id field from the Xccdf benchmark. + + .PARAMETER FilePath + Specifies the file path to the xccdf. Used to determine technology role in SQL STIGs - # regex matches is used to capture the log content directly to the changes variable - $changeList = [regex]::Matches( - $updateLog, '(?V-\d+)(?:::)(?.+)(?:::)(?.+)' +#> +function Split-BenchmarkId +{ + [CmdletBinding()] + [OutputType([Hashtable[]])] + param + ( + [Parameter(Mandatory = $true)] + [string] + $Id, + + [Parameter()] + [string] + $filePath ) - # The function returns a hastable - $updateList = @{} - foreach ($change in $changeList) + # Different STIG's present the Id field in a different format. + $idVariations = @( + '(_+)STIG', + '(_+)Security_Technical_Implementation_Guide_NewBenchmark', + '(_+)Security_Technical_Implementation_Guide' + ) + $sqlServerVariations = @( + 'Microsoft_SQL_Server', + 'MS_SQL_Server' + ) + $sqlServerInstanceVariations = @( + 'Database_Instance' + ) + $windowsVariations = @( + 'Microsoft_Windows', + 'Windows_Server', + 'Windows' + ) + $dnsServerVariations = @( + 'Server_Domain_Name_System', + 'Domain_Name_System' + ) + $activeDirectoryVariations = @( + 'Active_Directory' + ) + $OfficeVariations = @( + 'Excel', + 'Outlook', + 'PowerPoint', + 'Word' + ) + + $id = $id -replace ($idVariations -join '|'), '' + + switch ($id) { - $id = $change.Groups.Item('id').value - $oldText = $change.Groups.Item('oldText').value - # The trim removes any potential CRLF entries that will show up in a regex escape sequence. - # The replace replaces `r`n with an actual new line. This is useful if you need to add data on a separate line. - $newText = $change.Groups.Item('newText').value.Trim().Replace('`r`n',[Environment]::NewLine) - - $changeObject = [pscustomobject] @{ - OldText = $oldText - NewText = $newText - } + {$PSItem -match "SQL_Server"} + { + # The metadata does not differentiate between the database and instance STIG so we have to get that from the file name. + $sqlRole = Get-SqlTechnologyRole -Path $filePath - <# - Some rule have multiple changes that need to be made, so if a rule already - has a change, then add the next change to the value (array) - #> - if ($updateList.ContainsKey($id)) + $returnId = $id -replace ($sqlServerVariations -join '|'), 'SqlServer' + $returnId = $returnId -replace ($sqlServerInstanceVariations -join '|'), $sqlRole + continue + } + {$PSItem -match "_Firewall"} { - $null = $updateList[$id] += $changeObject + $returnId = 'WindowsFirewall_All' + continue } - else + {$PSItem -match "Windows_Defender_Antivirus"} + { + $returnId = 'WindowsDefender_All' + continue + } + {$PSItem -match "IIS_8-5_Server"} + { + $returnId = 'IISServer-8.5' + continue + } + {$PSItem -match "IIS_8-5_Site"} { - $null = $updateList.Add($id, @($changeObject)) + $returnId = 'IISSite-8.5' + continue } + {$PSItem -match "Domain_Name_System"} + { + # The Windows Server 2012 and 2012 R2 STIGs are combined, so return the 2012R2 + $id = $id -replace '_2012_', '_2012R2_' + $returnId = $id -replace ($dnsServerVariations -join '|'), 'DNS' + $returnId = $returnId -replace ($windowsVariations -join '|'), 'WindowsServer' + continue + } + {$PSItem -match "Windows_10"} + { + $returnId = $id -Replace "Windows", 'WindowsClient' + continue + } + {$PSItem -match "Windows"} + { + # The Windows Server 2012 and 2012 R2 STIGs are combined, so return the 2012R2 + $id = $id -replace '_2012_', '_2012R2_' + $returnId = $id -replace ($windowsVariations -join '|'), 'WindowsServer' + continue + } + {$PSItem -match "Active_Directory"} + { + $role = ($id -split '_')[-1] + $returnId = "ActiveDirectory_All_$role" + continue + } + {$PSItem -match "IE_"} + { + $returnId = "InternetExplorer_11" + continue + } + {$PSItem -match 'FireFox'} + { + $returnId = "FireFox_All" + continue + } + {$PSItem -match 'Excel' -or $PSItem -match 'Outlook' -or $PSItem -match 'PowerPoint' -or $PSItem -match 'Word'} + { + $officeStig = ($id -split '_') + $officeStig = $officeStig[1] + $officeStig[2] + $returnId = 'Office_' + $officeStig + continue + } + {$PSItem -match 'Dot_Net'} + { + $returnId = 'DotNetFramework_4' + continue + } + default + { + $returnId = $id + } + } + + $returnId = $returnId -Split '_' + + return @{ + 'Technology' = $returnId[0] + 'TechnologyVersion' = $returnId[1] + 'TechnologyRole' = $returnId[2] } +} + +<# + .SYNOPSIS + Retrieves the SQL Server technology role from the file name of the xccdf. +#> +function Get-SqlTechnologyRole +{ + [CmdletBinding()] + [OutputType([string])] + param + ( + [Parameter(Mandatory=$true)] + [AllowEmptyString()] + [string] + $Path + ) + + $split = $Path -split '_' + $stigIndex = $split.IndexOf('STIG') + $sqlRole = $split[$stigIndex -1] - $updateList + return $sqlRole } +<# + .SYNOPSIS + Creates a version number from the xccdf benchmark element details. + + .PARAMETER stigDetails + A reference to the in memory xml document. + + .NOTES + This function should only be called from the public ConvertTo-DscStigXml function. + +#> +function Get-StigVersionNumber +{ + [CmdletBinding()] + [OutputType([version])] + param + ( + [Parameter(Mandatory = $true)] + [xml] + $StigDetails + ) + + # Extract the revision number from the xccdf + $revision = ( $StigDetails.Benchmark.'plain-text'.'#text' ` + -split "(Release:)(.*?)(Benchmark)" )[2].trim() + + "$($StigDetails.Benchmark.version).$revision" + +} + +#endregion + diff --git a/Module/STIG/Convert/Functions.PowerStigXml.ps1 b/Module/STIG/Convert/Functions.PowerStigXml.ps1 index 85d4afcb6..7effe4210 100644 --- a/Module/STIG/Convert/Functions.PowerStigXml.ps1 +++ b/Module/STIG/Convert/Functions.PowerStigXml.ps1 @@ -1,6 +1,189 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. #region Main Function +<# + .SYNOPSIS + Identifies the type of STIG that has been input and selects the proper private function to + further convert the STIG strings into usable objects. + + .DESCRIPTION + This function enables the core translation of the raw xccdf file by reading the benchmark + title property to determine where to send the data for processing. + + When a ruleset match is found, the xccdf data is sent to private functions that are + dedicated to processing individual STIG setting types, such as registry settings or + security policy. + + If the function is unable to find a rule set match, an error is returned. + + .PARAMETER Path + The path to the xccdf file to be processed. + + .PARAMETER IncludeRawString + This will add the 'Check-Content' from the xcccdf to the output for any additional validation + or spot checking that may be needed. + + .PARAMETER RuleIdFilter + Filters the list rules that are converted to simplify debugging the conversion process. + + .EXAMPLE + ConvertFrom-StigXccdf -Path C:\Stig\U_Windows_2012_and_2012_R2_MS_STIG_V2R8_Manual-xccdf.xml + + .OUTPUTS + Custom objects are created from the STIG base class that are provided in the module + + .NOTES + This is an ongoing project that should be retested with each iteration of the STIG. This is + due to the non-standard way, the content is published. Each version of the STIG may require + a rule to be updated to account for a new string format. All the formatting rules are heavily + tested, so making changes is a simple task. + + .LINK + http://iase.disa.mil/stigs/Lists/stigs-masterlist/AllItems.aspx +#> +function ConvertFrom-StigXccdf +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [string] + $Path, + + [Parameter()] + [string[]] + $RuleIdFilter + ) + + # Get the xml data from the file path provided. + $stigBenchmarkXml = Get-StigXccdfBenchmarkContent -Path $path + + # Global variable needed to distinguish between the IIS server and site stigs. Server Stig needs xIISLogging resource, Site Stig needs xWebsite + $global:stigTitle = $stigBenchmarkXml.title + + # Global variable needed to set and get specific logic needed for filtering and parsing FileContentRules + switch ($true) + { + {$global:stigXccdfName -and -join ((Split-Path -Path $path -Leaf).Split('_') | Select-Object -Index (1, 2)) -eq ''} + { + break; + } + {!$global:stigXccdfName -or $global:stigXccdfName -ne -join ((Split-Path -Path $path -Leaf).Split('_') | Select-Object -Index (1, 2))} + { + $global:stigXccdfName = -join ((Split-Path -Path $path -Leaf).Split('_') | Select-Object -Index (1, 2)) + break; + } + } + # Read in the root stig data from the xml additional functions will dig in deeper + $stigRuleParams = @{ + StigGroupListChangeLog = Get-RuleChangeLog -Path $Path + } + + if ($RuleIdFilter) + { + $stigRuleParams.StigGroupList = $stigBenchmarkXml.Group | Where-Object {$RuleIdFilter -contains $PSItem.Id} + } + else + { + $stigRuleParams.StigGroupList = $stigBenchmarkXml.Group + } + + # The benchmark title drives the rest of the function and must exist to continue. + if ( $null -eq $stigBenchmarkXml.title ) + { + Write-Error -Message 'The Benchmark title property is null. Unable to determine ruleset target.' + return + } + + Get-RegistryRuleExpressions -Path $Path -StigBenchmarkXml $stigBenchmarkXml + + return Get-StigRuleList @stigRuleParams +} + +<# + .SYNOPSIS + Loads the regular expressions files + + .DESCRIPTION + This function loads the regular expression sets to process registry rules in the xccdf file. + + .PARAMETER Path + The path to the xccdf file to be processed. + + .PARAMETER StigBenchmarkXml + The xml for the xccdf file to be processed. +#> +function Get-RegistryRuleExpressions +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [string] + $Path, + + [Parameter(Mandatory = $true)] + [object] + $StigBenchmarkXml + ) + + begin + { + # Use $stigBenchmarkXml.id to determine the stig file + $benchmarkId = Split-BenchmarkId $stigBenchmarkXml.id + if ([string]::IsNullOrEmpty($benchmarkId.TechnologyRole)) + { + $benchmarkId.TechnologyRole = $stigBenchmarkXml.id + } + + # Handles testing and production + $xccdfFileName = Split-Path $Path -Leaf + $spInclude = @('Data.Core.ps1') + if ($xccdfFileName -eq 'TextData.xml') + { + # Query TechnologyRole and map to file + $officeApps = @('Outlook', 'Excel', 'PowerPoint', 'Word') + $spExclude = @($MyInvocation.MyCommand.Name, 'Template.*.txt', 'Data.ps1', 'Functions.*.ps1', 'Methods.ps1') + + switch ($benchmarkId.TechnologyRole) + { + { $null -ne ($officeApps | Where-Object { $benchmarkId.TechnologyRole -match $_ }) } + { + $spInclude += "Data.Office.ps1" + } + } + } + else + { + # Query directory of xccdf file + $spResult = Split-Path (Split-Path $Path -Parent) -Leaf + if ($spResult) + { + $spInclude += "Data." + $spResult + ".ps1" + } + } + } + + process + { + # Load specific and core expression sets + $childItemParams = @{ + Path = "$PSScriptRoot\..\..\Rule\Convert" + Exclude = $spExclude + Include = $spInclude + Recurse = $true + } + + $spSupportFileList = Get-ChildItem @childItemParams | Sort-Object -Descending + Clear-Variable SingleLine* -Scope Global + foreach ($supportFile in $spSupportFileList) + { + Write-Verbose "Loading $($supportFile.FullName)" + . $supportFile.FullName + } + } +} + <# .SYNOPSIS This function generates a new xml file based on the convert objects from ConvertFrom-StigXccdf. @@ -48,7 +231,7 @@ function ConvertTo-PowerStigXml $DoNotExportDescription ) - Begin + begin { $CurrentVerbosePreference = $global:VerbosePreference @@ -57,7 +240,7 @@ function ConvertTo-PowerStigXml $global:VerbosePreference = 'Continue' } } - Process + process { $convertedStigObjects = ConvertFrom-StigXccdf -Path $Path -RuleIdFilter $RuleIdFilter @@ -259,7 +442,7 @@ function ConvertTo-PowerStigXml Write-Output "Org Settings Output: $($fileList.OrgSettings.FullName)" } } - End + end { $global:VerbosePreference = $CurrentVerbosePreference } @@ -291,7 +474,7 @@ function Compare-PowerStigXml [switch] $IgnoreRawString ) - Begin + begin { $CurrentVerbosePreference = $global:VerbosePreference @@ -300,7 +483,7 @@ function Compare-PowerStigXml $global:VerbosePreference = 'Continue' } } - Process + process { [xml] $OldStigContent = Get-Content -Path $OldStigPath -Encoding UTF8 @@ -373,7 +556,7 @@ function Compare-PowerStigXml } $returnCompareList.GetEnumerator() | Sort-Object Name } - End + end { $global:VerbosePreference = $CurrentVerbosePreference } @@ -466,235 +649,6 @@ function New-OrganizationalSettingsXmlFile Write-Output -InputObject "`r`n" | Out-File -FilePath $Destination -Append -Encoding utf8 -NoNewline } -<# - .SYNOPSIS - Creates a version number from the xccdf benchmark element details. - .PARAMETER stigDetails - A reference to the in memory xml document. - .NOTES - This function should only be called from the public ConvertTo-DscStigXml function. -#> -function Get-StigVersionNumber -{ - [CmdletBinding()] - [OutputType([version])] - param - ( - [Parameter(Mandatory = $true)] - [xml] - $StigDetails - ) - - # Extract the revision number from the xccdf - $revision = ( $StigDetails.Benchmark.'plain-text'.'#text' ` - -split "(Release:)(.*?)(Benchmark)" )[2].trim() - - "$($StigDetails.Benchmark.version).$revision" -} - -<# - .SYNOPSIS - Creates the file name to create from the xccdf content - .PARAMETER StigDetails - A reference to the in memory xml document. - .NOTES - This function should only be called from the public ConvertTo-DscStigXml function. -#> -function Get-PowerStigFileList -{ - [CmdletBinding()] - [OutputType([Hashtable[]])] - param - ( - [Parameter(Mandatory = $true)] - [xml] - $StigDetails, - - [Parameter()] - [string] - $Destination - ) - - $id = Split-BenchmarkId -Id $stigDetails.Benchmark.id -FilePath $Path - - $fileNameBase = "$($id.Technology)-$($id.TechnologyVersion)" - - # If there is a technology role add it to the output name - if ($id.TechnologyRole) - { - $fileNameBase = $fileNameBase + "-$($id.TechnologyRole)" - } - - $fileNameBase = $fileNameBase + "-$(Get-StigVersionNumber -StigDetails $StigDetails)" - - if ($Destination) - { - $Destination = Resolve-Path -Path $Destination - } - else - { - $Destination = "$(Split-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot)))\StigData\Processed" - } - - Write-Verbose "[$($MyInvocation.MyCommand.Name)] Destination: $Destination" - - return @{ - Settings = [System.IO.FileInfo]::new("$Destination\$fileNameBase.xml") - OrgSettings = [System.IO.FileInfo]::new("$Destination\$fileNameBase.org.default.xml") - } -} - -<# - .SYNOPSIS - Splits the Xccdf benchmark ID into an object. - .PARAMETER Id - The Id field from the Xccdf benchmark. - .PARAMETER FilePath - Specifies the file path to the xccdf. Used to determine technology role in SQL STIGs -#> -function Split-BenchmarkId -{ - [CmdletBinding()] - [OutputType([Hashtable[]])] - param - ( - [Parameter(Mandatory = $true)] - [string] - $Id, - - [Parameter()] - [string] - $FilePath - ) - - # Different STIG's present the Id field in a different format. - $idVariations = @( - '(_+)STIG', - '(_+)Security_Technical_Implementation_Guide_NewBenchmark', - '(_+)Security_Technical_Implementation_Guide' - ) - $sqlServerVariations = @( - 'Microsoft_SQL_Server', - 'MS_SQL_Server' - ) - $sqlServerInstanceVariations = @( - 'Database_Instance' - ) - $windowsVariations = @( - 'Microsoft_Windows', - 'Windows_Server', - 'Windows' - ) - $dnsServerVariations = @( - 'Server_Domain_Name_System', - 'Domain_Name_System' - ) - $activeDirectoryVariations = @( - 'Active_Directory' - ) - $OfficeVariations = @( - 'Excel', - 'Outlook', - 'PowerPoint', - 'Word' - ) - - $id = $id -replace ($idVariations -join '|'), '' - - switch ($id) - { - {$PSItem -match "SQL_Server"} - { - # The metadata does not differentiate between the database and instance STIG so we have to get that from the file name. - $sqlRole = Get-SqlTechnologyRole -Path $FilePath - - $returnId = $id -replace ($sqlServerVariations -join '|'), 'SqlServer' - $returnId = $returnId -replace ($sqlServerInstanceVariations -join '|'), $sqlRole - continue - } - {$PSItem -match "_Firewall"} - { - $returnId = 'WindowsFirewall_All' - continue - } - {$PSItem -match "Windows_Defender_Antivirus"} - { - $returnId = 'WindowsDefender_All' - continue - } - {$PSItem -match "IIS_8-5_Server"} - { - $returnId = 'IISServer-8.5' - continue - } - {$PSItem -match "IIS_8-5_Site"} - { - $returnId = 'IISSite-8.5' - continue - } - {$PSItem -match "Domain_Name_System"} - { - # The Windows Server 2012 and 2012 R2 STIGs are combined, so return the 2012R2 - $id = $id -replace '_2012_', '_2012R2_' - $returnId = $id -replace ($dnsServerVariations -join '|'), 'DNS' - $returnId = $returnId -replace ($windowsVariations -join '|'), 'WindowsServer' - continue - } - {$PSItem -match "Windows_10"} - { - $returnId = $id -Replace "Windows", 'WindowsClient' - continue - } - {$PSItem -match "Windows"} - { - # The Windows Server 2012 and 2012 R2 STIGs are combined, so return the 2012R2 - $id = $id -replace '_2012_', '_2012R2_' - $returnId = $id -replace ($windowsVariations -join '|'), 'WindowsServer' - continue - } - {$PSItem -match "Active_Directory"} - { - $role = ($id -split '_')[-1] - $returnId = "ActiveDirectory_All_$role" - continue - } - {$PSItem -match "IE_"} - { - $returnId = "InternetExplorer_11" - continue - } - {$PSItem -match 'FireFox'} - { - $returnId = "FireFox_All" - continue - } - {$PSItem -match 'Excel' -or $PSItem -match 'Outlook' -or $PSItem -match 'PowerPoint' -or $PSItem -match 'Word'} - { - $officeStig = ($id -split '_') - $officeStig = $officeStig[1] + $officeStig[2] - $returnId = 'Office_' + $officeStig - continue - } - {$PSItem -match 'Dot_Net'} - { - $returnId = 'DotNetFramework_4' - continue - } - default - { - $returnId = $id - } - } - - $returnId = $returnId -Split '_' - - return @{ - 'Technology' = $returnId[0] - 'TechnologyVersion' = $returnId[1] - 'TechnologyRole' = $returnId[2] - } -} - <# .SYNOPSIS Filters the list of STIG objects and returns anything that requires an organizational decision. @@ -850,29 +804,6 @@ function Get-BaseRulePropertyName return (Get-Member -InputObject $baseRule -MemberType Property).Name } -<# - .SYNOPSIS - Retrieves the SQL Server technology role from the file name of the xccdf. -#> -function Get-SqlTechnologyRole -{ - [CmdletBinding()] - [OutputType([string])] - param - ( - [Parameter(Mandatory=$true)] - [AllowEmptyString()] - [string] - $Path - ) - - $split = $Path -split '_' - $stigIndex = $split.IndexOf('STIG') - $sqlRole = $split[$stigIndex -1] - - return $sqlRole -} - <# .SYNOPSIS Returns a list of all PowerSTIG RuleTypes. @@ -906,4 +837,69 @@ function Get-DynamicParameterRuleTypeName $runtimeDefinedParamDictionary.Add($parameterName, $runtimeDefinedParam) return $runtimeDefinedParamDictionary } + +<# + .SYNOPSIS + Looks up the change log for a given xccdf file and loads the changes +#> +function Get-RuleChangeLog +{ + [CmdletBinding()] + [OutputType([hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [string] + $Path + ) + + $path = $Path -replace '\.xml', '.log' + + try + { + $updateLog = Get-Content -Path $path -Encoding UTF8 -Raw -ErrorAction Stop + } + catch + { + Write-Warning "$path not found. Please create it if needed." + return @{} + } + + # regex matches is used to capture the log content directly to the changes variable + $changeList = [regex]::Matches( + $updateLog, '(?V-\d+)(?:::)(?.+)(?:::)(?.+)' + ) + + # The function returns a hastable + $updateList = @{} + foreach ($change in $changeList) + { + $id = $change.Groups.Item('id').value + $oldText = $change.Groups.Item('oldText').value + # The trim removes any potential CRLF entries that will show up in a regex escape sequence. + # The replace replaces `r`n with an actual new line. This is useful if you need to add data on a separate line. + $newText = $change.Groups.Item('newText').value.Trim().Replace('`r`n',[Environment]::NewLine) + + $changeObject = [pscustomobject] @{ + OldText = $oldText + NewText = $newText + } + + <# + Some rule have multiple changes that need to be made, so if a rule already + has a change, then add the next change to the value (array) + #> + if ($updateList.ContainsKey($id)) + { + $null = $updateList[$id] += $changeObject + } + else + { + $null = $updateList.Add($id, @($changeObject)) + } + } + + $updateList +} + #endregion diff --git a/Module/STIG/Functions.Checklist.ps1 b/Module/STIG/Functions.Checklist.ps1 index 70555328c..e9814a74e 100644 --- a/Module/STIG/Functions.Checklist.ps1 +++ b/Module/STIG/Functions.Checklist.ps1 @@ -41,7 +41,7 @@ function New-StigCheckList $ReferenceConfiguration, [Parameter(Mandatory = $true, ParameterSetName = 'result')] - [System.Collections.ArrayList] + [psobject] $DscResult, [Parameter(Mandatory = $true)] @@ -57,13 +57,14 @@ function New-StigCheckList $ManualCheckFile ) + # Validate parameters before continuing if ($ManualCheckFile) { if (-not (Test-Path -Path $ManualCheckFile)) { throw "$($ManualCheckFile) is not a valid path to a ManualCheckFile. Provide a full valid path" } - $manualCheckData = Import-PowerShellDataFile -path $ManualCheckFile + [string]$manualCheckData = Get-Content $manualCheckFile } if (-not (Test-Path -Path $OutputPath.DirectoryName)) @@ -76,9 +77,56 @@ function New-StigCheckList throw "$($OutputPath.FullName) is not a valid checklist extension. Please provide a full valid path ending in .ckl" } - if (-not (Test-Path -Path $ReferenceConfiguration)) + # Values for some of these fields can be read from the .mof file or the DSC results file + if ($PSCmdlet.ParameterSetName -eq 'mof') + { + if (-not (Test-Path -Path $ReferenceConfiguration)) + { + throw "$($ReferenceConfiguration) is not a valid path to a configuration (.mof) file. Please provide a valid entry." + } + + $MofString = Get-Content -Path $ReferenceConfiguration -Raw + $TargetNode = Get-TargetNodeFromMof($MofString) + + } + elseif ($PSCmdlet.ParameterSetName -eq 'result') + { + # Check the returned object + if ($null -eq $DscResult) + { + throw 'Passed in $DscResult parameter is null. Please provide a valid result using Test-DscConfiguration.' + } + $TargetNode = $DscResult.PSComputerName + } + + $TargetNodeType = Get-TargetNodeType($TargetNode) + + switch ($TargetNodeType) { - throw "$($ReferenceConfiguration) is not a valid path to a configuration (.mof) file. Please provide a valid entry." + "MACAddress" + { + $HostnameMACAddress = $TargetNode + Break + } + "IPv4Address" + { + $HostnameIPAddress = $TargetNode + Break + } + "IPv6Address" + { + $HostnameIPAddress = $TargetNode + Break + } + "FQDN" + { + $HostnameFQDN = $TargetNode + Break + } + default + { + $Hostname = $TargetNode + } } $xmlWriterSettings = [System.Xml.XmlWriterSettings]::new() @@ -96,11 +144,10 @@ function New-StigCheckList $assetElements = [ordered] @{ 'ROLE' = 'None' 'ASSET_TYPE' = 'Computing' - 'HOST_NAME' = '' - 'HOST_IP' = '' - 'HOST_MAC' = '' - 'HOST_GUID' = '' - 'HOST_FQDN' = '' + 'HOST_NAME' = "$Hostname" + 'HOST_IP' = "$HostnameIPAddress" + 'HOST_MAC' = "$HostnameMACAddress" + 'HOST_FQDN' = "$HostnameFQDN" 'TECH_AREA' = '' 'TARGET_KEY' = '2350' 'WEB_OR_DATABASE' = 'false' @@ -163,13 +210,22 @@ function New-StigCheckList #region STIGS/iSTIG/VULN[] - foreach ($vulnerability in (Get-VulnerabilityList -XccdfBenchmark $xccdfBenchmarkContent)) + # Pull in the processed XML file to check for duplicate rules for each vulnerability + [xml]$xccdfBenchmark = Get-Content -Path $xccdfPath -Encoding UTF8 + $fileList = Get-PowerStigFileList -StigDetails $xccdfBenchmark + $processedFileName = $fileList.Settings.FullName + [xml]$processed = Get-Content -Path $processedFileName + + $vulnerabilities = Get-VulnerabilityList -XccdfBenchmark $xccdfBenchmarkContent + + foreach ($vulnerability in $vulnerabilities) { $writer.WriteStartElement("VULN") foreach ($attribute in $vulnerability.GetEnumerator()) { $status = $null + $findingDetails = $null $comments = $null $manualCheck = $null @@ -206,11 +262,14 @@ function New-StigCheckList if ($setting) { $status = $statusMap['NotAFinding'] + $comments = "To be addressed by PowerStig MOF via $setting" + $findingDetails = Get-FindingDetails -Setting $setting } elseif ($manualCheck) { $status = $statusMap["$($manualCheck.Status)"] + $findingDetails = $manualCheck.Details $comments = $manualCheck.Comments } else @@ -220,24 +279,76 @@ function New-StigCheckList } elseif ($PSCmdlet.ParameterSetName -eq 'result') { - $setting = Get-SettingsFromResult -DscResult $dscResult -Id $vid + $manualCheck = $manualCheckData | Where-Object -FilterScript {$_.VulID -eq $VID} + # If we have manual check data, we don't need to look at the configuration + if ($manualCheck) + { + $status = $statusMap["$($manualCheck.Status)"] + $findingDetails = $manualCheck.Details + $comments = $manualCheck.Comments + } + else + { + $setting = Get-SettingsFromResult -DscResult $dscResult -Id $vid + if ($setting) + { + if ($setting.InDesiredState -eq $true) + { + $status = $statusMap['NotAFinding'] + $comments = "Addressed by PowerStig MOF via $setting" + $findingDetails = Get-FindingDetails -Setting $setting + } + elseif ($setting.InDesiredState -eq $false) + { + $status = $statusMap['Open'] + $comments = "Configuration attempted by PowerStig MOF via $setting, but not currently set." + $findingDetails = Get-FindingDetails -Setting $setting + } + else + { + $status = $statusMap['Open'] + } + } + else + { + $status = $statusMap['NotReviewed'] + } + } + } - if ($setting) + # Test to see if this rule is managed as a duplicate + $convertedRule = $processed.SelectSingleNode("//Rule[@id='$vid']") + + if ($convertedRule.DuplicateOf) + { + # How is the duplicate rule handled? If it is handled, then this duplicate is also covered + if ($PSCmdlet.ParameterSetName -eq 'mof') { - if ($setting.InDesiredState) + $originalSetting = Get-SettingsFromMof -ReferenceConfiguration $referenceConfiguration -Id $convertedRule.DuplicateOf + + if ($originalSetting) { $status = $statusMap['NotAFinding'] + $findingDetails = 'See ' + $convertedRule.DuplicateOf + ' for Finding Details.' + $comments = 'Managed via PowerStigDsc - this rule is a duplicate of ' + $convertedRule.DuplicateOf + } + } + elseif ($PSCmdlet.ParameterSetName -eq 'result') + { + $originalSetting = Get-SettingsFromResult -DscResult $dscResult -id $convertedRule.DuplicateOf + + if ($originalSetting.InDesiredState -eq 'True') + { + $status = $statusMap['NotAFinding'] + $findingDetails = 'See ' + $convertedRule.DuplicateOf + ' for Finding Details.' + $comments = 'Managed via PowerStigDsc - this rule is a duplicate of ' + $convertedRule.DuplicateOf } else { $status = $statusMap['Open'] + $findingDetails = 'See ' + $convertedRule.DuplicateOf + ' for Finding Details.' + $comments = 'Managed via PowerStigDsc - this rule is a duplicate of ' + $convertedRule.DuplicateOf } - - $comments = 'Managed via PowerStigDsc from Live call' - } - else - { - $status = $statusMap['NotReviewed'] } } @@ -246,7 +357,7 @@ function New-StigCheckList $writer.WriteEndElement(<#STATUS#>) $writer.WriteStartElement("FINDING_DETAILS") - $writer.WriteString((Get-FindingDetails -Setting $setting)) + $writer.WriteString($findingDetails) $writer.WriteEndElement(<#FINDING_DETAILS#>) $writer.WriteStartElement("COMMENTS") @@ -385,7 +496,9 @@ function Get-SettingsFromMof $mofContent = Get-MofContent -ReferenceConfiguration $referenceConfiguration - return $mofContent.Where({$PSItem.ResourceID -match $id}) + $mofContentFound = $mofContent.Where({$PSItem.ResourceID -match $Id}) + + return $mofContentFound } <# @@ -399,7 +512,7 @@ function Get-SettingsFromResult param ( [Parameter(Mandatory = $true)] - [System.Collections.ArrayList] + [psobject] $DscResult, [Parameter(Mandatory = $true)] @@ -433,29 +546,113 @@ function Get-FindingDetails switch ($setting.ResourceID) { + # Only add custom entries if specific output is more valuable than dumping all properties + {$PSItem -match "^\[None\]"} + { + return "No DSC resource was leveraged for this rule (Resource=None)" + } {$PSItem -match "^\[(x)?Registry\]"} { return "Registry Value = $($setting.ValueData)" } - {$PSItem -match "^\[AuditPolicySubcategory\]"} + {$PSItem -match "^\[UserRightsAssignment\]"} { - return "AuditPolicySubcategory AuditFlag = $($setting.AuditFlag)" + return "UserRightsAssignment Identity = $($setting.Identity)" } - {$PSItem -match "^\[AccountPolicy\]"} + default { - return "AccountPolicy = Needs work" + return Get-FindingDetailsString -Setting $setting } - {$PSItem -match "^\[UserRightsAssignment\]"} + } +} + +<# + .SYNOPSIS + Formats properties and values with standard string format. + +#> +function Get-FindingDetailsString +{ + [OutputType([string])] + [CmdletBinding()] + param + ( + [Parameter(Mandatory)] + [AllowNull()] + [psobject] + $Setting + ) + + foreach ($property in $setting.PSobject.properties) { + if ($property.TypeNameOfValue -Match 'String') { - return "UserRightsAssignment Identity = $($setting.Identity)" + $returnString += $($property.Name) + ' = ' + $returnString += $($setting.PSobject.properties[$property.Name].Value) + "`n" + } + } + return $returnString +} +function Get-TargetNodeFromMof +{ + [OutputType([string])] + [CmdletBinding()] + param + ( + [Parameter(Mandatory)] + [string] + $MofString + ) + + $pattern = "((?<=@TargetNode=')(.*)(?='))" + $TargetNodeSearch = $mofstring | Select-String -Pattern $pattern + $TargetNode = $TargetNodeSearch.matches.value + return $TargetNode +} +function Get-TargetNodeType +{ + [OutputType([string])] + [CmdletBinding()] + param + ( + [Parameter(Mandatory)] + [string] + $TargetNode + ) + + switch ($TargetNode) + { + # Do we have a MAC address? + { + $_ -match '(([0-9a-f]{2}:){5}[0-9a-f]{2})' } - {$PSItem -match "^\[SecurityOption\]"} { - return "SecurityOption = Needs work" + return 'MACAddress' + } + + # Do we have an IPv6 address? + { + $_ -match '(([0-9a-f]{0,4}:){7}[0-9a-f]{0,4})' } - default { - return "not found" + return 'IPv4Address' + } + + # Do we have an IPv4 address? + { + $_ -match '(([0-9]{1,3}\.){3}[0-9]{1,3})' + } + { + return 'IPv6Address' + } + + # Do we have a Fully-qualified Domain Name? + { + $_ -match '([a-zA-Z0-9-.\+]{2,256}\.[a-z]{2,256}\b)' + } + { + return 'FQDN' } } + + return '' } diff --git a/PowerStig.psd1 b/PowerStig.psd1 index 3c2af6c9c..5e3724729 100644 --- a/PowerStig.psd1 +++ b/PowerStig.psd1 @@ -71,7 +71,11 @@ DscResourcesToExport = @( FunctionsToExport = @( 'Get-DomainName', 'Get-Stig', - 'New-StigCheckList' + 'New-StigCheckList', + 'Get-StigRuleList', + 'Get-StigVersionNumber', + 'Get-PowerStigFilelist', + 'Split-BenchmarkId' ) # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. diff --git a/PowerStig.psm1 b/PowerStig.psm1 index b9db961f9..5ee440081 100644 --- a/PowerStig.psm1 +++ b/PowerStig.psm1 @@ -14,5 +14,9 @@ foreach ($supportFile in (Get-ChildItem -Path $pathList -File -Filter '*.ps1')) Export-ModuleMember -Function @( 'Get-DomainName', 'Get-Stig', - 'New-StigCheckList' + 'New-StigCheckList', + 'Get-StigRuleList', + 'Get-StigVersionNumber', + 'Get-PowerStigFilelist', + 'Split-BenchmarkId' ) diff --git a/StigData/Processed/SqlServer-2016-Instance-1.3.xml b/StigData/Processed/SqlServer-2016-Instance-1.3.xml index 6c7300b71..3fe110b23 100644 --- a/StigData/Processed/SqlServer-2016-Instance-1.3.xml +++ b/StigData/Processed/SqlServer-2016-Instance-1.3.xml @@ -1,4 +1,4 @@ - + <VulnDiscussion>Database management includes the ability to control the number of users and user sessions utilizing SQL Server. Unlimited concurrent connections to SQL Server could allow a successful Denial of Service (DoS) attack by exhausting connection resources; and a system can also fail or be degraded by an overload of legitimate users. Limiting the number of concurrent sessions per user is helpful in reducing these risks. @@ -2235,6 +2235,27 @@ Note: <name> represents the username portion of the login. For example, if If no account information is returned, this is not a finding. If account information is returned, this is a finding. + + + <VulnDiscussion>Session auditing is for use when a user's activities are under investigation. To be sure of capturing all activity during those periods when session auditing is in use, it needs to be in operation for the whole time SQL Server is running.</VulnDiscussion><FalsePositives></FalsePositives><FalseNegatives></FalseNegatives><Documentable>false</Documentable><Mitigations></Mitigations><SeverityOverrideGuidance></SeverityOverrideGuidance><PotentialImpacts></PotentialImpacts><ThirdPartyTools></ThirdPartyTools><MitigationControl></MitigationControl><Responsibility></Responsibility><IAControls></IAControls> + + False + False + + When Audits are enabled, they start up when the instance starts. +https://msdn.microsoft.com/en-us/library/cc280386.aspx#Anchor_2 + +Check if an audit is configured and enabled. + +Execute the following query: + +SELECT name AS 'Audit Name', +status_desc AS 'Audit Status', +audit_file_path AS 'Current Audit File' +FROM sys.dm_server_audit_status +WHERE status_desc = 'STARTED' + +All currently defined audits for the SQL server instance will be listed. If no audits are returned, this is a finding. <VulnDiscussion>If audit data were to become compromised, then competent forensic analysis and discovery of the true source of potentially malicious system activity is difficult, if not impossible, to achieve. In addition, access to audit records provides information an attacker could potentially use to his or her advantage. @@ -2736,32 +2757,6 @@ For more information, see https://support.microsoft.com/en-us/kb/3141890. - - <VulnDiscussion>Session auditing is for use when a user's activities are under investigation. To be sure of capturing all activity during those periods when session auditing is in use, it needs to be in operation for the whole time SQL Server is running.</VulnDiscussion><FalsePositives></FalsePositives><FalseNegatives></FalseNegatives><Documentable>false</Documentable><Mitigations></Mitigations><SeverityOverrideGuidance></SeverityOverrideGuidance><PotentialImpacts></PotentialImpacts><ThirdPartyTools></ThirdPartyTools><MitigationControl></MitigationControl><Responsibility></Responsibility><IAControls></IAControls> - - USE [master] DECLARE @MissingAuditCount INTEGER DECLARE @server_specification_id INTEGER DECLARE @FoundCompliant INTEGER SET @FoundCompliant = 0 /* Create a table for the events that we are looking for */ CREATE TABLE #AuditEvents (AuditEvent varchar(100)) INSERT INTO #AuditEvents (AuditEvent) VALUES () /* Create a cursor to walk through all audits that are enabled at startup */ DECLARE auditspec_cursor CURSOR FOR SELECT s.server_specification_id FROM sys.server_audits a INNER JOIN sys.server_audit_specifications s ON a.audit_guid = s.audit_guid WHERE a.is_state_enabled = 1; OPEN auditspec_cursor FETCH NEXT FROM auditspec_cursor INTO @server_specification_id WHILE @@FETCH_STATUS = 0 AND @FoundCompliant = 0 /* Does this specification have the needed events in it? */ BEGIN SET @MissingAuditCount = (SELECT Count(a.AuditEvent) AS MissingAuditCount FROM #AuditEvents a JOIN sys.server_audit_specification_details d ON a.AuditEvent = d.audit_action_name WHERE d.audit_action_name NOT IN (SELECT d2.audit_action_name FROM sys.server_audit_specification_details d2 WHERE d2.server_specification_id = @server_specification_id)) IF @MissingAuditCount = 0 SET @FoundCompliant = 1; FETCH NEXT FROM auditspec_cursor INTO @server_specification_id END CLOSE auditspec_cursor; DEALLOCATE auditspec_cursor; DROP TABLE #AuditEvents /* Produce output that works with DSC - records if we do not find the audit events we are looking for */ IF @FoundCompliant > 0 SELECT name FROM sys.sql_logins WHERE principal_id = -1; ELSE SELECT name FROM sys.sql_logins WHERE principal_id = 1 - False - False - - When Audits are enabled, they start up when the instance starts. -https://msdn.microsoft.com/en-us/library/cc280386.aspx#Anchor_2 - -Check if an audit is configured and enabled. - -Execute the following query: - -SELECT name AS 'Audit Name', -status_desc AS 'Audit Status', -audit_file_path AS 'Current Audit File' -FROM sys.dm_server_audit_status -WHERE status_desc = 'STARTED' - -All currently defined audits for the SQL server instance will be listed. If no audits are returned, this is a finding. - /* See STIG supplemental files for the annotated version of this script */ USE [master] IF EXISTS (SELECT 1 FROM sys.server_audit_specifications WHERE name = 'STIG_AUDIT_SERVER_SPECIFICATION') ALTER SERVER AUDIT SPECIFICATION STIG_AUDIT_SERVER_SPECIFICATION WITH (STATE = OFF); IF EXISTS (SELECT 1 FROM sys.server_audit_specifications WHERE name = 'STIG_AUDIT_SERVER_SPECIFICATION') DROP SERVER AUDIT SPECIFICATION STIG_AUDIT_SERVER_SPECIFICATION; IF EXISTS (SELECT 1 FROM sys.server_audits WHERE name = 'STIG_AUDIT') ALTER SERVER AUDIT STIG_AUDIT WITH (STATE = OFF); IF EXISTS (SELECT 1 FROM sys.server_audits WHERE name = 'STIG_AUDIT') DROP SERVER AUDIT STIG_AUDIT; CREATE SERVER AUDIT STIG_AUDIT TO FILE (FILEPATH = 'C:\Audits', MAXSIZE = 200MB, MAX_ROLLOVER_FILES = 50, RESERVE_DISK_SPACE = OFF) WITH (QUEUE_DELAY = 1000, ON_FAILURE = SHUTDOWN) IF EXISTS (SELECT 1 FROM sys.server_audits WHERE name = 'STIG_AUDIT') ALTER SERVER AUDIT STIG_AUDIT WITH (STATE = ON); CREATE SERVER AUDIT SPECIFICATION STIG_AUDIT_SERVER_SPECIFICATION FOR SERVER AUDIT STIG_AUDIT ADD (APPLICATION_ROLE_CHANGE_PASSWORD_GROUP), ADD (AUDIT_CHANGE_GROUP), ADD (BACKUP_RESTORE_GROUP), ADD (DATABASE_CHANGE_GROUP), ADD (DATABASE_OBJECT_CHANGE_GROUP), ADD (DATABASE_OBJECT_OWNERSHIP_CHANGE_GROUP), ADD (DATABASE_OBJECT_PERMISSION_CHANGE_GROUP), ADD (DATABASE_OPERATION_GROUP), ADD (DATABASE_OWNERSHIP_CHANGE_GROUP), ADD (DATABASE_PERMISSION_CHANGE_GROUP), ADD (DATABASE_PRINCIPAL_CHANGE_GROUP), ADD (DATABASE_PRINCIPAL_IMPERSONATION_GROUP), ADD (DATABASE_ROLE_MEMBER_CHANGE_GROUP), ADD (DBCC_GROUP), ADD (FAILED_LOGIN_GROUP), ADD (LOGIN_CHANGE_PASSWORD_GROUP), ADD (LOGOUT_GROUP), ADD (SCHEMA_OBJECT_CHANGE_GROUP), ADD (SCHEMA_OBJECT_OWNERSHIP_CHANGE_GROUP), ADD (SCHEMA_OBJECT_PERMISSION_CHANGE_GROUP), ADD (SERVER_OBJECT_CHANGE_GROUP), ADD (SERVER_OBJECT_OWNERSHIP_CHANGE_GROUP), ADD (SERVER_OBJECT_PERMISSION_CHANGE_GROUP), ADD (SERVER_OPERATION_GROUP), ADD (SERVER_PERMISSION_CHANGE_GROUP), ADD (SERVER_PRINCIPAL_CHANGE_GROUP), ADD (SERVER_PRINCIPAL_IMPERSONATION_GROUP), ADD (SERVER_ROLE_MEMBER_CHANGE_GROUP), ADD (SERVER_STATE_CHANGE_GROUP), ADD (SUCCESSFUL_LOGIN_GROUP), ADD (TRACE_CHANGE_GROUP) WITH (STATE = ON); GO - USE [master] DECLARE @MissingAuditCount INTEGER DECLARE @server_specification_id INTEGER DECLARE @FoundCompliant INTEGER SET @FoundCompliant = 0 /* Create a table for the events that we are looking for */ CREATE TABLE #AuditEvents (AuditEvent varchar(100)) INSERT INTO #AuditEvents (AuditEvent) VALUES () /* Create a cursor to walk through all audits that are enabled at startup */ DECLARE auditspec_cursor CURSOR FOR SELECT s.server_specification_id FROM sys.server_audits a INNER JOIN sys.server_audit_specifications s ON a.audit_guid = s.audit_guid WHERE a.is_state_enabled = 1; OPEN auditspec_cursor FETCH NEXT FROM auditspec_cursor INTO @server_specification_id WHILE @@FETCH_STATUS = 0 AND @FoundCompliant = 0 /* Does this specification have the needed events in it? */ BEGIN SET @MissingAuditCount = (SELECT Count(a.AuditEvent) AS MissingAuditCount FROM #AuditEvents a JOIN sys.server_audit_specification_details d ON a.AuditEvent = d.audit_action_name WHERE d.audit_action_name NOT IN (SELECT d2.audit_action_name FROM sys.server_audit_specification_details d2 WHERE d2.server_specification_id = @server_specification_id)) IF @MissingAuditCount = 0 SET @FoundCompliant = 1; FETCH NEXT FROM auditspec_cursor INTO @server_specification_id END CLOSE auditspec_cursor; DEALLOCATE auditspec_cursor; DROP TABLE #AuditEvents /* Produce output that works with DSC - records if we do not find the audit events we are looking for */ IF @FoundCompliant > 0 SELECT name FROM sys.sql_logins WHERE principal_id = -1; ELSE SELECT name FROM sys.sql_logins WHERE principal_id = 1 - - - <VulnDiscussion>Without auditing the enforcement of access restrictions against changes to configuration, it would be difficult to identify attempted attacks and an audit trail would not be available for forensic investigation for after-the-fact actions. @@ -2989,9 +2984,9 @@ https://msdn.microsoft.com/en-us/library/cc280663.aspx - + <VulnDiscussion>Changes in the database objects (tables, views, procedures, functions) that record and control permissions, privileges, and roles granted to users and roles must be tracked. Without an audit trail, unauthorized changes to the security subsystem could go undetected. The database could be severely compromised or rendered inoperative.</VulnDiscussion><FalsePositives></FalsePositives><FalseNegatives></FalseNegatives><Documentable>false</Documentable><Mitigations></Mitigations><SeverityOverrideGuidance></SeverityOverrideGuidance><PotentialImpacts></PotentialImpacts><ThirdPartyTools></ThirdPartyTools><MitigationControl></MitigationControl><Responsibility></Responsibility><IAControls></IAControls> - V-79141 + USE [master] DECLARE @MissingAuditCount INTEGER DECLARE @server_specification_id INTEGER DECLARE @FoundCompliant INTEGER SET @FoundCompliant = 0 /* Create a table for the events that we are looking for */ CREATE TABLE #AuditEvents (AuditEvent varchar(100)) INSERT INTO #AuditEvents (AuditEvent) VALUES () /* Create a cursor to walk through all audits that are enabled at startup */ DECLARE auditspec_cursor CURSOR FOR SELECT s.server_specification_id FROM sys.server_audits a INNER JOIN sys.server_audit_specifications s ON a.audit_guid = s.audit_guid WHERE a.is_state_enabled = 1; OPEN auditspec_cursor FETCH NEXT FROM auditspec_cursor INTO @server_specification_id WHILE @@FETCH_STATUS = 0 AND @FoundCompliant = 0 /* Does this specification have the needed events in it? */ BEGIN SET @MissingAuditCount = (SELECT Count(a.AuditEvent) AS MissingAuditCount FROM #AuditEvents a JOIN sys.server_audit_specification_details d ON a.AuditEvent = d.audit_action_name WHERE d.audit_action_name NOT IN (SELECT d2.audit_action_name FROM sys.server_audit_specification_details d2 WHERE d2.server_specification_id = @server_specification_id)) IF @MissingAuditCount = 0 SET @FoundCompliant = 1; FETCH NEXT FROM auditspec_cursor INTO @server_specification_id END CLOSE auditspec_cursor; DEALLOCATE auditspec_cursor; DROP TABLE #AuditEvents /* Produce output that works with DSC - records if we do not find the audit events we are looking for */ IF @FoundCompliant > 0 SELECT name FROM sys.sql_logins WHERE principal_id = -1; ELSE SELECT name FROM sys.sql_logins WHERE principal_id = 1 False False @@ -3026,7 +3021,7 @@ If the "SCHEMA_OBJECT_CHANGE_GROUP" is not returned in an active audit, this is <VulnDiscussion>Changes in the database objects (tables, views, procedures, functions) that record and control permissions, privileges, and roles granted to users and roles must be tracked. Without an audit trail, unauthorized changes to the security subsystem could go undetected. The database could be severely compromised or rendered inoperative. To aid in diagnosis, it is necessary to keep track of failed attempts in addition to the successful ones.</VulnDiscussion><FalsePositives></FalsePositives><FalseNegatives></FalseNegatives><Documentable>false</Documentable><Mitigations></Mitigations><SeverityOverrideGuidance></SeverityOverrideGuidance><PotentialImpacts></PotentialImpacts><ThirdPartyTools></ThirdPartyTools><MitigationControl></MitigationControl><Responsibility></Responsibility><IAControls></IAControls> - V-79141 + V-79267 USE [master] DECLARE @MissingAuditCount INTEGER DECLARE @server_specification_id INTEGER DECLARE @FoundCompliant INTEGER SET @FoundCompliant = 0 /* Create a table for the events that we are looking for */ CREATE TABLE #AuditEvents (AuditEvent varchar(100)) INSERT INTO #AuditEvents (AuditEvent) VALUES () /* Create a cursor to walk through all audits that are enabled at startup */ DECLARE auditspec_cursor CURSOR FOR SELECT s.server_specification_id FROM sys.server_audits a INNER JOIN sys.server_audit_specifications s ON a.audit_guid = s.audit_guid WHERE a.is_state_enabled = 1; OPEN auditspec_cursor FETCH NEXT FROM auditspec_cursor INTO @server_specification_id WHILE @@FETCH_STATUS = 0 AND @FoundCompliant = 0 /* Does this specification have the needed events in it? */ BEGIN SET @MissingAuditCount = (SELECT Count(a.AuditEvent) AS MissingAuditCount FROM #AuditEvents a JOIN sys.server_audit_specification_details d ON a.AuditEvent = d.audit_action_name WHERE d.audit_action_name NOT IN (SELECT d2.audit_action_name FROM sys.server_audit_specification_details d2 WHERE d2.server_specification_id = @server_specification_id)) IF @MissingAuditCount = 0 SET @FoundCompliant = 1; FETCH NEXT FROM auditspec_cursor INTO @server_specification_id END CLOSE auditspec_cursor; DEALLOCATE auditspec_cursor; DROP TABLE #AuditEvents /* Produce output that works with DSC - records if we do not find the audit events we are looking for */ IF @FoundCompliant > 0 SELECT name FROM sys.sql_logins WHERE principal_id = -1; ELSE SELECT name FROM sys.sql_logins WHERE principal_id = 1 False False @@ -3125,7 +3120,7 @@ https://msdn.microsoft.com/en-us/library/cc280663.aspx <VulnDiscussion>The removal of security objects from the database/DBMS would seriously degrade a system's information assurance posture. If such an event occurs, it must be logged.</VulnDiscussion><FalsePositives></FalsePositives><FalseNegatives></FalseNegatives><Documentable>false</Documentable><Mitigations></Mitigations><SeverityOverrideGuidance></SeverityOverrideGuidance><PotentialImpacts></PotentialImpacts><ThirdPartyTools></ThirdPartyTools><MitigationControl></MitigationControl><Responsibility></Responsibility><IAControls></IAControls> - V-79141 + V-79267 USE [master] DECLARE @MissingAuditCount INTEGER DECLARE @server_specification_id INTEGER DECLARE @FoundCompliant INTEGER SET @FoundCompliant = 0 /* Create a table for the events that we are looking for */ CREATE TABLE #AuditEvents (AuditEvent varchar(100)) INSERT INTO #AuditEvents (AuditEvent) VALUES () /* Create a cursor to walk through all audits that are enabled at startup */ DECLARE auditspec_cursor CURSOR FOR SELECT s.server_specification_id FROM sys.server_audits a INNER JOIN sys.server_audit_specifications s ON a.audit_guid = s.audit_guid WHERE a.is_state_enabled = 1; OPEN auditspec_cursor FETCH NEXT FROM auditspec_cursor INTO @server_specification_id WHILE @@FETCH_STATUS = 0 AND @FoundCompliant = 0 /* Does this specification have the needed events in it? */ BEGIN SET @MissingAuditCount = (SELECT Count(a.AuditEvent) AS MissingAuditCount FROM #AuditEvents a JOIN sys.server_audit_specification_details d ON a.AuditEvent = d.audit_action_name WHERE d.audit_action_name NOT IN (SELECT d2.audit_action_name FROM sys.server_audit_specification_details d2 WHERE d2.server_specification_id = @server_specification_id)) IF @MissingAuditCount = 0 SET @FoundCompliant = 1; FETCH NEXT FROM auditspec_cursor INTO @server_specification_id END CLOSE auditspec_cursor; DEALLOCATE auditspec_cursor; DROP TABLE #AuditEvents /* Produce output that works with DSC - records if we do not find the audit events we are looking for */ IF @FoundCompliant > 0 SELECT name FROM sys.sql_logins WHERE principal_id = -1; ELSE SELECT name FROM sys.sql_logins WHERE principal_id = 1 False False @@ -3160,7 +3155,7 @@ If the "SCHEMA_OBJECT_CHANGE_GROUP" is not returned in an active audit, this is <VulnDiscussion>The removal of security objects from the database/DBMS would seriously degrade a system's information assurance posture. If such an action is attempted, it must be logged. To aid in diagnosis, it is necessary to keep track of failed attempts in addition to the successful ones.</VulnDiscussion><FalsePositives></FalsePositives><FalseNegatives></FalseNegatives><Documentable>false</Documentable><Mitigations></Mitigations><SeverityOverrideGuidance></SeverityOverrideGuidance><PotentialImpacts></PotentialImpacts><ThirdPartyTools></ThirdPartyTools><MitigationControl></MitigationControl><Responsibility></Responsibility><IAControls></IAControls> - V-79141 + V-79267 USE [master] DECLARE @MissingAuditCount INTEGER DECLARE @server_specification_id INTEGER DECLARE @FoundCompliant INTEGER SET @FoundCompliant = 0 /* Create a table for the events that we are looking for */ CREATE TABLE #AuditEvents (AuditEvent varchar(100)) INSERT INTO #AuditEvents (AuditEvent) VALUES () /* Create a cursor to walk through all audits that are enabled at startup */ DECLARE auditspec_cursor CURSOR FOR SELECT s.server_specification_id FROM sys.server_audits a INNER JOIN sys.server_audit_specifications s ON a.audit_guid = s.audit_guid WHERE a.is_state_enabled = 1; OPEN auditspec_cursor FETCH NEXT FROM auditspec_cursor INTO @server_specification_id WHILE @@FETCH_STATUS = 0 AND @FoundCompliant = 0 /* Does this specification have the needed events in it? */ BEGIN SET @MissingAuditCount = (SELECT Count(a.AuditEvent) AS MissingAuditCount FROM #AuditEvents a JOIN sys.server_audit_specification_details d ON a.AuditEvent = d.audit_action_name WHERE d.audit_action_name NOT IN (SELECT d2.audit_action_name FROM sys.server_audit_specification_details d2 WHERE d2.server_specification_id = @server_specification_id)) IF @MissingAuditCount = 0 SET @FoundCompliant = 1; FETCH NEXT FROM auditspec_cursor INTO @server_specification_id END CLOSE auditspec_cursor; DEALLOCATE auditspec_cursor; DROP TABLE #AuditEvents /* Produce output that works with DSC - records if we do not find the audit events we are looking for */ IF @FoundCompliant > 0 SELECT name FROM sys.sql_logins WHERE principal_id = -1; ELSE SELECT name FROM sys.sql_logins WHERE principal_id = 1 False False @@ -3234,7 +3229,7 @@ If "Both failed and successful logins" is not selected, this is a finding. <VulnDiscussion>For completeness of forensic analysis, it is necessary to track failed attempts to log on to SQL Server. While positive identification may not be possible in a case of failed authentication, as much information as possible about the incident must be captured.</VulnDiscussion><FalsePositives></FalsePositives><FalseNegatives></FalseNegatives><Documentable>false</Documentable><Mitigations></Mitigations><SeverityOverrideGuidance></SeverityOverrideGuidance><PotentialImpacts></PotentialImpacts><ThirdPartyTools></ThirdPartyTools><MitigationControl></MitigationControl><Responsibility></Responsibility><IAControls></IAControls> - V-79141 + V-79267 USE [master] DECLARE @MissingAuditCount INTEGER DECLARE @server_specification_id INTEGER DECLARE @FoundCompliant INTEGER SET @FoundCompliant = 0 /* Create a table for the events that we are looking for */ CREATE TABLE #AuditEvents (AuditEvent varchar(100)) INSERT INTO #AuditEvents (AuditEvent) VALUES () /* Create a cursor to walk through all audits that are enabled at startup */ DECLARE auditspec_cursor CURSOR FOR SELECT s.server_specification_id FROM sys.server_audits a INNER JOIN sys.server_audit_specifications s ON a.audit_guid = s.audit_guid WHERE a.is_state_enabled = 1; OPEN auditspec_cursor FETCH NEXT FROM auditspec_cursor INTO @server_specification_id WHILE @@FETCH_STATUS = 0 AND @FoundCompliant = 0 /* Does this specification have the needed events in it? */ BEGIN SET @MissingAuditCount = (SELECT Count(a.AuditEvent) AS MissingAuditCount FROM #AuditEvents a JOIN sys.server_audit_specification_details d ON a.AuditEvent = d.audit_action_name WHERE d.audit_action_name NOT IN (SELECT d2.audit_action_name FROM sys.server_audit_specification_details d2 WHERE d2.server_specification_id = @server_specification_id)) IF @MissingAuditCount = 0 SET @FoundCompliant = 1; FETCH NEXT FROM auditspec_cursor INTO @server_specification_id END CLOSE auditspec_cursor; DEALLOCATE auditspec_cursor; DROP TABLE #AuditEvents /* Produce output that works with DSC - records if we do not find the audit events we are looking for */ IF @FoundCompliant > 0 SELECT name FROM sys.sql_logins WHERE principal_id = -1; ELSE SELECT name FROM sys.sql_logins WHERE principal_id = 1 False False @@ -3506,7 +3501,7 @@ If the identified groups are not returned, this is a finding. Concurrent connections by the same user from multiple workstations may be valid use of the system; or such connections may be due to improper circumvention of the requirement to use the CAC for authentication; or they may indicate unauthorized account sharing; or they may be because an account has been compromised. (If the fact of multiple, concurrent logons by a given user can be reliably reconstructed from the log entries for other events (logons/connections; voluntary and involuntary disconnections), then it is not mandatory to create additional log entries specifically for this.)</VulnDiscussion><FalsePositives></FalsePositives><FalseNegatives></FalseNegatives><Documentable>false</Documentable><Mitigations></Mitigations><SeverityOverrideGuidance></SeverityOverrideGuidance><PotentialImpacts></PotentialImpacts><ThirdPartyTools></ThirdPartyTools><MitigationControl></MitigationControl><Responsibility></Responsibility><IAControls></IAControls> - V-79141 + V-79267 USE [master] DECLARE @MissingAuditCount INTEGER DECLARE @server_specification_id INTEGER DECLARE @FoundCompliant INTEGER SET @FoundCompliant = 0 /* Create a table for the events that we are looking for */ CREATE TABLE #AuditEvents (AuditEvent varchar(100)) INSERT INTO #AuditEvents (AuditEvent) VALUES () /* Create a cursor to walk through all audits that are enabled at startup */ DECLARE auditspec_cursor CURSOR FOR SELECT s.server_specification_id FROM sys.server_audits a INNER JOIN sys.server_audit_specifications s ON a.audit_guid = s.audit_guid WHERE a.is_state_enabled = 1; OPEN auditspec_cursor FETCH NEXT FROM auditspec_cursor INTO @server_specification_id WHILE @@FETCH_STATUS = 0 AND @FoundCompliant = 0 /* Does this specification have the needed events in it? */ BEGIN SET @MissingAuditCount = (SELECT Count(a.AuditEvent) AS MissingAuditCount FROM #AuditEvents a JOIN sys.server_audit_specification_details d ON a.AuditEvent = d.audit_action_name WHERE d.audit_action_name NOT IN (SELECT d2.audit_action_name FROM sys.server_audit_specification_details d2 WHERE d2.server_specification_id = @server_specification_id)) IF @MissingAuditCount = 0 SET @FoundCompliant = 1; FETCH NEXT FROM auditspec_cursor INTO @server_specification_id END CLOSE auditspec_cursor; DEALLOCATE auditspec_cursor; DROP TABLE #AuditEvents /* Produce output that works with DSC - records if we do not find the audit events we are looking for */ IF @FoundCompliant > 0 SELECT name FROM sys.sql_logins WHERE principal_id = -1; ELSE SELECT name FROM sys.sql_logins WHERE principal_id = 1 False False diff --git a/Tests/Integration/PowerStig.Integration.tests.ps1 b/Tests/Integration/PowerStig.Integration.tests.ps1 index b638018c0..cb1bb70f1 100644 --- a/Tests/Integration/PowerStig.Integration.tests.ps1 +++ b/Tests/Integration/PowerStig.Integration.tests.ps1 @@ -37,7 +37,7 @@ Describe "$moduleName module" { Context 'Exported Commands' { $commands = (Get-Command -Module $moduleName).Name - $exportedCommands = @('Get-DomainName', 'Get-Stig', 'New-StigCheckList') + $exportedCommands = @('Get-DomainName', 'Get-Stig', 'New-StigCheckList', 'Get-StigRuleList', 'Get-StigVersionNumber', 'Get-PowerStigFileList', 'Split-BenchmarkId') foreach ($export in $exportedCommands) { diff --git a/Tests/Unit/Module/STIG.tests.ps1 b/Tests/Unit/Module/STIG.tests.ps1 index 5ddb5c2db..5f79ae8fc 100644 --- a/Tests/Unit/Module/STIG.tests.ps1 +++ b/Tests/Unit/Module/STIG.tests.ps1 @@ -82,7 +82,6 @@ try #> } Describe 'Split-BenchmarkId' { - $sampleStrings = [ordered]@{ 'SQLServer' = @( @{