Compliance/Test-PCI-DSS.ps1

function Test-PCI-DSS {
    <#
    .SYNOPSIS
        Tests system compliance against PCI-DSS requirements
    .DESCRIPTION
        Evaluates Windows system against Payment Card Industry Data Security Standard (PCI-DSS)
        requirements for systems that store, process, or transmit cardholder data
    .PARAMETER Version
        PCI-DSS version: 3.2.1 or 4.0 (default: 4.0)
    .PARAMETER RequirementFilter
        Specific requirement to test: All, Network, Data, AccessControl, Monitoring, Testing
    .EXAMPLE
        Test-PCI-DSS
        Test-PCI-DSS -Version '4.0' -RequirementFilter Network
    .OUTPUTS
        PSCustomObject with PCI-DSS compliance results
    #>

    
    [CmdletBinding()]
    param(
        [Parameter()]
        [ValidateSet('3.2.1', '4.0')]
        [string]$Version = '4.0',
        
        [Parameter()]
        [ValidateSet('All', 'Network', 'Data', 'AccessControl', 'Monitoring', 'Testing')]
        [string]$RequirementFilter = 'All'
    )
    
    begin {
        Write-Host "=== PCI-DSS COMPLIANCE TEST ===" -ForegroundColor Cyan
        Write-Host "Start Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Yellow
        Write-Host "PCI-DSS Version: $Version" -ForegroundColor Yellow
        Write-Host "Requirement: $RequirementFilter" -ForegroundColor Yellow
        
        $results = [PSCustomObject]@{
            TestDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
            ComputerName = $env:COMPUTERNAME
            PCIDSSVersion = $Version
            RequirementFilter = $RequirementFilter
            Requirements = @()
            Summary = @{
                TotalRequirements = 0
                InPlace = 0
                NotInPlace = 0
                NotApplicable = 0
                CompliancePercentage = 0
            }
            Recommendations = @()
        }
        
        # Helper function to test registry value
        function Test-RegistryValue {
            param(
                [string]$Path,
                [string]$Name,
                [object]$ExpectedValue
            )
            
            try {
                if (Test-Path $Path) {
                    $value = Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue | 
                             Select-Object -ExpandProperty $Name -ErrorAction SilentlyContinue
                    
                    return @{
                        Compliant = ($value -eq $ExpectedValue)
                        ActualValue = $value
                        ExpectedValue = $ExpectedValue
                    }
                }
                return @{
                    Compliant = $false
                    ActualValue = "Path not found"
                    ExpectedValue = $ExpectedValue
                }
            }
            catch {
                return @{
                    Compliant = $false
                    ActualValue = "Error"
                    ExpectedValue = $ExpectedValue
                }
            }
        }
    }
    
    process {
        try {
            # 1. REQUIREMENT 1: Install and maintain network security controls
            if ($RequirementFilter -in @('All', 'Network')) {
                Write-Host "`n[1/6] Testing Requirement 1: Network Security Controls..." -ForegroundColor Cyan
                
                try {
                    # Req 1.2: Network security controls (firewalls)
                    $firewallProfiles = Get-NetFirewallProfile -ErrorAction SilentlyContinue
                    $allFirewallsEnabled = ($null -ne $firewallProfiles) -and (@($firewallProfiles | Where-Object { $null -ne $_ -and -not $_.Enabled }).Count -eq 0)
                    
                    $req = [PSCustomObject]@{
                        RequirementID = '1.2.1'
                        RequirementName = 'Firewall Configuration'
                        Category = 'Network'
                        Description = 'Firewalls must be enabled and properly configured'
                        Expected = 'All firewall profiles enabled'
                        Actual = if ($null -eq $firewallProfiles) { "Unable to retrieve firewall profiles" }
                                 elseif ($allFirewallsEnabled) { "All enabled" } else { "Some disabled" }
                        Status = if ($allFirewallsEnabled) { 'InPlace' } else { 'NotInPlace' }
                        Priority = 'Critical'
                        Remediation = 'Enable all Windows Firewall profiles (Domain, Private, Public)'
                    }
                    $results.Requirements += $req
                    
                    # Req 1.3: Inbound traffic restrictions
                    $defaultInboundBlock = @($firewallProfiles | Where-Object { $null -ne $_ -and $_.DefaultInboundAction -ne 'Block' })
                    
                    $req = [PSCustomObject]@{
                        RequirementID = '1.3.1'
                        RequirementName = 'Inbound Traffic Restriction'
                        Category = 'Network'
                        Description = 'Inbound traffic should be denied by default'
                        Expected = 'Default action: Block'
                        Actual = if ($null -eq $firewallProfiles) { "Unable to retrieve firewall profiles" }
                                 elseif ($defaultInboundBlock.Count -eq 0) { "All blocked" } else { "$($defaultInboundBlock.Count) profiles allow" }
                        Status = if (($null -ne $firewallProfiles) -and $defaultInboundBlock.Count -eq 0) { 'InPlace' } else { 'NotInPlace' }
                        Priority = 'High'
                        Remediation = 'Set default inbound action to Block for all profiles'
                    }
                    $results.Requirements += $req
                    
                    # Req 1.4: Restrict connections between untrusted networks
                    $publicProfile = $firewallProfiles | Where-Object { $null -ne $_ -and $_.Name -eq 'Public' }
                    
                    $req = [PSCustomObject]@{
                        RequirementID = '1.4.1'
                        RequirementName = 'Public Network Protection'
                        Category = 'Network'
                        Description = 'Public network profile must be restrictive'
                        Expected = 'Public profile enabled and blocking inbound'
                        Actual = if ($publicProfile) { "Enabled: $($publicProfile.Enabled), Inbound: $($publicProfile.DefaultInboundAction)" } else { "Public profile not found" }
                        Status = if ($publicProfile -and $publicProfile.Enabled -and $publicProfile.DefaultInboundAction -eq 'Block') { 'InPlace' } else { 'NotInPlace' }
                        Priority = 'High'
                        Remediation = 'Enable Public profile with default inbound block'
                    }
                    $results.Requirements += $req
                }
                catch {
                    Write-Warning "Error testing Network requirements: $_"
                }
            }
            
            # 2. REQUIREMENT 3: Protect stored account data
            if ($RequirementFilter -in @('All', 'Data')) {
                Write-Host "`n[2/6] Testing Requirement 3: Protect Stored Data..." -ForegroundColor Cyan
                
                try {
                    # Req 3.3: Sensitive authentication data not retained
                    $req = [PSCustomObject]@{
                        RequirementID = '3.3.1'
                        RequirementName = 'Sensitive Data Retention'
                        Category = 'Data'
                        Description = 'Sensitive authentication data must not be stored'
                        Expected = 'No card data stored (manual verification)'
                        Actual = 'Manual verification required'
                        Status = 'NotApplicable'
                        Priority = 'Critical'
                        Remediation = 'Ensure no full track data, CVV2, or PIN blocks are stored'
                    }
                    $results.Requirements += $req
                    
                    # Req 3.5: Primary account number (PAN) protection
                    $bitlockerStatus = Get-BitLockerVolume -MountPoint "C:" -ErrorAction SilentlyContinue
                    $bitlockerEnabled = ($null -ne $bitlockerStatus) -and ($bitlockerStatus.ProtectionStatus -eq 'On')
                    
                    $req = [PSCustomObject]@{
                        RequirementID = '3.5.1'
                        RequirementName = 'Data at Rest Encryption'
                        Category = 'Data'
                        Description = 'PAN must be rendered unreadable (encryption)'
                        Expected = 'Disk encryption enabled (BitLocker)'
                        Actual = if ($bitlockerEnabled) { "Enabled" } else { "Disabled" }
                        Status = if ($bitlockerEnabled) { 'InPlace' } else { 'NotInPlace' }
                        Priority = 'Critical'
                        Remediation = 'Enable BitLocker or third-party disk encryption'
                    }
                    $results.Requirements += $req
                    
                    # Req 3.6: Cryptographic key management
                    $req = [PSCustomObject]@{
                        RequirementID = '3.6.1'
                        RequirementName = 'Key Management'
                        Category = 'Data'
                        Description = 'Cryptographic keys must be managed securely'
                        Expected = 'Key management procedures documented'
                        Actual = 'Manual verification required'
                        Status = 'NotApplicable'
                        Priority = 'High'
                        Remediation = 'Document and implement key management procedures'
                    }
                    $results.Requirements += $req
                }
                catch {
                    Write-Warning "Error testing Data Protection requirements: $_"
                }
            }
            
            # 3. REQUIREMENT 7 & 8: Access Control
            if ($RequirementFilter -in @('All', 'AccessControl')) {
                Write-Host "`n[3/6] Testing Requirements 7 & 8: Access Control..." -ForegroundColor Cyan
                
                try {
                    # Req 7.1: Limit access to system components
                    $guestUser = Get-LocalUserByRid -Rid 501 -ErrorAction SilentlyContinue
                    $guestKnown = ($null -ne $guestUser) -and ($null -ne $guestUser.Enabled)
                    $guestDisabled = $guestKnown -and ($guestUser.Enabled -eq $false)

                    $req = [PSCustomObject]@{
                        RequirementID = '7.1.1'
                        RequirementName = 'Access Limitations'
                        Category = 'AccessControl'
                        Description = 'Unnecessary accounts must be disabled'
                        Expected = 'Guest account disabled'
                        Actual = if (-not $guestKnown) { 'Unable to determine (insufficient privileges or unsupported platform)' }
                                 elseif ($guestDisabled) { "Disabled" } else { "Enabled" }
                        Status = if (-not $guestKnown) { 'NotApplicable' }
                                 elseif ($guestDisabled) { 'InPlace' } else { 'NotInPlace' }
                        Priority = 'High'
                        Remediation = 'Disable Guest and other unnecessary accounts'
                    }
                    $results.Requirements += $req
                    
                    # Req 8.2: User identification and authentication
                    $passwordPolicy = Get-LocalPasswordPolicy
                    $minPasswordLength = $passwordPolicy.MinPasswordLength

                    $req = [PSCustomObject]@{
                        RequirementID = '8.2.3'
                        RequirementName = 'Password Complexity'
                        Category = 'AccessControl'
                        Description = 'Passwords must meet minimum complexity'
                        Expected = 'Minimum 12 characters (or 8 if MFA)'
                        Actual = if ($null -eq $minPasswordLength) { 'Unable to determine (insufficient privileges or unsupported platform)' }
                                 else { "$minPasswordLength characters" }
                        Status = if ($null -eq $minPasswordLength) { 'NotApplicable' }
                                 elseif ($minPasswordLength -ge 12) { 'InPlace' } else { 'NotInPlace' }
                        Priority = 'Critical'
                        Remediation = 'Set minimum password length to 12 characters'
                    }
                    $results.Requirements += $req

                    # Req 8.2.4: Change passwords every 90 days
                    $maxAgeDays = $passwordPolicy.MaxPasswordAgeDays

                    $req = [PSCustomObject]@{
                        RequirementID = '8.2.4'
                        RequirementName = 'Password Change Frequency'
                        Category = 'AccessControl'
                        Description = 'Passwords must be changed periodically'
                        Expected = '90 days or less'
                        Actual = if ($null -eq $maxAgeDays) { 'Unable to determine (insufficient privileges or unsupported platform)' }
                                 elseif ($maxAgeDays -eq -1) { 'Never expires' } else { "$maxAgeDays days" }
                        Status = if ($null -eq $maxAgeDays) { 'NotApplicable' }
                                 elseif ($maxAgeDays -gt 0 -and $maxAgeDays -le 90) { 'InPlace' } else { 'NotInPlace' }
                        Priority = 'High'
                        Remediation = 'Set maximum password age to 90 days'
                    }
                    $results.Requirements += $req
                    
                    # Req 8.3: Multi-factor authentication
                    $req = [PSCustomObject]@{
                        RequirementID = '8.3.1'
                        RequirementName = 'Multi-Factor Authentication'
                        Category = 'AccessControl'
                        Description = 'MFA required for all access to CDE'
                        Expected = 'MFA enabled for administrators'
                        Actual = 'Manual verification required'
                        Status = 'NotApplicable'
                        Priority = 'Critical'
                        Remediation = 'Implement MFA (Windows Hello, Smart Card, or third-party)'
                    }
                    $results.Requirements += $req
                }
                catch {
                    Write-Warning "Error testing Access Control requirements: $_"
                }
            }
            
            # 4. REQUIREMENT 10: Log and monitor all access
            if ($RequirementFilter -in @('All', 'Monitoring')) {
                Write-Host "`n[4/6] Testing Requirement 10: Logging and Monitoring..." -ForegroundColor Cyan
                
                try {
                    # Req 10.2: Audit logs for all users
                    $logonGuid = Get-AuditSubcategoryGuid -Name 'Logon'
                    $logonAuditValue = $null
                    if ($null -ne $logonGuid) {
                        $logonTable = Get-AuditPolicyTable -Subcategory 'Logon'
                        $logonKey = ($logonGuid -replace '[{}]', '').ToLowerInvariant()
                        if ($logonTable.ContainsKey($logonKey)) { $logonAuditValue = $logonTable[$logonKey].Value }
                    }

                    $req = [PSCustomObject]@{
                        RequirementID = '10.2.1'
                        RequirementName = 'User Access Logging'
                        Category = 'Monitoring'
                        Description = 'All access to system components must be logged'
                        Expected = 'Logon auditing: Success and Failure'
                        Actual = if ($null -eq $logonAuditValue) { 'Unable to determine (insufficient privileges or unsupported platform)' }
                                 elseif ($logonAuditValue -eq 3) { "Enabled" } else { "Not configured" }
                        Status = if ($null -eq $logonAuditValue) { 'NotApplicable' }
                                 elseif ($logonAuditValue -eq 3) { 'InPlace' } else { 'NotInPlace' }
                        Priority = 'Critical'
                        Remediation = 'Enable audit policy for Logon events'
                    }
                    $results.Requirements += $req

                    # Req 10.3: Audit trail content
                    $fileSystemGuid = Get-AuditSubcategoryGuid -Name 'File System'
                    $objectAuditValue = $null
                    if ($null -ne $fileSystemGuid) {
                        $fileSystemTable = Get-AuditPolicyTable -Subcategory 'File System'
                        $fileSystemKey = ($fileSystemGuid -replace '[{}]', '').ToLowerInvariant()
                        if ($fileSystemTable.ContainsKey($fileSystemKey)) { $objectAuditValue = $fileSystemTable[$fileSystemKey].Value }
                    }

                    $req = [PSCustomObject]@{
                        RequirementID = '10.3.1'
                        RequirementName = 'Audit Trail Details'
                        Category = 'Monitoring'
                        Description = 'Logs must contain sufficient detail'
                        Expected = 'File system auditing enabled'
                        Actual = if ($null -eq $objectAuditValue) { 'Unable to determine (insufficient privileges or unsupported platform)' }
                                 elseif ($objectAuditValue -ge 1 -and $objectAuditValue -le 3) { "Enabled" } else { "Not configured" }
                        Status = if ($null -eq $objectAuditValue) { 'NotApplicable' }
                                 elseif ($objectAuditValue -ge 1 -and $objectAuditValue -le 3) { 'InPlace' } else { 'NotInPlace' }
                        Priority = 'High'
                        Remediation = 'Enable file system object access auditing'
                    }
                    $results.Requirements += $req
                    
                    # Req 10.5: Protect audit trails
                    $securityLog = Get-WinEvent -ListLog Security -ErrorAction SilentlyContinue
                    $eventLogSize = if ($securityLog) { $securityLog.MaximumSizeInBytes } else { 0 }
                    
                    $req = [PSCustomObject]@{
                        RequirementID = '10.5.1'
                        RequirementName = 'Audit Trail Protection'
                        Category = 'Monitoring'
                        Description = 'Audit trails must be secured and retained'
                        Expected = 'Security log >= 100MB'
                        Actual = "$([math]::Round($eventLogSize/1MB)) MB"
                        Status = if ($eventLogSize -ge 100MB) { 'InPlace' } else { 'NotInPlace' }
                        Priority = 'High'
                        Remediation = 'Increase Security log size to at least 100MB'
                    }
                    $results.Requirements += $req
                    
                    # Req 10.7: Time synchronization
                    $w32timeService = Get-Service -Name W32Time -ErrorAction SilentlyContinue
                    
                    $req = [PSCustomObject]@{
                        RequirementID = '10.7.1'
                        RequirementName = 'Time Synchronization'
                        Category = 'Monitoring'
                        Description = 'System clocks must be synchronized'
                        Expected = 'Windows Time service running'
                        Actual = if ($w32timeService) { $w32timeService.Status } else { "Not found" }
                        Status = if ($w32timeService -and $w32timeService.Status -eq 'Running') { 'InPlace' } else { 'NotInPlace' }
                        Priority = 'Medium'
                        Remediation = 'Ensure Windows Time service is running and configured'
                    }
                    $results.Requirements += $req
                }
                catch {
                    Write-Warning "Error testing Monitoring requirements: $_"
                }
            }
            
            # 5. REQUIREMENT 11: Test security systems regularly
            if ($RequirementFilter -in @('All', 'Testing')) {
                Write-Host "`n[5/6] Testing Requirement 11: Security Testing..." -ForegroundColor Cyan
                
                try {
                    # Req 11.3: Vulnerability scanning
                    $defenderStatus = Get-MpComputerStatus -ErrorAction SilentlyContinue
                    $defenderEnabled = ($null -ne $defenderStatus) -and $defenderStatus.AntivirusEnabled
                    
                    $req = [PSCustomObject]@{
                        RequirementID = '11.3.1'
                        RequirementName = 'Vulnerability Management'
                        Category = 'Testing'
                        Description = 'Vulnerability scans must be performed'
                        Expected = 'Antivirus enabled'
                        Actual = if ($defenderEnabled) { "Windows Defender enabled" } else { "Disabled" }
                        Status = if ($defenderEnabled) { 'InPlace' } else { 'NotInPlace' }
                        Priority = 'Critical'
                        Remediation = 'Enable Windows Defender or approved antivirus'
                    }
                    $results.Requirements += $req
                    
                    # Req 11.5: Change detection
                    $req = [PSCustomObject]@{
                        RequirementID = '11.5.1'
                        RequirementName = 'File Integrity Monitoring'
                        Category = 'Testing'
                        Description = 'Implement file integrity monitoring'
                        Expected = 'FIM solution deployed'
                        Actual = 'Manual verification required'
                        Status = 'NotApplicable'
                        Priority = 'High'
                        Remediation = 'Implement file integrity monitoring (FIM) solution'
                    }
                    $results.Requirements += $req
                }
                catch {
                    Write-Warning "Error testing Security Testing requirements: $_"
                }
            }
            
            # 6. REQUIREMENT 12: Support information security with organizational policies
            if ($RequirementFilter -in @('All')) {
                Write-Host "`n[6/6] Testing Requirement 12: Security Policies..." -ForegroundColor Cyan
                
                try {
                    # Req 12.1: Security policy
                    $req = [PSCustomObject]@{
                        RequirementID = '12.1.1'
                        RequirementName = 'Information Security Policy'
                        Category = 'Policy'
                        Description = 'Establish security policy'
                        Expected = 'Security policy documented and reviewed annually'
                        Actual = 'Manual verification required'
                        Status = 'NotApplicable'
                        Priority = 'High'
                        Remediation = 'Document and maintain information security policy'
                    }
                    $results.Requirements += $req
                    
                    # Req 12.10: Incident response plan
                    $req = [PSCustomObject]@{
                        RequirementID = '12.10.1'
                        RequirementName = 'Incident Response Plan'
                        Category = 'Policy'
                        Description = 'Implement incident response plan'
                        Expected = 'Incident response plan documented and tested'
                        Actual = 'Manual verification required'
                        Status = 'NotApplicable'
                        Priority = 'Critical'
                        Remediation = 'Create and test incident response procedures'
                    }
                    $results.Requirements += $req
                }
                catch {
                    Write-Warning "Error testing Policy requirements: $_"
                }
            }
            
            # Calculate summary
            $results.Summary.TotalRequirements = $results.Requirements.Count
            $results.Summary.InPlace = @($results.Requirements | Where-Object { $_.Status -eq 'InPlace' }).Count
            $results.Summary.NotInPlace = @($results.Requirements | Where-Object { $_.Status -eq 'NotInPlace' }).Count
            $results.Summary.NotApplicable = @($results.Requirements | Where-Object { $_.Status -eq 'NotApplicable' }).Count
            
            $testableRequirements = $results.Summary.TotalRequirements - $results.Summary.NotApplicable
            if ($testableRequirements -gt 0) {
                $results.Summary.CompliancePercentage = [math]::Round(
                    ($results.Summary.InPlace / $testableRequirements) * 100, 2
                )
            }
            
            # Generate recommendations
            $criticalNotInPlace = @($results.Requirements | Where-Object { $_.Status -eq 'NotInPlace' -and $_.Priority -eq 'Critical' })
            $highNotInPlace = @($results.Requirements | Where-Object { $_.Status -eq 'NotInPlace' -and $_.Priority -eq 'High' })
            
            if ($criticalNotInPlace.Count -gt 0) {
                $results.Recommendations += "CRITICAL: $($criticalNotInPlace.Count) critical requirement(s) not in place"
                $results.Recommendations += "Address critical gaps immediately to achieve compliance"
            }
            
            if ($highNotInPlace.Count -gt 0) {
                $results.Recommendations += "HIGH: $($highNotInPlace.Count) high-priority requirement(s) not in place"
            }
            
            if ($results.Summary.CompliancePercentage -lt 100) {
                $results.Recommendations += "Full PCI-DSS compliance requires all requirements to be in place"
            }
            
            $results.Recommendations += "Engage a Qualified Security Assessor (QSA) for formal validation"
            $results.Recommendations += "Review PCI-DSS Self-Assessment Questionnaire (SAQ) for your merchant level"
            
        }
        catch {
            Write-Error "Error during PCI-DSS compliance testing: $_"
            throw
        }
    }
    
    end {
        Write-Host "`n=== COMPLIANCE TEST COMPLETE ===" -ForegroundColor $(
            if ($results.Summary.CompliancePercentage -eq 100) { 'Green' }
            elseif ($results.Summary.CompliancePercentage -ge 80) { 'Yellow' }
            else { 'Red' }
        )
        
        Write-Host "`nCompliance Summary:" -ForegroundColor Cyan
        Write-Host " Total Requirements Tested: $($results.Summary.TotalRequirements)" -ForegroundColor White
        Write-Host " In Place: $($results.Summary.InPlace)" -ForegroundColor Green
        Write-Host " Not In Place: $($results.Summary.NotInPlace)" -ForegroundColor Red
        Write-Host " Not Applicable: $($results.Summary.NotApplicable)" -ForegroundColor Gray
        Write-Host " Compliance: $($results.Summary.CompliancePercentage)%" -ForegroundColor $(
            if ($results.Summary.CompliancePercentage -eq 100) { 'Green' }
            elseif ($results.Summary.CompliancePercentage -ge 80) { 'Yellow' }
            else { 'Red' }
        )
        
        if ($results.Requirements.Count -gt 0) {
            $notInPlace = @($results.Requirements | Where-Object { $_.Status -eq 'NotInPlace' })
            if ($notInPlace.Count -gt 0) {
                Write-Host "`nGaps by Priority:" -ForegroundColor Cyan
                $gapsByPriority = $notInPlace | Group-Object Priority
                foreach ($group in $gapsByPriority | Sort-Object Name) {
                    Write-Host " $($group.Name): $($group.Count)" -ForegroundColor $(
                        switch ($group.Name) {
                            'Critical' { 'Red' }
                            'High' { 'Red' }
                            'Medium' { 'Yellow' }
                            default { 'White' }
                        }
                    )
                }
            }
        }
        
        if ($results.Recommendations.Count -gt 0) {
            Write-Host "`nRecommendations:" -ForegroundColor Cyan
            $results.Recommendations | ForEach-Object {
                Write-Host " ! $_" -ForegroundColor Yellow
            }
        }
        
        Write-Host "`nPCI Security Standards: https://www.pcisecuritystandards.org/" -ForegroundColor Gray
        Write-Host "Note: This is a preliminary assessment. Formal PCI-DSS compliance requires QSA validation." -ForegroundColor Yellow
        
        return $results
    }
}