Classes/LS2Issue.ps1

class LS2Issue {
    # Core issue identification
    [string]$Technique              # ESC1, ESC2, ESC6, etc.
    [string]$Forest                 # Forest where issue was found
    [string]$Name                   # Friendly name of vulnerable object
    [string]$DistinguishedName      # DN of vulnerable object
    
    # Principal information (for permission-based issues)
    [string]$IdentityReference      # DOMAIN\User or group name
    [string]$IdentityReferenceSID   # SID of the principal
    [string]$ActiveDirectoryRights  # GenericAll, ExtendedRight, etc.
    
    # Template-specific properties
    [Nullable[bool]]$Enabled        # Whether template is enabled on any CA
    [string[]]$EnabledOn            # List of CAs where template is enabled
    
    # CA-specific properties
    [string]$CAFullName             # For CA issues: SERVER\CA
    
    # Ownership properties
    [string]$Owner                  # Owner of the vulnerable object
    [Nullable[bool]]$HasNonStandardOwner  # Whether object has non-standard owner
    
    # Group expansion properties
    [Nullable[int]]$MemberCount     # For group issues: number of members expanded
    
    # Issue details
    [string]$Issue                  # Description of the vulnerability
    [string]$Fix                    # PowerShell script to remediate
    [string]$Revert                 # PowerShell script to undo remediation
    
    # Constructor for creating issues from hashtable
    LS2Issue([hashtable]$Properties) {
        # Core properties
        $this.Technique = $Properties.Technique
        $this.Forest = $Properties.Forest
        $this.Name = $Properties.Name
        $this.DistinguishedName = $Properties.DistinguishedName
        
        # Principal properties (may be null for non-permission issues)
        $this.IdentityReference = $Properties.IdentityReference
        $this.IdentityReferenceSID = $Properties.IdentityReferenceSID
        $this.ActiveDirectoryRights = $Properties.ActiveDirectoryRights
        
        # Template properties (may be null for CA issues)
        $this.Enabled = $Properties.Enabled
        $this.EnabledOn = $Properties.EnabledOn
        
        # CA properties (may be null for template issues)
        $this.CAFullName = $Properties.CAFullName
        
        # Ownership properties
        $this.Owner = $Properties.Owner
        $this.HasNonStandardOwner = $Properties.HasNonStandardOwner
        
        # Issue details
        $this.Issue = $Properties.Issue
        $this.Fix = $Properties.Fix
        $this.Revert = $Properties.Revert
    }
    
    # Method to get a friendly identifier for the issue
    [string] GetIdentifier() {
        if ($this.IdentityReference) {
            return "$($this.Technique): $($this.Name) - $($this.IdentityReference)"
        } elseif ($this.Owner) {
            return "$($this.Technique): $($this.Name) - Owner: $($this.Owner)"
        } else {
            return "$($this.Technique): $($this.Name)"
        }
    }
    
    # Method to check if this is a permission-based issue
    [bool] HasPrincipal() {
        return -not [string]::IsNullOrEmpty($this.IdentityReference)
    }
    
    # Method to check if this is a template issue
    [bool] IsTemplateIssue() {
        return $null -ne $this.Enabled
    }
    
    # Method to check if this is a CA issue
    [bool] IsCAIssue() {
        return -not [string]::IsNullOrEmpty($this.CAFullName)
    }
    
    # Method to check if this issue matches another issue (for deduplication)
    [bool] Matches([LS2Issue]$Other) {
        if ($null -eq $Other) {
            return $false
        }
        
        # Core properties must match
        if ($this.Technique -ne $Other.Technique) { return $false }
        if ($this.DistinguishedName -ne $Other.DistinguishedName) { return $false }
        
        # Principal properties must match (null-safe comparison)
        if ($this.IdentityReferenceSID -ne $Other.IdentityReferenceSID) { return $false }
        if ($this.ActiveDirectoryRights -ne $Other.ActiveDirectoryRights) { return $false }
        
        # CA property must match
        if ($this.CAFullName -ne $Other.CAFullName) { return $false }
        
        # Owner must match
        if ($this.Owner -ne $Other.Owner) { return $false }
        
        return $true
    }
}