Module/Document.Main/Function.NewStigCheckList.ps1

<#
    .SYNOPSIS
        Automatically creates a Stig Viewer checklist from the DSC results or
        compiled MOF
    .PARAMETER ReferenceConfiguration
        The MOF that was compiled with a PowerStig composite
    .PARAMETER DscResult
        The resutls of Test-DscConfiguration
    .PARAMETER XccdfPath
        The path to the matching xccdf file. This is currently needed since we
        do not pull add xccdf data into PowerStig
    .PARAMETER OutputPath
        The location you want the checklist saved to
    .PARAMETER Enforcement
         Flag to add additional checklist metadata
    .EXAMPLE
        New-StigCheckList -ReferenceConfiguration $ReferenceConfiguration -XccdfPath $XccdfPath -OutputPath $outputPath -Enforcement DSC
#>

function New-StigCheckList
{
    [CmdletBinding()]
    [OutputType([xml])]
    param
    (
        [Parameter(Mandatory = $true, ParameterSetName = 'mof')]
        [string]
        $ReferenceConfiguration,

        [Parameter(Mandatory = $true, ParameterSetName = 'result')]
        [PSCustomObject]
        $DscResult,

        [Parameter(Mandatory = $true)]
        [string]
        $XccdfPath,

        [Parameter(Mandatory = $true)]
        [string]
        $OutputPath,

        [Parameter(Mandatory = $true)]
        [ValidateSet('DSC', 'GPO')]
        [string]
        $Enforcement
    )

    #region Checklist Root

    # Start the XML doc
    $settings = [System.Xml.XmlWriterSettings]::new()
    $settings.Indent = $true
    $writer = [System.Xml.XmlWriter]::Create($OutputPath, $settings)

    $writer.WriteStartElement('CHECKLIST')

    $writer.WriteStartElement("ASSET")

    $assetElements = [ordered] @{
        'ROLE'            = 'None'
        'ASSET_TYPE'      = 'Computing'
        'HOST_NAME'       = ''
        'HOST_IP'         = ''
        'HOST_MAC'        = ''
        'HOST_GUID'       = ''
        'HOST_FQDN'       = ''
        'TECH_AREA'       = ''
        'TARGET_KEY'      = ''
        'WEB_OR_DATABASE' = $false
        'WEB_DB_SITE'     = ''
        'WEB_DB_INSTANCE' = ''
    }

    foreach ($assetElement in $assetElements.GetEnumerator())
    {
        $writer.WriteStartElement($assetElement.name)
        $writer.WriteString($assetElement.value)
        $writer.WriteEndElement()
    }

    $writer.WriteEndElement(<#ASSET#>)
    $writer.WriteStartElement("STIGS")
    $writer.WriteStartElement("iSTIG")
    $writer.WriteStartElement("STIG_INFO")

    #endregion

    #region SI Data

    $xccdfBenchmarkContent = Get-StigXccdfBenchmarkContent -Path $XccdfPath

    $StigInfoElements = [ordered] @{
        'version'        = "$($xccdfBenchmarkContent.version)"
        'classification' = 'UNCLASSIFIED'
        'customname'     = ''
        'stigid'         = "$($xccdfBenchmarkContent.id)"
        'description'    = "$($xccdfBenchmarkContent.description)"
        'filename'       = "$(Split-Path -Path $XccdfPath -Leaf)"
        'releaseinfo'    = "$($xccdfBenchmarkContent.'plain-text'.InnerText)"
        'title'          = "$($xccdfBenchmarkContent.title)"
        'uuid'           = "$((New-Guid).Guid)"
        'notice'         = "$($xccdfBenchmarkContent.notice.InnerText)"
        'source'         = "$($xccdfBenchmarkContent.reference.source)"
    }

    foreach ($StigInfoElement in $StigInfoElements.GetEnumerator())
    {
        $writer.WriteStartElement("SI_DATA")

            $writer.WriteStartElement('SID_NAME')
            $writer.WriteString($StigInfoElement.name)
            $writer.WriteEndElement(<#SID_NAME#>)

            $writer.WriteStartElement('SID_DATA')
            $writer.WriteString($StigInfoElement.value)
            $writer.WriteEndElement(<#SID_DATA#>)

        $writer.WriteEndElement(<#SI_DATA#>)
    }

    $writer.WriteEndElement(<#STIG_INFO#>)

    #endregion
    #region Vulnerability

    $vulnerabilities = Get-VulnerabilityList -XccdfBenchmark $xccdfBenchmarkContent

    foreach ( $vulnerability in $vulnerabilities )
    {
        $writer.WriteStartElement("VULN")

        foreach ($attribute in $vulnerability.GetEnumerator())
        {
            $status = $null
            $comments = $null

            if ($attribute.Name -eq 'Vuln_Num')
            {
                $vid = $attribute.Value
            }

            $writer.WriteStartElement("STIG_DATA")

                $writer.WriteStartElement("VULN_ATTRIBUTE")
                $writer.WriteString($attribute.Name)
                $writer.WriteEndElement()

                $writer.WriteStartElement("ATTRIBUTE_DATA")
                $writer.WriteString($attribute.Value)
                $writer.WriteEndElement()

            $writer.WriteEndElement(<#STIG_DATA#>)
        }

        if ($PSCmdlet.ParameterSetName -eq 'mof')
        {
            $setting = Get-SettingsFromMof -ReferenceConfiguration $ReferenceConfiguration -Id $vid

            if ($setting)
            {
                $status = 'NotAFinding'
                $comments = 'Managed via PowerStigDsc'
            }
            else
            {
                $status = 'NotReviewed'
            }
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'result')
        {
            $setting = Get-SettingsFromResult -DscResult $DscResult -Id $vid

            if ($setting)
            {
                if ($setting.InDesiredState)
                {
                    $status = 'NotAFinding'
                }
                else
                {
                    $status = 'Open'
                }

                $comments = 'Managed via PowerStigDsc from Live call'
            }
            else
            {
                $status = 'NotReviewed'
            }
        }

        $writer.WriteStartElement("STATUS")
        $writer.WriteString($status)
        $writer.WriteEndElement(<#STATUS#>)

        $writer.WriteStartElement("FINDING_DETAILS")
        $writer.WriteString( ( Get-FindingDetails -Setting $setting ) )
        $writer.WriteEndElement(<#FINDING_DETAILS#>)

        $writer.WriteStartElement("COMMENTS")
        $writer.WriteString($comments)
        $writer.WriteEndElement(<#COMMENTS#>)

        $writer.WriteStartElement("SEVERITY_OVERRIDE")
        $writer.WriteString('')
        $writer.WriteEndElement(<#SEVERITY_OVERRIDE#>)

        $writer.WriteStartElement("SEVERITY_JUSTIFICATION")
        $writer.WriteString('')
        $writer.WriteEndElement(<#SEVERITY_JUSTIFICATION#>)

        $writer.WriteEndElement(<#VULN#>)
    }
    #endregion

    $writer.WriteEndElement(<#iSTIG#>)
    $writer.WriteEndElement(<#STIGS#>)
    $writer.WriteEndElement(<#CHECKLIST#>)
    $writer.Flush()
    $writer.Close()
}

<#
    .SYNOPSIS
        Gets the vulnerability details from the rule description
#>

function Get-VulnerabilityList
{
    [CmdletBinding()]
    [OutputType([xml])]
    param
    (
        [Parameter()]
        [psobject]
        $XccdfBenchmark
    )

    [System.Collections.ArrayList] $vulnerabilityList = @()

    foreach ( $vulnerability in $XccdfBenchmark.Group )
    {
        [xml]$vulnerabiltyDiscussionElement = "<discussionroot>$($vulnerability.Rule.description)</discussionroot>"

        [void] $vulnerabilityList.Add(
            [ordered]@{
                'Vuln_Num'                      = "$($vulnerability.id)"
                'Severity'                      = "$($vulnerability.Rule.severity)"
                'Group_Title'                   = "$($vulnerability.title)"
                'Rule_ID'                       = "$($vulnerability.Rule.id)"
                'Rule_Ver'                      = "$($vulnerability.Rule.version)"
                'Rule_Title'                    = "$($vulnerability.Rule.title)"
                'Vuln_Discuss'                  = "$($vulnerabiltyDiscussionElement.discussionroot.VulnDiscussion)"
                'IA_Controls'                   = "$($vulnerabiltyDiscussionElement.discussionroot.IAControls)"
                'Check_Content'                 = "$($vulnerability.Rule.check.'check-content')"
                'Fix_Text'                      = "$($vulnerability.Rule.fixtext.InnerText)"
                'False_Positives'               = "$($vulnerabiltyDiscussionElement.discussionroot.FalsePositives)"
                'False_Negatives'               = "$($vulnerabiltyDiscussionElement.discussionroot.FalseNegatives)"
                'Documentable'                  = "$($vulnerabiltyDiscussionElement.discussionroot.Documentable)"
                'Mitigations'                   = "$($vulnerabiltyDiscussionElement.discussionroot.Mitigations)"
                'Potential_Impact'              = "$($vulnerabiltyDiscussionElement.discussionroot.PotentialImpacts)"
                'Third_Party_Tools'             = "$($vulnerabiltyDiscussionElement.discussionroot.ThirdPartyTools)"
                'Mitigation_Control'            = "$($vulnerabiltyDiscussionElement.discussionroot.MitigationControl)"
                'Responsibility'                = "$($vulnerabiltyDiscussionElement.discussionroot.Responsibility)"
                'Security_Override_Guidance'    = "$($vulnerabiltyDiscussionElement.discussionroot.SeverityOverrideGuidance)"
                'Check_Content_Ref'             = "$($vulnerability.Rule.check.'check-content-ref'.href)"
                'Class'                         = 'Unclass'
                'STIGRef'                       = "$($XccdfBenchmark.title) :: $($XccdfBenchmark.'plain-text'.InnerText)"
                'TargetKey'                     = "$($vulnerability.Rule.reference.identifier)"
                'CCI_REF'                       = "$($vulnerability.Rule.ident.InnerText)"
            }
        )
    }

    return $vulnerabilityList
}

<#
    .SYNOPSIS
        Converts the mof into an array of objects
#>

function Get-MofContent
{
    [CmdletBinding()]
    [OutputType([psobject])]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]
        $ReferenceConfiguration
    )

    if ( -not $script:mofContent )
    {
        $script:mofContent = [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::ImportInstances($ReferenceConfiguration, 4)
    }

    return $script:mofContent
}

<#
    .SYNOPSIS
        Gets the stig details from the mof
#>

function Get-SettingsFromMof
{
    [CmdletBinding()]
    [OutputType([psobject])]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]
        $ReferenceConfiguration,

        [Parameter(Mandatory = $true)]
        [string]
        $Id
    )

    $mofContent = Get-MofContent -ReferenceConfiguration $ReferenceConfiguration

    return $mofContent.Where( {$PSItem.ResourceID -match $Id} )
}

<#
    .SYNOPSIS
        Gets the stig details from the Test\Get-DscConfiguration output
#>

function Get-SettingsFromResult
{
    [CmdletBinding()]
    [OutputType([psobject])]
    param
    (
        [Parameter(Mandatory = $true)]
        [PSCustomObject]
        $DscResult,

        [Parameter(Mandatory = $true)]
        [string]
        $Id
    )

    if (-not $script:allResources)
    {
        $script:allResources = $DscResult.ResourcesNotInDesiredState + $DscResult.ResourcesInDesiredState
    }

    return $script:allResources.Where( {$PSItem.ResourceID -match $Id} )
}

<#
    .SYNOPSIS
        Gets the value from a STIG setting
#>

function Get-FindingDetails
{
    [OutputType([string])]
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory)]
        [AllowNull()]
        [psobject]
        $Setting
    )

    switch ($setting.ResourceID)
    {
        {$PSItem -match "^\[Registry\]"}
        {
            return "Registry Value = $($setting.ValueData)"
        }
        {$PSItem -match "^\[AuditPolicySubcategory\]"}
        {
            return "AuditPolicySubcategory AuditFlag = $($setting.AuditFlag)"
        }
        {$PSItem -match "^\[AccountPolicy\]"}
        {
            return "AccountPolicy = Needs work"
        }
        {$PSItem -match "^\[UserRightsAssignment\]"}
        {
            return "UserRightsAssignment Identity = $($setting.Identity)"
        }
        {$PSItem -match "^\[SecurityOption\]"}
        {
            return "SecurityOption = Needs work"
        }
        default
        {
            return "not found"
        }
    }
}

#endregion