-
Notifications
You must be signed in to change notification settings - Fork 116
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update PowerSTIG to Provide Rule Data from Processed xml (#777)
* create tooling function for rule query by end user * update changelog.md * update new functions to dsc guideline standards * deving exception string tooling * update function to address u009D in description * added vulnId to non-detailed output * merged local with 4.6.0 * updated functions, tests are outstanding * updated tests. * added tests for RuleQuery functions. * update build.yaml to skip broke git changelog test * mod build.yaml to correct exclusion for changelog * updated test based on feedback
- Loading branch information
Showing
7 changed files
with
360 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
#region Header | ||
. $PSScriptRoot\.tests.header.ps1 | ||
#endregion | ||
|
||
$xmlTestData = @' | ||
<DISASTIG version="1" classification="UNCLASSIFIED" customname="" stigid="TestSTIGData" description="Test STIG Data" filename="U_Test_Data_STIG_V1R1_Manual-xccdf.xml" releaseinfo="Release: 1 Benchmark Date: 14 Nov 2016" title="Test STIG Data Security Technical Implementation Guide" notice="terms-of-use" source="STIG.DOD.MIL" fullversion="1.1" created="9/6/2019"> | ||
<RegistryRule dscresourcemodule="PSDscResources"> | ||
<Rule id="V-1000" severity="medium" conversionstatus="pass" title="SRG-APP-000000" dscresource="Registry"> | ||
<Description><VulnDiscussion>Test STIG Description</VulnDiscussion><</Description> | ||
<DuplicateOf /> | ||
<Ensure>Present</Ensure> | ||
<IsNullOrEmpty>False</IsNullOrEmpty> | ||
<Key>HKEY_LOCAL_MACHINE\Software\Microsoft\TestKeyData</Key> | ||
<OrganizationValueRequired>False</OrganizationValueRequired> | ||
<OrganizationValueTestString /> | ||
<RawString>Test Data RawString</RawString> | ||
<ValueData>TestValueData</ValueData> | ||
<ValueName>TestValueName</ValueName> | ||
<ValueType>String</ValueType> | ||
</Rule> | ||
</RegistryRule> | ||
</DISASTIG> | ||
'@ | ||
|
||
$exceptionString = "V-1000 = @{Ensure = 'Present'; Key = 'HKEY_LOCAL_MACHINE\Software\Microsoft\TestKeyData'; ValueData = 'TestValueData'; ValueName = 'TestValueName'; ValueType = 'String'}" | ||
|
||
|
||
try | ||
{ | ||
Describe 'Rule Query Functions' { | ||
|
||
$testProcessedXml = Join-Path -Path $TestDrive -ChildPath 'TestProcessedXml.xml' | ||
Set-Content -Path $testProcessedXml -Value $xmlTestData | ||
|
||
Context 'Get-StigRule' { | ||
It 'Should return a V-1000 Rule PSCustomObject Non-Detailed' { | ||
$getStigRuleResult = Get-StigRule -VulnId 'V-1000' -ProcessedXmlPath $testProcessedXml | ||
$getStigRuleResult.RuleType | Should -Be 'RegistryRule' | ||
$getStigRuleResult.VulnId | Should -Be 'V-1000' | ||
$getStigRuleResult.Ensure | Should -Be 'Present' | ||
$getStigRuleResult.Key | Should -Be 'HKEY_LOCAL_MACHINE\Software\Microsoft\TestKeyData' | ||
$getStigRuleResult.ValueData | Should -Be 'TestValueData' | ||
$getStigRuleResult.ValueName | Should -Be 'TestValueName' | ||
$getStigRuleResult.ValueType | Should -Be 'String' | ||
} | ||
|
||
It 'Should return a V-1000 Rule PSCustomObject Detailed' { | ||
$getStigRuleResult = Get-StigRule -VulnId 'V-1000' -ProcessedXmlPath $testProcessedXml -Detailed | ||
$getStigRuleResult.StigId | Should -Be 'TestSTIGData' | ||
$getStigRuleResult.StigVersion | Should -Be '1.1' | ||
$getStigRuleResult.Severity | Should -Be 'medium' | ||
$getStigRuleResult.Title | Should -Be 'SRG-APP-000000' | ||
$getStigRuleResult.Description | Should -Be 'Test STIG Description' | ||
$getStigRuleResult.RuleType | Should -Be 'RegistryRule' | ||
$getStigRuleResult.DscResource | Should -Be 'Registry' | ||
$getStigRuleResult.DuplicateOf | Should -Be $([string]::Empty) | ||
$getStigRuleResult.OrganizationValueRequired | Should -Be 'False' | ||
$getStigRuleResult.OrganizationValueTestString | Should -Be $([string]::Empty) | ||
$getStigRuleResult.VulnId | Should -Be 'V-1000' | ||
$getStigRuleResult.Ensure | Should -Be 'Present' | ||
$getStigRuleResult.Key | Should -Be 'HKEY_LOCAL_MACHINE\Software\Microsoft\TestKeyData' | ||
$getStigRuleResult.ValueData | Should -Be 'TestValueData' | ||
$getStigRuleResult.ValueName | Should -Be 'TestValueName' | ||
$getStigRuleResult.ValueType | Should -Be 'String' | ||
} | ||
} | ||
|
||
Context 'Get-StigRuleExceptionString' { | ||
It 'Should return a valid unformatted exception string' { | ||
$ruleData = Get-StigRule -VulnId 'V-1000' -ProcessedXmlPath $testProcessedXml | ||
$getStigRuleExceptionString = Get-StigRuleExceptionString -Rule $ruleData | ||
$getStigRuleExceptionString | Should -Be $exceptionString | ||
} | ||
|
||
It 'Should return a valid formatted exception string' { | ||
$ruleData = Get-StigRule -VulnId 'V-1000' -ProcessedXmlPath $testProcessedXml | ||
$getStigRuleExceptionStringFormatted = Get-StigRuleExceptionString -Rule $ruleData -Formatted | ||
$getStigRuleExceptionStringFormatted | Should -BeOfType System.String | ||
} | ||
} | ||
} | ||
} | ||
finally | ||
{ | ||
. $PSScriptRoot\.tests.footer.ps1 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,246 @@ | ||
using module ..\Rule\Rule.psm1 | ||
|
||
<# | ||
.SYNOPSIS | ||
Get the STIG Rule Details for a given rule supported by PowerSTIG. | ||
.DESCRIPTION | ||
Get the STIG Rule Details for a given rule supported by PowerSTIG. | ||
.PARAMETER VulnId | ||
VulnId within PowerSTIG is typically labled as the RuleId, which | ||
may not be consistent with DISA terminology. | ||
.PARAMETER ProcessedXmlPath | ||
Either the folder where the processed xml resides or a specific xml path. | ||
The default is .\StigData\Processed\*.xml | ||
.EXAMPLE | ||
PS> Get-StigRule -VulnId 'V-1114', 'V-1115' | ||
This example will return the rule details for V-1114 and V-1115 from the Windows Server | ||
2012 R2 Member Server and Domain Controller STIGs. | ||
#> | ||
function Get-StigRule | ||
{ | ||
[CmdletBinding()] | ||
[OutputType([PSCustomObject])] | ||
param | ||
( | ||
[Parameter(Mandatory = $true, Position = 0)] | ||
[ValidateScript({$_ -match '^V-\d{1,}(|\.[a-z])$'})] | ||
[Alias("RuleId")] | ||
[string[]] | ||
$VulnId, | ||
|
||
[Parameter()] | ||
[ValidateScript({Test-Path -Path $_})] | ||
[string] | ||
$ProcessedXmlPath = (Join-Path -Path $PSScriptRoot -ChildPath '..\..\StigData\Processed\*.xml'), | ||
|
||
[Parameter()] | ||
[switch] | ||
$Detailed | ||
) | ||
|
||
$processedXml = Select-String -Path $ProcessedXmlPath -Pattern $VulnId -Exclude '*.org.default.xml' | Sort-Object -Property Pattern | ||
|
||
if ($null -eq $processedXml) | ||
{ | ||
Write-Warning -Message "The VulnId(s) specified were not found in $ProcessedXmlPath" | ||
return | ||
} | ||
|
||
# hashtable to store rule property lookups when multiple rule types are specified | ||
$ruleTypeProperty = @{} | ||
|
||
foreach ($technologyXml in $processedXml) | ||
{ | ||
# based on the VulnId specificed use XPath to search the xml object | ||
$ruleIdXPath = '//Rule[@id = "{0}"]' -f $technologyXml.Pattern | ||
[xml] $xml = Get-Content -Path $technologyXml.Path | ||
$ruleData = $xml.DISASTIG.SelectNodes($ruleIdXPath) | ||
$ruleType = $ruleData.ParentNode.ToString() | ||
|
||
# if the current rule type is not stored in the hashtable, run Get-UniqueRuleTypeProperty and store the results for future use | ||
if (-not $ruleTypeProperty.ContainsKey($ruleType)) | ||
{ | ||
$uniqueRuleTypeProperty = Get-UniqueRuleTypeProperty -Rule $ruleData | ||
$ruleTypeProperty.Add($ruleType, $uniqueRuleTypeProperty) | ||
} | ||
|
||
# pulling the VulnDiscussion as the description out of the xml using a regex capture group | ||
$ruleDescriptionMatch = [regex]::Match($ruleData.description.Replace("`n", ' '), '<VulnDiscussion>(?<description>.*)<\/VulnDiscussion>') | ||
|
||
# address edge case where an out of place OS Control charactor [char]157 in the STIG's description, i.e. Adobe Reader / V-64919, removing it | ||
$ruleDescriptionValue = $ruleDescriptionMatch.Groups.Item('description').Value -replace '\u009D' | ||
|
||
# using PSv3 "ordered" to create an ordered hashtable for PSCustomObject property list display order | ||
if ($PSBoundParameters.ContainsKey('Detailed')) | ||
{ | ||
$ruleDetail = [ordered] @{ | ||
StigId = $xml.DISASTIG.stigid | ||
StigVersion = $xml.DISASTIG.fullversion | ||
VulnId = $ruleData.id | ||
Severity = $ruleData.severity | ||
Title = $ruleData.title | ||
Description = $ruleDescriptionValue | ||
RuleType = $ruleType | ||
DscResource = $ruleData.dscresource | ||
DuplicateOf = $ruleData.DuplicateOf | ||
OrganizationValueRequired = $ruleData.OrganizationValueRequired | ||
OrganizationValueTestString = $ruleData.OrganizationValueTestString | ||
} | ||
} | ||
else | ||
{ | ||
$ruleDetail = [ordered] @{ | ||
RuleType = $ruleType | ||
VulnId = $ruleData.id | ||
} | ||
} | ||
|
||
# adding the rule specific properties to the ordered hashtable and then casting to PSCustomObject | ||
foreach ($value in $ruleTypeProperty[$ruleType]) | ||
{ | ||
$ruleDetail.Add($value, $ruleData.$value) | ||
} | ||
|
||
[PSCustomObject] $ruleDetail | ||
} | ||
} | ||
|
||
<# | ||
.SYNOPSIS | ||
Get the unique rule type properties given a specific rule type. | ||
.DESCRIPTION | ||
Get the unique rule type properties given a specific rule type. | ||
.PARAMETER Rule | ||
A rule by leveraging the selected XmlNodeList from a processed xml. | ||
.EXAMPLE | ||
PS> Get-UniqueRuleTypeProperty -Rule $xml.DISASTIG.RegistryRule.Rule[0] | ||
Returns the delta properties between the RegistryRule and Base Rule class | ||
#> | ||
function Get-UniqueRuleTypeProperty | ||
{ | ||
[CmdletBinding()] | ||
[OutputType([string[]])] | ||
param | ||
( | ||
[Parameter(Mandatory = $true)] | ||
[Object] | ||
$Rule | ||
) | ||
|
||
$blankRule = New-Object -TypeName Rule | ||
$commonProperties = ($blankRule | Get-Member -MemberType Property).Name | ||
$ruleProperty = ($Rule | Get-Member -MemberType 'NoteProperty', 'Property').Name | ||
$compareObjResult = Compare-Object -ReferenceObject $ruleProperty -DifferenceObject $commonProperties | ||
$filteredCompareResult = $compareObjResult | Where-Object -FilterScript {$PSItem.SideIndicator -eq '<=' -and $PSItem -notmatch 'Stig(Id|Version)|VulnId|RuleType'} | ||
return $filteredCompareResult.InputObject | ||
} | ||
|
||
<# | ||
.SYNOPSIS | ||
Returns a string of all properties of a given rule and structured for use within a PowerSTIG configuraiton. | ||
.DESCRIPTION | ||
Returns a string of all properties of a given rule and structured for use within a PowerSTIG configuraiton. | ||
.PARAMETER Rule | ||
A rule object which was created through Get-StigRule | ||
.PARAMETER Formatted | ||
By default the function will return a single line string which represents the rule exception, when Formatted | ||
is supplied, the funciton will return a formatted string, i.e.: | ||
V-1155 = @{ | ||
Constant = 'SeDenyNetworkLogonRight' | ||
DisplayName = 'Deny access to this computer from the network' | ||
Force = 'False' | ||
Identity = '' | ||
} | ||
.EXAMPLE | ||
PS> $rule = Get-StigRule -RuleId V-1155 | Select-Object -First 1 | ||
PS> Get-StigRuleExceptionString -Rule $rule | ||
Returns the following exception string: | ||
V-1155 = @{Constant = 'SeDenyNetworkLogonRight'; DisplayName = 'Deny access to this computer from the network'; Force = 'False'; Identity = ''} | ||
.EXAMPLE | ||
PS> $rule = Get-StigRule -RuleId V-1155 | Select-Object -First 1 | ||
PS> Get-StigRuleExceptionString -Rule $rule -Formatted | ||
Returns the following exception string: | ||
V-1155 = @{ | ||
Constant = 'SeDenyNetworkLogonRight' | ||
DisplayName = 'Deny access to this computer from the network' | ||
Force = 'False' | ||
Identity = '' | ||
} | ||
#> | ||
function Get-StigRuleExceptionString | ||
{ | ||
[CmdletBinding()] | ||
[OutputType([string])] | ||
param | ||
( | ||
[Parameter(Mandatory = $true, ValueFromPipeline = $true)] | ||
[PSObject[]] | ||
$Rule, | ||
|
||
[Parameter()] | ||
[switch] | ||
$Formatted | ||
) | ||
|
||
begin | ||
{ | ||
$ruleTypeProperty = @{} | ||
} | ||
|
||
process | ||
{ | ||
foreach ($ruleData in $Rule) | ||
{ | ||
if (-not $ruleTypeProperty.ContainsKey($ruleData.RuleType)) | ||
{ | ||
$uniqueRuleTypeProperty = Get-UniqueRuleTypeProperty -Rule $ruleData | ||
$ruleTypeProperty.Add($ruleData.RuleType, $uniqueRuleTypeProperty) | ||
} | ||
|
||
$ruleDetail = [ordered] @{} | ||
|
||
foreach ($value in $ruleTypeProperty[$ruleData.RuleType]) | ||
{ | ||
if ($value -ne $ruleData.RuleType) | ||
{ | ||
$ruleDetail.Add($value, $ruleData.$value) | ||
} | ||
} | ||
|
||
$exceptionString = New-Object -TypeName System.Text.StringBuilder | ||
[void] $exceptionString.Append("$($ruleData.VulnId) = @{") | ||
foreach ($key in $ruleDetail.Keys) | ||
{ | ||
[void] $exceptionString.Append("$key = '$($ruleDetail[$key])'; ") | ||
} | ||
|
||
$exceptionString = $exceptionString.ToString() -replace ';\s$', '}' | ||
|
||
if ($Formatted) | ||
{ | ||
$exceptionString -replace ';\s?', "`n " -replace '@{', "@{`n " -replace '}', "`n}" | ||
} | ||
else | ||
{ | ||
$exceptionString | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.