Private/Logging/Write-SecurityLogEvent.ps1

#Requires -Version 5.1

<#
.SYNOPSIS
    Security-specific logging operations for Find-UnknownSID
 
.DESCRIPTION
    Provides specialized logging functionality for security events with enhanced
    protection, audit trail generation, and compliance with security requirements.
    Focuses solely on security event logging with appropriate sanitization and context.
 
.NOTES
    Author: Jeffrey Stuhr
    Blog: https://www.techbyjeff.net
    LinkedIn: https://www.linkedin.com/in/jeffrey-stuhr-034214aa/
    Last Updated: 2025-07-05
    Version: 2.0.0
 
    TROUBLESHOOTING:
    - For security logging issues: .\Troubleshooting\Security\Security-Logging.md
    - For audit compliance: .\Troubleshooting\Security\Audit-Requirements.md
 
.LINK
    https://docs.microsoft.com/en-us/security/compass/privileged-access-security-levels
#>



function Write-SecurityLogEvent {
    <#
    .SYNOPSIS
        Writes security-specific log entries with enhanced protection and audit trails
 
    .DESCRIPTION
        Specialized logging function for security events that require additional
        protection, sanitization of sensitive data, and compliance with audit
        requirements. This function ensures security events are properly logged
        with appropriate context and protection measures.
 
        Security events are automatically categorized and include enhanced
        metadata for compliance and forensic analysis.
 
    .PARAMETER SecurityEventType
        Type of security event being logged
 
    .PARAMETER Message
        Security event description (will be sanitized automatically)
 
    .PARAMETER Outcome
        Event outcome classification for security analysis
 
    .PARAMETER CorrelationId
        Correlation ID for tracking security operations across log entries
 
    .PARAMETER SecurityContext
        Additional security context data (sensitive values will be redacted)
 
    .PARAMETER Severity
        Security severity level for the event
 
    .EXAMPLE
        PS> Write-SecurityLogEvent -SecurityEventType "DataValidation" -Message "Input validated successfully" -Outcome "Success"
 
        Logs a successful data validation security event with default context
 
    .EXAMPLE
        PS> Write-SecurityLogEvent -SecurityEventType "CredentialAccess" -Message "Failed authentication attempt" -Outcome "Failure" -SecurityContext @{Source="RemoteIP"; User="testuser"}
 
        Logs a failed authentication attempt with additional security context
 
    .INPUTS
        None. This function does not accept pipeline input.
 
    .OUTPUTS
        None. This function writes to security logging subsystem.
 
    .NOTES
        All security events are automatically enhanced with:
        - Process and user context information
        - Timestamp with high precision
        - Correlation ID for operation tracking
        - Sanitized security context data
        - Appropriate log level assignment based on outcome
 
        SECURITY CONSIDERATIONS:
        - Sensitive data is automatically redacted (passwords, tokens, keys)
        - Context data is sanitized to prevent injection attacks
        - Event classification enables automated security monitoring
        - Audit trail information meets compliance requirements
 
        PERFORMANCE CHARACTERISTICS:
        - Execution time: 2-8ms depending on context size
        - Memory usage: Minimal (context hashtable only)
        - Audit overhead: Designed for high-frequency security events
 
        TROUBLESHOOTING:
        - Failed security events are elevated to Warning level for visibility
        - Correlation IDs enable cross-system event tracking
        - Context sanitization prevents log corruption
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateSet('DataValidation', 'CredentialAccess', 'PrivilegeUse', 'ObjectAccess', 'SystemAccess', 'ConfigurationChange', 'AuthenticationAttempt', 'AuthorizationCheck')]
        [string]$SecurityEventType,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Message,

        [Parameter()]
        [ValidateSet('Success', 'Failure', 'Attempt', 'Warning', 'Critical')]
        [string]$Outcome = 'Attempt',

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

        [Parameter()]
        [hashtable]$SecurityContext = @{},

        [Parameter()]
        [ValidateSet('Low', 'Medium', 'High', 'Critical')]
        [string]$Severity = 'Medium'
    )

    begin {
        Write-Verbose "Writing security log event - Type: $SecurityEventType, Outcome: $Outcome"
    }

    process {
        try {
            # Sanitize the security message
            $sanitizedMessage = Protect-LogMessage -Message $Message

            # Build enhanced security context
            $securityData = Build-SecurityContext -SecurityEventType $SecurityEventType -Outcome $Outcome -SecurityContext $SecurityContext -Severity $Severity -CorrelationId $CorrelationId

            # Determine appropriate log level based on outcome and severity
            $logLevel = Get-SecurityLogLevel -Outcome $Outcome -Severity $Severity

            # Write to structured logging system with security component
            Write-StructuredLogEntry -Message $sanitizedMessage -Level $logLevel -Component 'Security' -Details $securityData -CorrelationId $CorrelationId

            # Write additional audit trail entry for critical events
            if ($Outcome -eq 'Critical' -or $Severity -eq 'Critical') {
                Write-CriticalSecurityEvent -SecurityEventType $SecurityEventType -Message $sanitizedMessage -SecurityData $securityData -CorrelationId $CorrelationId
            }
        }
        catch {
            Write-Warning "Failed to write security log event: $($_.Exception.Message)"
            # Don't throw to prevent security logging failures from breaking operations
        }
    }

    end {
        Write-Verbose "Security log event write completed"
    }
}


