Module/Rule.nxFileLine/Convert/Methods.ps1

# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

<#
    .SYNOPSIS
        Retrieves the nxFileLineContainsLine from the check-content element in the xccdf

    .PARAMETER FixText
        Specifies the FixText element in the xccdf
#>

function Get-nxFileLineContainsLine
{
    [CmdletBinding()]
    [OutputType([string[]])]
    param
    (
        [Parameter(Mandatory = $true)]
        [string[]]
        $CheckContent
    )

    Write-Verbose "[$($MyInvocation.MyCommand.Name)]"
    try
    {
        $rawString = $CheckContent -join "`n"
        if
        (
            $rawString -match $regularExpression.nxFileLineContainsLine -or
            $rawString -match $regularExpression.nxFileLineContainsLineYumConf -or
            $rawString -match $regularExpression.nxFileLineContainsLineAuditUbuntu
        )
        {
            $matchResults = $Matches['setting'] -split "`n"
            $results = @()
            foreach ($line in $matchResults)
            {
                if
                (
                    [string]::IsNullOrEmpty($line) -eq $false -and
                    $line -notmatch $regularExpression.nxFileLineContainsLineExclude
                )
                {
                    $results += $line -replace '\s{2,}', ' '
                }
            }
        }
        elseif ($rawString -match 'You are accessing a U.S. Government \(USG\) [^"]+(?<=details.)')
        {
            $results = $matches.Values -replace '\.\r|:\r', ".`n"
        }

        return $results
    }
    catch
    {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] nxFileLineContainsLine : Not Found"
        return $null
    }
}

<#
    .SYNOPSIS
        Retreives the nxFileLineFilePath from the check-content element in the xccdf

    .PARAMETER FixText
        Specifies the check-content element in the xccdf
#>

function Get-nxFileLineFilePath
{
    [CmdletBinding()]
    [OutputType([string])]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]
        $CheckContent
    )

    try
    {
        $nxFileLineFilePathAggregate = '{0}|{1}|{2}|{3}|{4}|{5}|{6}' -f
            $regularExpression.nxFileLineFilePathAudit,
            $regularExpression.nxFileLineFilePathAuditUbuntu,
            $regularExpression.nxFileLineFilePathUbuntuBanner,
            $regularExpression.nxFileLineFilePathBannerUbuntu,
            $regularExpression.nxFileLineFilePathTftp,
            $regularExpression.nxFileLineFilePathRescue,
            $regularExpression.nxFileLineFilePath
        $null = $CheckContent -match $nxFileLineFilePathAggregate
        switch ($Matches.Keys)
        {
            {
                $PSItem -eq 'auditPath' -or $PSItem -eq 'auditPathUbuntu'
            }
            {
                return '/etc/audit/rules.d/audit.rules'
            }
            'ubuntuBanner'
            {
                return '/etc/issue'
            }
            'bannerPathUbuntu'
            {
                return $Matches['bannerPathUbuntu']
            }
            'tftpPath'
            {
                return $Matches['tftpPath']
            }
            'rescuePath'
            {
                return $Matches['rescuePath']
            }
            'filePath'
            {
                return $Matches['filePath']
            }
            default
            {
                Write-Verbose "[$($MyInvocation.MyCommand.Name)] nxFileLineFilePath : Not Found"
                return $null
            }
        }
    }
    catch
    {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] nxFileLineFilePath : Not Found"
        return $null
    }
}

<#
    .SYNOPSIS
        Retreives the nxFileLineDoesNotContainPattern from the
        check-content element in the xccdf

    .PARAMETER FixText
        Specifies the check-content element in the xccdf
#>

function Get-nxFileLineDoesNotContainPattern
{
    [CmdletBinding()]
    [OutputType([string])]
    param()

    $doesNotContainPatternExclusionRuleId = @(
        'V-71863'
    )

    if ($doesNotContainPatternExclusionRuleId -contains $this.Id)
    {
        return 'PatternNotRequired'
    }

    try
    {
        if ($doesNotContainPattern.ContainsKey($this.ContainsLine))
        {
            $results = $doesNotContainPattern[$this.ContainsLine]
        }

        if
        (
            $results -eq 'DynamicallyGeneratedDoesNotContainPattern' -or
            $doesNotContainPattern.ContainsKey($this.ContainsLine) -eq $false
        )
        {
            <#
                The "Dynamic" DoesNotContainPattern generation takes the containsLine and prefixes it with
                a hash, as well as replaces any spaces with a RegEx \s*.
            #>

            $doesNotContainPattern = $this.ContainsLine -replace '=', '\s*=\s*' -replace '\s+', '\s*'
            $doesNotContainPattern = $doesNotContainPattern.Replace('\s*\s*', '\s*')
            $results = '#\s*{0}' -f $doesNotContainPattern
        }
    }
    catch
    {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] nxFileLineDoesNotContainPattern : Not Found"
        return $null
    }

    return $results
}

