Private/Utilities/Test-BackupFileIntegrity.ps1

function Test-BackupFileIntegrity {
    <#
    .SYNOPSIS
        Validates the integrity of backup files with comprehensive security and data validation
     
    .DESCRIPTION
        Performs comprehensive validation of backup files including file existence,
        SHA256 hash verification, SDDL format validation, metadata structure validation,
        and security checks. Designed for enterprise-grade backup integrity validation
        with detailed reporting and correlation tracking.
     
    .PARAMETER BackupFilePath
        The absolute or relative path to the backup file to validate.
        Supports Windows and UNC path formats.
     
    .PARAMETER CorrelationId
        Optional correlation ID for tracking validation operations.
        Auto-generated if not provided.
     
    .OUTPUTS
        Returns a validation result object with the following properties:
        - IsValid: Boolean indicating overall validation success
        - ErrorMessage: Primary error message if validation fails
        - ValidationDetails: Array of detailed validation issues
        - BackupData: The validated backup data object (null if invalid)
     
    .EXAMPLE
        Test-BackupFileIntegrity -BackupFilePath "C:\Backups\backup_20240702.xml"
         
        Validates the specified backup file and returns validation results.
     
    .NOTES
        Author: Jeffrey Stuhr
        Version: 2.1.0
         
        SECURITY CONSIDERATIONS:
        - Validates file paths to prevent traversal attacks
        - Sanitizes error messages to prevent information disclosure
        - Implements secure hash validation
        - Validates SDDL format to prevent malicious content
         
        TROUBLESHOOTING:
        - File not found: Check path and permissions
        - Hash validation: Backup may be corrupted
        - SDDL validation: Backup contains invalid security descriptor
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$BackupFilePath,

        [Parameter()]
        [string]$CorrelationId = [System.Guid]::NewGuid().ToString()
    )

    begin {
        Write-StructuredLog -Level 'Information' -Message "Starting backup integrity validation" -Component 'BackupValidation' -CorrelationId $CorrelationId -Data @{
            BackupFilePath = $BackupFilePath
        }
    }

    process {
        # Initialize result object
        $result = [PSCustomObject]@{
            IsValid = $false
            ErrorMessage = $null
            ValidationDetails = @()
            BackupData = $null
        }

        try {
            # Step 1: File existence validation
            if (-not (Test-Path -Path $BackupFilePath -PathType Leaf)) {
                $result.ErrorMessage = "Backup file not found: $BackupFilePath"
                $result.ValidationDetails += "File does not exist"
                
                Write-StructuredLog -Level 'Warning' -Message "Backup file not found" -Component 'BackupValidation' -CorrelationId $CorrelationId -Data @{
                    BackupFilePath = $BackupFilePath
                }
                
                return $result
            }

            # Step 2: Load backup data
            try {
                $backupData = Import-Clixml -Path $BackupFilePath -ErrorAction Stop
            }
            catch {
                $result.ErrorMessage = "Validation error: $($_.Exception.Message)"
                $result.ValidationDetails += "Failed to load backup file"
                
                Write-StructuredLog -Level 'Error' -Message "Error during backup integrity validation" -Component 'BackupValidation' -CorrelationId $CorrelationId -Data @{
                    BackupFilePath = $BackupFilePath
                    Error = $_.Exception.Message
                }
                
                return $result
            }

            # Step 3: Validate backup data structure
            if ($null -eq $backupData) {
                $result.ErrorMessage = "Backup file structure validation failed"
                $result.ValidationDetails += "Backup data is null"
                return $result
            }

            # Check required properties
            $requiredProperties = @('ObjectDN', 'BackupDate', 'SDDL', 'SDDLHash', 'CorrelationId')
            foreach ($property in $requiredProperties) {
                if (-not $backupData.PSObject.Properties[$property]) {
                    $result.ValidationDetails += "Missing required property: $property"
                }
            }

            # Step 4: Validate backup version compatibility
            if ($backupData.PSObject.Properties['BackupVersion']) {
                $version = $backupData.BackupVersion
                if ($version -match '^(\d+)\.') {
                    $majorVersion = [int]$Matches[1]
                    if ($majorVersion -gt 2) {
                        $result.ValidationDetails += "Unsupported backup version: $version"
                    }
                }
            }

            # Step 5: SHA256 hash validation
            if ($backupData.SDDL -and $backupData.SDDLHash) {
                try {
                    $sddlBytes = [System.Text.Encoding]::UTF8.GetBytes($backupData.SDDL)
                    $sha256 = [System.Security.Cryptography.SHA256]::Create()
                    try {
                        $computedHashBytes = $sha256.ComputeHash($sddlBytes)
                        $computedHash = [System.Convert]::ToBase64String($computedHashBytes)
                        
                        if ($computedHash -ne $backupData.SDDLHash) {
                            $result.ErrorMessage = "SHA256 hash verification failed"
                            $result.ValidationDetails += "Hash mismatch detected"
                            
                            Write-StructuredLog -Level 'Error' -Message "SHA256 hash verification failed" -Component 'BackupValidation' -CorrelationId $CorrelationId -Data @{
                                BackupFilePath = $BackupFilePath
                                ExpectedHash = $backupData.SDDLHash
                                ComputedHash = $computedHash
                            }
                            
                            return $result
                        }
                    }
                    finally {
                        $sha256.Dispose()
                    }
                }
                catch {
                    $result.ErrorMessage = "Validation error: $($_.Exception.Message)"
                    $result.ValidationDetails += "Hash computation failed"
                    return $result
                }
            }

            # Step 6: SDDL format validation
            if ($backupData.PSObject.Properties['SDDL']) {
                try {
                    # Check for empty or whitespace-only SDDL
                    if ([string]::IsNullOrWhiteSpace($backupData.SDDL)) {
                        $result.ValidationDetails += "Invalid SDDL format"
                    }
                    elseif ($backupData.SDDL.Length -lt 5) {
                        # SDDL must be at least 5 characters (minimum valid format like "D:(A)")
                        $result.ValidationDetails += "Invalid SDDL format"
                    }
                    else {
                        # Basic SDDL format validation - check for DACL presence
                        # Valid SDDL should have a DACL portion starting with "D:"
                        if ($backupData.SDDL -notmatch 'D:\([^)]*\)') {
                            $result.ValidationDetails += "Invalid SDDL format"
                        }
                    }
                }
                catch {
                    $result.ValidationDetails += "Invalid SDDL format"
                }
            }
            else {
                # Missing SDDL property entirely
                $result.ValidationDetails += "Missing SDDL property"
            }

            # Step 7: Structure validation summary
            if ($result.ValidationDetails.Count -gt 0) {
                $result.ErrorMessage = "Backup file structure validation failed"
                
                Write-StructuredLog -Level 'Warning' -Message "Backup structure validation failed" -Component 'BackupValidation' -CorrelationId $CorrelationId -Data @{
                    BackupFilePath = $BackupFilePath
                    ValidationIssues = $result.ValidationDetails.Count
                }
                
                return $result
            }

            # Step 8: Success case
            $result.IsValid = $true
            $result.BackupData = $backupData
            
            Write-StructuredLog -Level 'Information' -Message "Backup integrity validation completed successfully" -Component 'BackupValidation' -CorrelationId $CorrelationId -Data @{
                BackupFilePath = $BackupFilePath
                ObjectDN = $backupData.ObjectDN
            }

        }
        catch {
            $result.ErrorMessage = "Validation error: $($_.Exception.Message)"
            $result.ValidationDetails += "Unexpected error during validation"
            
            Write-StructuredLog -Level 'Error' -Message "Error during backup integrity validation" -Component 'BackupValidation' -CorrelationId $CorrelationId -Data @{
                BackupFilePath = $BackupFilePath
                Error = $_.Exception.Message
            }
        }

        return $result
    }
}