function Build-SecurityContext {
    <#
    .SYNOPSIS
        Builds comprehensive security context for audit events
 
    .DESCRIPTION
        Creates standardized security context information including system
        details, user context, and sanitized additional data for security events.
 
    .PARAMETER SecurityEventType
        Type of security event
 
    .PARAMETER Outcome
        Event outcome
 
    .PARAMETER SecurityContext
        Additional context data
 
    .PARAMETER Severity
        Security severity level
 
    .PARAMETER CorrelationId
        Correlation ID for tracking
 
    .EXAMPLE
        PS> Build-SecurityContext -SecurityEventType "DataValidation" -Outcome "Success" -SecurityContext @{Source="API"}
 
        Returns hashtable with comprehensive security context
 
    .INPUTS
        None. This function does not accept pipeline input.
 
    .OUTPUTS
        [hashtable] Comprehensive security context data
 
    .NOTES
        Automatically includes system and user context information.
        Sanitizes sensitive data to prevent credential exposure.
    #>

    [CmdletBinding()]
    [OutputType([hashtable])]
    param(
        [Parameter(Mandatory)]
        [string]$SecurityEventType,

        [Parameter(Mandatory)]
        [string]$Outcome,

        [Parameter()]
        [hashtable]$SecurityContext = @{},

        [Parameter()]
        [string]$Severity = 'Medium',

        [Parameter()]
        [string]$CorrelationId
    )

    try {
        # Build base security context
        $baseContext = @{
            EventType = $SecurityEventType
            Outcome = $Outcome
            Severity = $Severity
            Timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss.fff'
            UserContext = "$env:USERNAME@$env:COMPUTERNAME"
            Domain = $env:USERDOMAIN
            ProcessId = [System.Diagnostics.Process]::GetCurrentProcess().Id
            ThreadId = [System.Threading.Thread]::CurrentThread.ManagedThreadId
            CorrelationId = $CorrelationId
        }

        # Add sanitized additional context
        $sanitizedContext = Protect-SecurityContext -SecurityContext $SecurityContext

        # Merge contexts
        foreach ($key in $sanitizedContext.Keys) {
            $baseContext[$key] = $sanitizedContext[$key]
        }

        return $baseContext
    }
    catch {
        Write-Warning "Failed to build security context: $($_.Exception.Message)"
        # Return minimal context to ensure logging continues
        return @{
            EventType = $SecurityEventType
            Outcome = $Outcome
            Timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
            CorrelationId = $CorrelationId
        }
    }
}


function Protect-SecurityContext {
    <#
    .SYNOPSIS
        Sanitizes security context data to prevent credential exposure
 
    .DESCRIPTION
        Processes security context hashtable to identify and redact
        sensitive information while preserving audit trail value.
 
    .PARAMETER SecurityContext
        Security context data to sanitize
 
    .EXAMPLE
        PS> Protect-SecurityContext -SecurityContext @{Username="user"; Password="secret"}
 
        Returns: @{Username="user"; Password="[REDACTED]"}
 
    .INPUTS
        None. This function does not accept pipeline input.
 
    .OUTPUTS
        [hashtable] Sanitized security context data
 
    .NOTES
        Identifies sensitive fields by name pattern matching.
        Preserves structure while protecting sensitive values.
    #>

    [CmdletBinding()]
    [OutputType([hashtable])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNull()]
        [hashtable]$SecurityContext
    )

    $sanitizedContext = @{}

    foreach ($key in $SecurityContext.Keys) {
        $value = $SecurityContext[$key]

        # Check if this is a sensitive field that should be redacted
        if ($key -match '(?i)(password|secret|token|key|credential|api|auth|private)') {
            if ($value -and $value.ToString().Length -gt 0) {
                $sanitizedContext[$key] = '[REDACTED]'
            } else {
                $sanitizedContext[$key] = '[EMPTY]'
            }
        } else {
            # Sanitize the value using log message protection
            if ($null -ne $value) {
                $sanitizedContext[$key] = Protect-LogMessage -Message $value.ToString()
            } else {
                $sanitizedContext[$key] = '[NULL]'
            }
        }
    }

    return $sanitizedContext
}