<#
    .SYNOPSIS
        There are several rules that publish multiple FileLine settings in a single rule.
        This function will check for multiple entries.

    .PARAMETER CheckContent
        The standard check content string to look for duplicate entries.
#>

function Test-nxFileLineMultipleEntries
{
    [CmdletBinding()]
    [OutputType([bool])]
    param
    (
        [Parameter(Mandatory = $true)]
        [psobject]
        $CheckContent
    )

    $filePath = $CheckContent | Select-String -Pattern $regularExpression.nxFileLineFilePath -AllMatches
    $filePathCount = @()
    foreach ($path in $filePath.Matches)
    {
        if ($path.Groups['filePath'].Value -ne '/etc/issue')
        {
            $filePathCount += $path.Groups['filePath'].Value
        }
    }

    $filePathUniqueCount = $filePathCount | Select-Object -Unique | Measure-Object
    if ($filePathUniqueCount.Count -gt 1)
    {
        return $true
    }

    $splitCheckContent = Split-nxFileLineMultipleEntries -CheckContent $CheckContent
    if ($splitCheckContent.Count -gt 1)
    {
        return $true
    }

    return $false
}

<#
    .SYNOPSIS
        There are several rules that publish multiple FileLine settings in a single rule.
        This function will split multiple entries.

    .PARAMETER CheckContent
        The standard check content string to look for duplicate entries.
#>

function Split-nxFileLineMultipleEntries
{
    [CmdletBinding()]
    [OutputType([System.Collections.ArrayList])]
    param
    (
        [Parameter(Mandatory = $true)]
        [psobject]
        $CheckContent
    )

    $splitCheckContent = @()

    # Split CheckContent based on File Path or 'sudo auditctl...':
    $splitFilePathPatternAggregate = '{0}|{1}' -f $regularExpression.nxFileLineFilePath, $regularExpression.nxFileLineFilePathAuditUbuntu
    [array] $splitFilePathLineNumber = ($CheckContent | Select-String -Pattern $splitFilePathPatternAggregate).LineNumber

    # Header for the rule should start at 0 through the first detected file path subtract 2 since Select-String LineNumber is not 0 based
    $headerLineRange = 0..($splitFilePathLineNumber[0] - 2)
    $headerFileLine = $CheckContent[$headerLineRange]

    # Footer should start from the last detected "If" to the end of CheckContent
    [array] $footerDetection = ($CheckContent | Select-String -Pattern $regularExpression.nxFileLineFooterDetection).LineNumber
    $footerLineRange = ($footerDetection[-1] - 1)..($CheckContent.Count - 1)
    $footerFileLine = $CheckContent[$footerLineRange]

    # Putting it all together and returning separate entries to the next loop
    for ($i = 0; $i -lt $splitFilePathLineNumber.Count; $i++)
    {
        $splitFilePathStringBuilder = New-Object -TypeName System.Text.StringBuilder
        foreach ($headerLine in $headerFileLine)
        {
            [void] $splitFilePathStringBuilder.AppendLine($headerLine)
        }

        # If the index is equal to the 0 based array count then we are at the list item and the range is calculated from the footer
        if ($i -eq ($splitFilePathLineNumber.Count - 1))
        {
            $splitFileLineContentRange = ($splitFilePathLineNumber[$i] - 1)..($footerLineRange[0] - 1)
        }
        else
        {
            # Determine start of next rule and subtract by 2 since Select-String LineNumber is not 0 based
            $splitFileLineContentRange = ($splitFilePathLineNumber[$i] - 1)..($splitFilePathLineNumber[$i + 1] - 2)
        }

        # Insert the split rule contents and add the footer\, then store the string in the collection
        foreach ($line in $CheckContent[$splitFileLineContentRange])
        {
            [void] $splitFilePathStringBuilder.AppendLine($line)
        }

        foreach ($footerLine in $footerFileLine)
        {
            [void] $splitFilePathStringBuilder.AppendLine($footerLine)
        }

        $splitCheckContent += $splitFilePathStringBuilder.ToString()
    }

    # Split modified CheckContent based each File Path Setting:
    $splitEntries = @()
    foreach ($content in $splitCheckContent)
    {
        $fileContainsLine = Get-nxFileLineContainsLine -CheckContent $content
        if ($null -ne $fileContainsLine)
        {
            if ($fileContainsLine -match 'You are accessing a U.S. Government \(USG\) [^"]+(?<=details.)')
            {
                $splitEntries += $fileContainsLine
            }
            else
            {
                $checkContentData = $content.Replace(($fileContainsLine -join "`n"), '{0}')
                foreach ($setting in $fileContainsLine)
                {
                    $splitEntries += $checkContentData -f $setting
                }
            }
        }
    }

    return $splitEntries
}