Core/Themis.Core.psm1

<#
.SYNOPSIS
    ThemisRules Engine v2.0 (Modular Core)
    Universal Compliance & Policy Enforcement for Windows
 
.DESCRIPTION
    Dynamically loads providers from '\Providers' and dispatches rules.
    Features: Parallel Execution, Provider Abstraction.
 
.AUTHOR
    Cassiel Security Team
#>


# Load Providers Globals
$ProviderCache = @{}

function Initialize-ThemisProviders {
    $providerPath = Join-Path $PSScriptRoot "..\Providers"
    $modules = Get-ChildItem -Path $providerPath -Filter "Themis.Provider.*.psm1"
    
    foreach ($mod in $modules) {
        Import-Module $mod.FullName -Force -Scope Global
        
        # Detect Type from filename (Themis.Provider.Registry.psm1 -> Registry)
        if ($mod.Name -match "Themis\.Provider\.(.+)\.psm1") {
            $type = $matches[1]
            $cmdlet = "Invoke-Themis${type}Rule"
            
            if (Get-Command $cmdlet -ErrorAction SilentlyContinue) {
                $script:ProviderCache[$type] = $cmdlet
                Write-Verbose "[Themis] Registered Provider: $type -> $cmdlet"
            }
        }
    }
}

function Invoke-ThemisPolicy {
    <#
    .SYNOPSIS
        Executes a security policy with optional level-based filtering.
     
    .PARAMETER PolicyPath
        Path to the policy JSON file.
     
    .PARAMETER Mode
        Execution mode: Audit (check only) or Enforce (apply changes).
     
    .PARAMETER MacroLevel
        Filter rules by hardening level (L1-L5). Only rules <= specified level are evaluated.
        Example: -MacroLevel "L1" evaluates only L1 rules.
     
    .PARAMETER Parallel
        Enable parallel execution (experimental).
     
    .EXAMPLE
        Invoke-ThemisPolicy -PolicyPath ".\system_security.json" -Mode Audit -MacroLevel "L1"
    #>

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

        [Parameter(Mandatory = $false)]
        [ValidateSet("Audit", "Enforce")]
        [string]$Mode = "Audit",

        [Parameter(Mandatory = $false)]
        [ValidatePattern('^L[1-5]$')]
        [string]$MacroLevel = $null,

        [Parameter(Mandatory = $false)]
        [switch]$Parallel = $false
    )

    # 1. Initialize
    if ($script:ProviderCache.Count -eq 0) { Initialize-ThemisProviders }

    if (-not (Test-Path $PolicyPath)) {
        Write-Error "[Themis] Policy not found: $PolicyPath"
        return $null
    }

    $policy = Get-Content $PolicyPath -Raw | ConvertFrom-Json
    $policyName = if ($policy.Meta.Name) { $policy.Meta.Name } else { [System.IO.Path]::GetFileNameWithoutExtension($PolicyPath) }
    $policyVersion = if ($policy.Meta.Version) { $policy.Meta.Version } else { "1.0.0" }
    
    Write-Verbose "Loaded Policy: $policyName v$policyVersion"
    if ($MacroLevel) {
        Write-Verbose "Filtering by MacroLevel: $MacroLevel"
    }

    # 2. Flatten Rules with MacroLevel Filtering
    $allRules = @()
    $targetLevel = if ($MacroLevel) { [int]($MacroLevel -replace 'L', '') } else { 999 }
    
    # Iterate known provider types in the JSON
    foreach ($prop in $policy.PSObject.Properties) {
        if ($prop.Name -match "(.+)Rules") {
            $type = $matches[1] # "Registry" from "RegistryRules"
            
            if ($script:ProviderCache.ContainsKey($type)) {
                foreach ($rule in $prop.Value) {
                    # Apply MacroLevel filter
                    $ruleLevel = 999 # Default: include if no MacroLevel specified
                    if ($rule.MacroLevel -and $rule.MacroLevel -match '^L([1-5])$') {
                        $ruleLevel = [int]$matches[1]
                    }
                    
                    # Only include rules <= target level
                    if ($ruleLevel -le $targetLevel) {
                        # Inject Type and Metadata into Rule Object
                        $rule | Add-Member -MemberType NoteProperty -Name "_ProviderType" -Value $type -Force
                        $rule | Add-Member -MemberType NoteProperty -Name "_PolicyName" -Value $policyName -Force
                        $rule | Add-Member -MemberType NoteProperty -Name "_PolicyPath" -Value $PolicyPath -Force
                        $allRules += $rule
                    }
                    else {
                        Write-Verbose "Skipping rule $($rule.ID) (Level: $($rule.MacroLevel), Target: $MacroLevel)"
                    }
                }
            }
        }
    }

    Write-Verbose "Total rules after filtering: $($allRules.Count)"
    $results = @()
    
    # 3. Execution
    if ($Parallel) {
        Write-Verbose "Parallel flag ignored in v1.2.0 (Stability focus)."
    }

    foreach ($rule in $allRules) {
        $type = $rule._ProviderType
        $cmdlet = $script:ProviderCache[$type]
        
        Write-Verbose "Processing Rule: $($rule.ID) ($type)"
        
        # Dispatch
        try {
            $res = & $cmdlet -Rule $rule -Mode $Mode
            
            # Enrich Result with Enhanced Metadata
            $obj = [PSCustomObject]@{
                Timestamp   = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
                Policy      = $rule._PolicyName
                PolicyPath  = $rule._PolicyPath
                ID          = $rule.ID
                Type        = $type
                Name        = $rule.Name
                MacroLevel  = $rule.MacroLevel
                Status      = $res.Status
                IsCompliant = $res.IsCompliant
                Reason      = $res.Reason
                Actual      = $res.Actual
            }
            $results += $obj

        }
        catch {
            Write-Error "Dispatch Error ($type / $($rule.ID)): $_"
        }
    }

    return $results
}

Export-ModuleMember -Function Invoke-ThemisPolicy