Module/Rule/Convert/ConvertFactory.psm1

# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
using module .\..\..\Common\Common.psm1
using module .\..\..\Rule.HardCoded\Convert\HardCodedRule.Convert.psm1
using module .\..\..\Rule.AccountPolicy\Convert\AccountPolicyRule.Convert.psm1
using module .\..\..\Rule.AuditPolicy\Convert\AuditPolicyRule.Convert.psm1
using module .\..\..\Rule.DnsServerRootHint\Convert\DnsServerRootHintRule.Convert.psm1
using module .\..\..\Rule.DnsServerSetting\Convert\DnsServerSettingRule.Convert.psm1
using module .\..\..\Rule.Document\Convert\DocumentRule.Convert.psm1
using module .\..\..\Rule.FileContent\Convert\FileContentRule.Convert.psm1
using module .\..\..\Rule.Group\Convert\GroupRule.Convert.psm1
using module .\..\..\Rule.IISLogging\Convert\IISLoggingRule.Convert.psm1
using module .\..\..\Rule.Manual\Convert\ManualRule.Convert.psm1
using module .\..\..\Rule.MimeType\Convert\MimeTypeRule.Convert.psm1
using module .\..\..\Rule.Permission\Convert\PermissionRule.Convert.psm1
using module .\..\..\Rule.ProcessMitigation\Convert\ProcessMitigationRule.Convert.psm1
using module .\..\..\Rule.Registry\Convert\RegistryRule.Convert.psm1
using module .\..\..\Rule.SecurityOption\Convert\SecurityOptionRule.Convert.psm1
using module .\..\..\Rule.Service\Convert\ServiceRule.Convert.psm1
using module .\..\..\Rule.SqlScriptQuery\Convert\SqlScriptQueryRule.Convert.psm1
using module .\..\..\Rule.UserRight\Convert\UserRightRule.Convert.psm1
using module .\..\..\Rule.WebAppPool\Convert\WebAppPoolRule.Convert.psm1
using module .\..\..\Rule.WebConfigurationProperty\Convert\WebConfigurationPropertyRule.Convert.psm1
using module .\..\..\Rule.WindowsFeature\Convert\WindowsFeatureRule.Convert.psm1
using module .\..\..\Rule.WinEventLog\Convert\WinEventLogRule.Convert.psm1
using module .\..\..\Rule.AuditSetting\Convert\AuditSettingRule.Convert.psm1
using module .\..\..\Rule.SslSettings\Convert\SslSettingsRule.Convert.psm1
using module .\..\..\Rule.VsphereAdvancedSettings\Convert\VsphereAdvancedSettingsRule.Convert.psm1
using module .\..\..\Rule.VsphereService\Convert\VsphereServiceRule.Convert.psm1
using module .\..\..\Rule.VspherePortGroupSecurity\Convert\VspherePortGroupSecurityRule.Convert.psm1
using module .\..\..\Rule.VsphereAcceptanceLevel\Convert\VsphereAcceptanceLevelRule.Convert.psm1
using module .\..\..\Rule.VsphereSnmpAgent\Convert\VsphereSnmpAgentRule.Convert.psm1
using module .\..\..\Rule.VsphereKernelActiveDumpPartition\Convert\VsphereKernelActiveDumpPartitionRule.Convert.psm1
using module .\..\..\Rule.VsphereNtpSettings\Convert\VsphereNtpSettingsRule.Convert.psm1
using module .\..\..\Rule.VsphereVssSecurity\Convert\VsphereVssSecurityRule.Convert.psm1
using module .\..\..\Rule.nxPackage\Convert\nxPackageRule.Convert.psm1
using module .\..\..\Rule.nxService\Convert\nxServiceRule.Convert.psm1
using module .\..\..\Rule.nxFileLine\Convert\nxFileLineRule.Convert.psm1
using module .\..\..\Rule.nxFile\Convert\nxFileRule.Convert.psm1
using module .\..\..\Rule.RootCertificate\Convert\RootCertificateRule.Convert.psm1
using module .\..\..\Rule.SqlServerConfiguration\Convert\SqlServerConfigurationRule.Convert.psm1
using module .\..\..\Rule.SqlLogin\Convert\SqlLoginRule.Convert.psm1
using module .\..\..\Rule.SqlProtocol\Convert\SqlProtocolRule.Convert.psm1
using module .\..\..\Rule.SqlDatabase\Convert\SqlDatabaseRule.Convert.psm1

# Header

class SplitFactory
{
    <#
        .SYNOPSIS
            Static method split
    #>