function Get-SecurityLogLevel {
    <#
    .SYNOPSIS
        Determines appropriate log level for security events
 
    .DESCRIPTION
        Maps security event outcomes and severity levels to appropriate
        log levels for proper visibility and filtering.
 
    .PARAMETER Outcome
        Security event outcome
 
    .PARAMETER Severity
        Security severity level
 
    .EXAMPLE
        PS> Get-SecurityLogLevel -Outcome "Failure" -Severity "High"
 
        Returns: "Error"
 
    .INPUTS
        None. This function does not accept pipeline input.
 
    .OUTPUTS
        [string] Appropriate log level for the security event
 
    .NOTES
        Balances security visibility with log noise reduction.
        Critical and high-severity events get elevated visibility.
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory)]
        [ValidateSet('Success', 'Failure', 'Attempt', 'Warning', 'Critical')]
        [string]$Outcome,

        [Parameter()]
        [ValidateSet('Low', 'Medium', 'High', 'Critical')]
        [string]$Severity = 'Medium'
    )

    # Map outcome and severity to log levels
    $logLevel = switch ($Outcome) {
        'Critical' { 'Critical' }
        'Failure' {
            switch ($Severity) {
                'Critical' { 'Critical' }
                'High' { 'Error' }
                'Medium' { 'Warning' }
                'Low' { 'Information' }
                default { 'Warning' }
            }
        }
        'Warning' { 'Warning' }
        'Success' {
            switch ($Severity) {
                'Critical' { 'Information' }
                'High' { 'Information' }
                default { 'Debug' }
            }
        }
        'Attempt' {
            switch ($Severity) {
                'Critical' { 'Warning' }
                'High' { 'Information' }
                default { 'Debug' }
            }
        }
        default { 'Debug' }
    }

    return $logLevel
}


function Write-CriticalSecurityEvent {
    <#
    .SYNOPSIS
        Writes critical security events with enhanced visibility
 
    .DESCRIPTION
        Handles critical security events that require immediate attention
        and additional logging measures for security incident response.
 
    .PARAMETER SecurityEventType
        Type of critical security event
 
    .PARAMETER Message
        Critical event message
 
    .PARAMETER SecurityData
        Security context data
 
    .PARAMETER CorrelationId
        Correlation ID for tracking
 
    .EXAMPLE
        PS> Write-CriticalSecurityEvent -SecurityEventType "PrivilegeEscalation" -Message "Unauthorized privilege use detected"
 
        Writes critical security event with enhanced logging
 
    .INPUTS
        None. This function does not accept pipeline input.
 
    .OUTPUTS
        None. This function writes to logging and alerting systems.
 
    .NOTES
        Critical events may trigger additional alerting mechanisms.
        Enhanced context is provided for security incident response.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$SecurityEventType,

        [Parameter(Mandatory)]
        [string]$Message,

        [Parameter()]
        [hashtable]$SecurityData = @{},

        [Parameter()]
        [string]$CorrelationId
    )

    try {
        # Create critical event marker for easy identification
        $criticalMessage = "*** CRITICAL SECURITY EVENT *** $SecurityEventType : $Message"

        # Enhanced critical event context
        $criticalContext = $SecurityData.Clone()
        $criticalContext['CriticalEvent'] = $true
        $criticalContext['AlertLevel'] = 'IMMEDIATE'
        $criticalContext['IncidentResponse'] = 'REQUIRED'

        # Write with Critical level for maximum visibility
        Write-StructuredLogEntry -Message $criticalMessage -Level 'Critical' -Component 'SecurityAlert' -Details $criticalContext -CorrelationId $CorrelationId

        # Additional console warning for critical events
        Write-Warning "CRITICAL SECURITY EVENT: $SecurityEventType - $Message (CorrelationId: $CorrelationId)"
    }
    catch {
        Write-Error "Failed to write critical security event: $($_.Exception.Message)" -ErrorAction Continue
    }
}