    static [System.Collections.ArrayList] XccdfRule ([xml.xmlelement] $Rule, [string] $TypeName)
    {
        [System.Collections.ArrayList] $ruleList = @()

        $instance = New-Object -TypeName $TypeName
        $hasMultipleRules = $instance.GetType().GetMethod('HasMultipleRules')

        if (-not $hasMultipleRules.IsStatic)
        {
            throw "$TypeName does not have a static HasMultipleRules method"
        }

        if ($HasMultipleRules.Invoke($HasMultipleRules, $Rule.rule.Check.'check-content'))
        {
            $splitMultipleRules = $instance.GetType().GetMethod('SplitMultipleRules')
            [string[]] $splitRules = $splitMultipleRules.Invoke($splitMultipleRules, $Rule.rule.Check.'check-content')
            [int] $byte = 97
            foreach ($splitRule in $splitRules)
            {
                <#
                    Creating the split rule name here since some split rules have hardcoded
                    values and are detected based on the split rule name
                #>

                $newRule = $Rule.Clone()
                $newRule.rule.Check.'check-content' = $splitRule
                $newRule.Id = "$($Rule.id).$([CHAR][BYTE]$byte)"
                $byte ++
                $ruleList += (New-Object -TypeName $TypeName -ArgumentList $newRule).AsRule()
            }
        }
        else
        {
            $ruleList += (New-Object -TypeName $TypeName -ArgumentList $Rule).AsRule()
        }
        return $ruleList
    }

    <#
        .SYNOPSIS
            Instance method split
    #>

    static [System.Collections.ArrayList] XccdfRule ([psobject] $Rule, [string] $TypeName, [string] $Property)
    {
        [System.Collections.ArrayList] $ruleList = @()

        $instance = New-Object -TypeName $TypeName -ArgumentList $Rule
        if ($instance.HasMultipleRules())
        {
            [string[]] $splitRules = $instance.SplitMultipleRules()
            foreach ($splitRule in $splitRules)
            {
                $ruleClone = $instance.Clone()
                $ruleClone.$Property = $splitRule
                $ruleList += $ruleClone.AsRule()
            }
        }
        else
        {
            $ruleList += $instance.AsRule()
        }
        return $ruleList
    }
}

class ConvertFactory
{
    static [System.Collections.ArrayList] Rule ([xml.xmlelement] $Rule)
    {
        [System.Collections.ArrayList] $ruleTypeList = @()

        switch ($Rule.rule.check.'check-content')
        {
            {[HardCodedRuleConvert]::Match($PSItem)}
            {
                $hardCodedRule = [SplitFactory]::XccdfRule($Rule, 'HardCodedRuleConvert')
                if ($hardCodedRule -is [System.Collections.ICollection])
                {
                    $null = $ruleTypeList.AddRange($hardCodedRule)
                }
                else
                {
                    $null = $ruleTypeList.Add($hardCodedRule)
                }
                break
            }
            {[AccountPolicyRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [AccountPolicyRuleConvert]::new($Rule).AsRule()
                )
            }
            {[AuditPolicyRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [AuditPolicyRuleConvert]::new($Rule).AsRule()
                )
            }
            {[DnsServerSettingRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [DnsServerSettingRuleConvert]::new($Rule).AsRule()
                )
            }
            {[DnsServerRootHintRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [DnsServerRootHintRuleConvert]::new($Rule).AsRule()
                )
            }
            {[FileContentRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.AddRange(
                    [SplitFactory]::XccdfRule($Rule, 'FileContentRuleConvert')
                )
            }
            {[GroupRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [GroupRuleConvert]::new($Rule).AsRule()
                )
            }
            {[IisLoggingRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [IisLoggingRuleConvert]::new($Rule).AsRule()
                )
            }
            {[MimeTypeRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.AddRange(
                    [SplitFactory]::XccdfRule($Rule, 'MimeTypeRuleConvert')
                )
            }
            {[PermissionRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.AddRange(
                    [SplitFactory]::XccdfRule($Rule, 'PermissionRuleConvert')
                )
            }
            {[ProcessMitigationRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.AddRange(
                    [SplitFactory]::XccdfRule($Rule, 'ProcessMitigationRuleConvert')
                )
            }
            {[RegistryRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.AddRange(
                    [SplitFactory]::XccdfRule($Rule, 'RegistryRuleConvert')
                )
            }
            {[SecurityOptionRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [SecurityOptionRuleConvert]::new($Rule).AsRule()
                )
            }
            {[ServiceRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.AddRange(
                    [SplitFactory]::XccdfRule($Rule, 'ServiceRuleConvert', 'ServiceName')
                )
            }
            {[SqlScriptQueryRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [SqlScriptQueryRuleConvert]::new($Rule).AsRule()
                )
            }
            {[SQLServerConfigurationRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [SQLServerConfigurationRuleConvert]::new($Rule).AsRule()
                )
            }
            {[SqlLoginRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [SqlLoginRuleConvert]::new($Rule).AsRule()
                )
            }
            {[SqlProtocolRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [SqlProtocolRuleConvert]::new($Rule).AsRule()
                )
            }
            {[SqlDatabaseRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.AddRange(
                    [SplitFactory]::XccdfRule($Rule, 'SqlDatabaseRuleConvert')
                )
            }
            {[UserRightRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.AddRange(
                    [SplitFactory]::XccdfRule($Rule, 'UserRightRuleConvert')
                )
            }
            {[WebAppPoolRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [WebAppPoolRuleConvert]::new($Rule).AsRule()
                )
            }
            {[WebConfigurationPropertyRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.AddRange(
                    [SplitFactory]::XccdfRule($Rule, 'WebConfigurationPropertyRuleConvert')
                )
            }
            {[WindowsFeatureRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.AddRange(
                    [SplitFactory]::XccdfRule($Rule, 'WindowsFeatureRuleConvert', 'Name')
                )
            }
            {[WinEventLogRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [WinEventLogRuleConvert]::new($Rule).AsRule()
                )
            }
            {[AuditSettingRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [AuditSettingRuleConvert]::new($Rule).AsRule()
                )
            }
            {[SslSettingsRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [SslSettingsRuleConvert]::new($Rule).AsRule()
                )
            }
            {[VsphereAdvancedSettingsRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [VsphereAdvancedSettingsRuleConvert]::new($Rule).AsRule()
                )
            }
            {[VsphereServiceRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [VsphereServiceRuleConvert]::new($Rule).AsRule()
                )
            }
            {[VspherePortGroupSecurityRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [VspherePortGroupSecurityRuleConvert]::new($Rule).AsRule()
                )
            }
            {[VsphereAcceptanceLevelRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [VsphereAcceptanceLevelRuleConvert]::new($Rule).AsRule()
                )
            }
            {[VsphereSnmpAgentRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [VsphereSnmpAgentRuleConvert]::new($Rule).AsRule()
                )
            }
            {[VsphereKernelActiveDumpPartitionRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [VsphereKernelActiveDumpPartitionRuleConvert]::new($Rule).AsRule()
                )
            }
            {[VsphereNtpSettingsRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [VsphereNtpSettingsRuleConvert]::new($Rule).AsRule()
                )
            }
            {[VsphereVssSecurityRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [VsphereVssSecurityRuleConvert]::new($Rule).AsRule()
                )
            }
            {[RootCertificateRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.AddRange(
                    [SplitFactory]::XccdfRule($Rule, 'RootCertificateRuleConvert')
                )
            }
            {[nxPackageRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [nxPackageRuleConvert]::new($Rule).AsRule()
                )
            }
            {[nxServiceRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [nxServiceRuleConvert]::new($Rule).AsRule()
                )
            }
            {[nxFileLineRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.AddRange(
                    [SplitFactory]::XccdfRule($Rule, 'nxFileLineRuleConvert')
                )
            }
            {[nxFileRuleConvert]::Match($PSItem)}
            {
                $null = $ruleTypeList.Add(
                    [nxFileRuleConvert]::new($Rule).AsRule()
                )
            }

            <#
                Some rules have a documentation requirement only for exceptions,
                so the DocumentRule needs to be at the end of the switch as a
                catch all for documentation rules. Once a rule has been parsed,
                it should not be converted into a document rule.
            #>

            {[DocumentRuleConvert]::Match($PSItem) -and $ruleTypeList.Count -eq 0}
            {
                $null = $ruleTypeList.Add(
                    [DocumentRuleConvert]::new($Rule).AsRule()
                )
            }
            default
            {
                $null = $ruleTypeList.Add(
                    [ManualRuleConvert]::new($Rule).AsRule()
                )
            }
        }

        <#
            Rules can be split into multiple rules of multiple types, so the list
            of Id's needs to be validated to be unique. Split factory initially
            split the "id" as well as the ConvertFactory, removed this code from
            SplitFactory as it was redundant.
        #>

        $ruleCount = $ruleTypeList | Measure-Object
        $uniqueRuleCount = $ruleTypeList | Select-Object -Property Id -Unique | Measure-Object

        if ($uniqueRuleCount.Count -ne $ruleCount.Count)
        {
            # 97 = lowercase a (alpha)
            [int] $byte = 97
            foreach ($convertedrule in $ruleTypeList)
            {
                $convertedrule.id = "$($Rule.id).$([CHAR][BYTE]$byte)"
                if ([string]::IsNullOrEmpty($convertedrule.LegacyId) -eq $false)
                {
                    $convertedrule.LegacyId = "$($convertedrule.LegacyId).$([CHAR][BYTE]$byte)"
                }

                $byte ++
            }
        }

        return $ruleTypeList
    }
}