noid-privacy/NoIDPrivacy.ps1

539 lines
22 KiB
PowerShell

#Requires -Version 5.1
#Requires -RunAsAdministrator
<#
.SYNOPSIS
NoID Privacy - Professional Windows 11 Security & Privacy Hardening Framework
.DESCRIPTION
Enterprise-grade security hardening for Windows 11 implementing:
- Microsoft Security Baseline (425+ settings)
- Attack Surface Reduction (19 rules)
- Secure DNS configuration
- AI features disable
- Telemetry & privacy controls
- And more...
.PARAMETER Module
Specific module to run (SecurityBaseline, ASR, DNS, etc.)
If not specified, shows interactive menu
.PARAMETER DryRun
Preview changes without applying them
.PARAMETER VerboseLogging
Enable verbose logging output
.PARAMETER Config
Path to custom configuration file (default: config.json)
.EXAMPLE
.\NoIDPrivacy.ps1
Interactive menu mode
.EXAMPLE
.\NoIDPrivacy.ps1 -Module SecurityBaseline
Run only the Security Baseline module
.EXAMPLE
.\NoIDPrivacy.ps1 -Module ASR -DryRun
Preview ASR rule changes without applying
.EXAMPLE
.\NoIDPrivacy.ps1 -Module All -VerboseLogging
Run all enabled modules with verbose logging
.NOTES
DISCLAIMER:
This software is provided "as is" without warranty of any kind.
By using this software, you agree that the authors are not liable for any damages
resulting from its use. USE AT YOUR OWN RISK.
Author: NexusOne23
Version: 2.2.2
Requires: PowerShell 5.1+, Administrator privileges, Windows 11
License: GPL-3.0 (Core CLI). See LICENSE for full terms.
.OUTPUTS
Exit Codes for CI/CD Integration:
0 = SUCCESS - All operations completed successfully
1 = ERROR_GENERAL - General/unspecified error
2 = ERROR_PREREQUISITES - System requirements not met (OS, PowerShell, Admin)
3 = ERROR_CONFIG - Configuration file error (missing, invalid JSON)
4 = ERROR_MODULE - One or more modules failed during execution
5 = ERROR_FATAL - Fatal/unexpected exception
10 = SUCCESS_REBOOT - Success, but reboot is required for changes to take effect
Example CI/CD usage:
$exitCode = (Start-Process powershell -ArgumentList "-File NoIDPrivacy.ps1 -Module All" -Wait -PassThru).ExitCode
if ($exitCode -eq 0 -or $exitCode -eq 10) { "Success" } else { "Failed with code $exitCode" }
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[ValidateSet(
"SecurityBaseline",
"ASR",
"DNS",
"Privacy",
"AntiAI",
"EdgeHardening",
"AdvancedSecurity",
"All"
)]
[string]$Module,
[Parameter(Mandatory = $false)]
[switch]$DryRun,
[Parameter(Mandatory = $false)]
[switch]$VerboseLogging,
[Parameter(Mandatory = $false)]
[string]$ConfigPath
)
# Enable strict mode for better error detection
Set-StrictMode -Version Latest
# ============================================================================
# RESET BACKUP STATE - Each NoIDPrivacy.ps1 call gets a fresh session
# ============================================================================
# This ensures multiple runs from Interactive Menu create separate sessions
$global:BackupBasePath = ""
$global:BackupIndex = @()
$global:NewlyCreatedKeys = @()
$global:SessionManifest = @{}
$global:CurrentModule = ""
# ============================================================================
# EXIT CODES - For CI/CD and automation integration
# ============================================================================
$script:EXIT_SUCCESS = 0 # All operations completed successfully
$script:EXIT_ERROR_GENERAL = 1 # General/unspecified error
$script:EXIT_ERROR_PREREQUISITES = 2 # System requirements not met
$script:EXIT_ERROR_CONFIG = 3 # Configuration file error
$script:EXIT_ERROR_MODULE = 4 # One or more modules failed
$script:EXIT_ERROR_FATAL = 5 # Fatal/unexpected exception
$script:EXIT_SUCCESS_REBOOT = 10 # Success, reboot required
# Script root path
$script:RootPath = $PSScriptRoot
# Import Core modules
Write-Host "Loading NoID Privacy Framework..." -ForegroundColor Cyan
Write-Host ""
try {
# Load Logger first
. (Join-Path $script:RootPath "Core\Logger.ps1")
# Initialize logger with absolute path
$logLevel = if ($VerboseLogging) { [LogLevel]::DEBUG } else { [LogLevel]::INFO }
$logDirectory = Join-Path $script:RootPath "Logs"
Initialize-Logger -LogDirectory $logDirectory -MinimumLevel $logLevel
Write-Log -Level INFO -Message "=== NoID Privacy Framework v2.2.2 ===" -Module "Main"
Write-Log -Level INFO -Message "Starting framework initialization..." -Module "Main"
# Load other Core modules
. (Join-Path $script:RootPath "Core\Config.ps1")
. (Join-Path $script:RootPath "Core\Validator.ps1")
. (Join-Path $script:RootPath "Core\Rollback.ps1")
. (Join-Path $script:RootPath "Core\NonInteractive.ps1") # Must load BEFORE Framework for GUI mode
. (Join-Path $script:RootPath "Core\Framework.ps1")
# Load Utils
. (Join-Path $script:RootPath "Utils\Registry.ps1")
. (Join-Path $script:RootPath "Utils\Service.ps1")
. (Join-Path $script:RootPath "Utils\Hardware.ps1")
# NOTE: Utils\GPO.ps1 removed - v2.0 SecurityBaseline is self-contained
. (Join-Path $script:RootPath "Utils\Localization.ps1")
. (Join-Path $script:RootPath "Utils\Compatibility.ps1")
. (Join-Path $script:RootPath "Utils\Dependencies.ps1")
Write-Log -Level SUCCESS -Message "All core modules loaded successfully" -Module "Main"
}
catch {
Write-Host "" -ForegroundColor Red
Write-Host "==========================================================" -ForegroundColor Red
Write-Host "FATAL ERROR: Failed to load core framework modules" -ForegroundColor Red
Write-Host "==========================================================" -ForegroundColor Red
Write-Host "Error: $_" -ForegroundColor Red
Write-Host "Location: $($_.InvocationInfo.ScriptName):$($_.InvocationInfo.ScriptLineNumber)" -ForegroundColor Red
Write-Host "Stack Trace: $($_.ScriptStackTrace)" -ForegroundColor Red
Write-Host "" -ForegroundColor Red
Write-Host "Please ensure all framework files are present and not corrupted." -ForegroundColor Yellow
exit $script:EXIT_ERROR_FATAL
}
# Load configuration
try {
Write-Log -Level INFO -Message "Loading configuration..." -Module "Main"
# Check for ConfigPath from environment variable (GUI mode)
if ([string]::IsNullOrEmpty($ConfigPath) -and $env:NOIDPRIVACY_CONFIGPATH) {
$ConfigPath = $env:NOIDPRIVACY_CONFIGPATH
}
if ($ConfigPath) {
Initialize-Config -ConfigPath $ConfigPath
}
else {
Initialize-Config
}
Write-Log -Level SUCCESS -Message "Configuration loaded" -Module "Main"
}
catch {
Write-Log -Level ERROR -Message "Failed to load configuration file" -Module "Main" -Exception $_.Exception
Write-Host "ERROR: Configuration file error - check config.json syntax" -ForegroundColor Red
exit $script:EXIT_ERROR_CONFIG
}
# Validate prerequisites (full framework pre-flight: system, domain, backup)
try {
Write-Log -Level INFO -Message "Validating framework prerequisites..." -Module "Main"
$ok = Test-FrameworkPrerequisites
if (-not $ok) {
Write-Log -Level ERROR -Message "Framework prerequisites failed" -Module "Main"
Write-Host "ERROR: Prerequisite checks failed. See log for details." -ForegroundColor Red
exit $script:EXIT_ERROR_PREREQUISITES
}
Write-Log -Level SUCCESS -Message "Framework prerequisites met" -Module "Main"
}
catch {
Write-ErrorLog -Message "Framework prerequisite validation failed" -Module "Main" -ErrorRecord $_
Write-Host "ERROR: System requirements not met - see log for details" -ForegroundColor Red
exit $script:EXIT_ERROR_PREREQUISITES
}
# Display banner
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " NoID Privacy - v2.2.2" -ForegroundColor Cyan
Write-Host " Windows 11 Security Hardening" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
if ($DryRun) {
Write-Host "[DRY RUN MODE - No changes will be applied]" -ForegroundColor Yellow
Write-Host ""
}
# Interactive menu or direct module execution
if (-not $Module) {
# Show interactive menu
Write-Host "Available Actions:" -ForegroundColor White
Write-Host ""
Write-Host " APPLY HARDENING:" -ForegroundColor Cyan
Write-Host " 1. SecurityBaseline - Microsoft Security Baseline (425+ settings)" -ForegroundColor Green
Write-Host " 2. ASR - Attack Surface Reduction (19 rules)" -ForegroundColor Green
Write-Host " 3. DNS - Secure DNS with DoH (Quad9/Cloudflare/AdGuard)" -ForegroundColor Green
Write-Host " 4. Privacy - Telemetry & Privacy hardening (3 modes)" -ForegroundColor Green
Write-Host " 5. AntiAI - Disable Windows AI features (15 features, 32 policies)" -ForegroundColor Green
Write-Host " 6. EdgeHardening - Secure Microsoft Edge browser" -ForegroundColor Green
Write-Host " 7. AdvancedSecurity - Legacy Protocol hardening, Windows Update, SRP (CVE-2025-9491)" -ForegroundColor Green
Write-Host " 99. ALL MODULES (WIZARD) - Interactive setup for all modules" -ForegroundColor Cyan
Write-Host ""
Write-Host " SYSTEM OPERATIONS:" -ForegroundColor Cyan
Write-Host " V. Verify Settings - Check up to 630+ hardening settings" -ForegroundColor Magenta
Write-Host " R. Restore Backup - Rollback to previous state" -ForegroundColor Yellow
Write-Host " B. List Backups - Show all available backups" -ForegroundColor Gray
Write-Host " I. System Info - Display system information" -ForegroundColor Gray
Write-Host ""
Write-Host " 0. Exit" -ForegroundColor Red
Write-Host ""
do {
$selection = Read-Host "Select option [1-7, 99, V, R, B, I, 0] (default: 99)"
if ([string]::IsNullOrWhiteSpace($selection)) { $selection = "99" }
$selection = $selection.ToUpper()
if ($selection -notin @('1','2','3','4','5','6','7','99','V','R','B','I','0')) {
Write-Host ""
Write-Host "Invalid selection. Please choose from the menu." -ForegroundColor Red
Write-Host ""
}
} while ($selection -notin @('1','2','3','4','5','6','7','99','V','R','B','I','0'))
switch ($selection) {
"1" { $Module = "SecurityBaseline" }
"2" { $Module = "ASR" }
"3" { $Module = "DNS" }
"4" { $Module = "Privacy" }
"5" { $Module = "AntiAI" }
"6" { $Module = "EdgeHardening" }
"7" { $Module = "AdvancedSecurity" }
"99" { $Module = "All" }
"V" {
# Verify all settings
Write-Host ""
Write-Host "Running complete verification..." -ForegroundColor Cyan
Write-Host ""
$verifyScript = Join-Path $script:RootPath "Tools\Verify-Complete-Hardening.ps1"
if (Test-Path $verifyScript) {
# Discard return value so that 'True' / 'False' is not printed to console
$null = & $verifyScript
}
else {
Write-Host "ERROR: Verification script not found" -ForegroundColor Red
}
Write-Host ""
Write-Host "Press any key to exit..." -ForegroundColor Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
exit 0
}
"R" {
# Restore from backup - Interactive session selection from disk
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " RESTORE FROM BACKUP" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
# Get all backup sessions from disk
$sessions = Get-BackupSessions
if ($sessions.Count -eq 0) {
Write-Host "No backup sessions found." -ForegroundColor Yellow
Write-Host "Backups are created when you apply hardening modules." -ForegroundColor Gray
Write-Host ""
Write-Host "Press any key to continue..." -ForegroundColor Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
exit 0
}
Write-Host "Available backup sessions:" -ForegroundColor White
Write-Host ""
$i = 1
foreach ($session in $sessions) {
$moduleNames = ($session.Modules | ForEach-Object { $_.name }) -join ", "
$dateStr = $session.Timestamp.ToString("yyyy-MM-dd HH:mm:ss")
Write-Host " [$i] $dateStr" -ForegroundColor Green
Write-Host " Modules: $moduleNames" -ForegroundColor Gray
Write-Host " Items: $($session.TotalItems)" -ForegroundColor Gray
Write-Host ""
$i++
}
Write-Host " [0] Cancel and return" -ForegroundColor Red
Write-Host ""
$selection = Read-Host "Select session to restore [1-$($sessions.Count), 0=Cancel]"
if ($selection -eq "0" -or [string]::IsNullOrWhiteSpace($selection)) {
Write-Host "Restore cancelled." -ForegroundColor Yellow
exit 0
}
$selIndex = [int]$selection - 1
if ($selIndex -lt 0 -or $selIndex -ge $sessions.Count) {
Write-Host "Invalid selection." -ForegroundColor Red
exit $script:EXIT_ERROR_GENERAL
}
$selectedSession = $sessions[$selIndex]
Write-Host ""
Write-Host "Restoring session: $($selectedSession.SessionId)" -ForegroundColor Cyan
Write-Host ""
# Call Restore-Session with the session path
$success = Restore-Session -SessionPath $selectedSession.FolderPath
if ($success) {
Write-Host ""
Write-Host "Restore completed successfully!" -ForegroundColor Green
}
else {
Write-Host ""
Write-Host "Restore completed with some errors. Check logs for details." -ForegroundColor Yellow
}
Write-Host ""
Write-Host "Press any key to exit..." -ForegroundColor Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
exit 0
}
"B" {
# List backups
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " Available Backups" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
$backupPath = Join-Path $script:RootPath "Backups"
if (Test-Path $backupPath) {
$backups = Get-ChildItem -Path $backupPath -Directory | Sort-Object CreationTime -Descending
if ($backups.Count -eq 0) {
Write-Host " No backups found" -ForegroundColor Yellow
}
else {
Write-Host " Found $($backups.Count) backup(s):" -ForegroundColor White
Write-Host ""
foreach ($backup in $backups) {
$age = (Get-Date) - $backup.CreationTime
$ageStr = if ($age.TotalHours -lt 1) { "$([math]::Round($age.TotalMinutes)) minutes ago" }
elseif ($age.TotalDays -lt 1) { "$([math]::Round($age.TotalHours)) hours ago" }
else { "$([math]::Round($age.TotalDays)) days ago" }
Write-Host " - $($backup.Name)" -ForegroundColor Green -NoNewline
Write-Host " ($ageStr)" -ForegroundColor Gray
}
}
}
else {
Write-Host " Backup directory not found" -ForegroundColor Yellow
}
Write-Host ""
Write-Host "Press any key to exit..." -ForegroundColor Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
exit 0
}
"I" {
# System information
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " System Information" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
try {
$os = Get-CimInstance Win32_OperatingSystem
$cs = Get-CimInstance Win32_ComputerSystem
Write-Host " Computer Name: $($cs.Name)" -ForegroundColor White
Write-Host " OS Version: $($os.Caption) Build $($os.BuildNumber)" -ForegroundColor White
Write-Host " PowerShell: $($PSVersionTable.PSVersion)" -ForegroundColor White
Write-Host " Domain Joined: $(if ($cs.PartOfDomain) { 'Yes' } else { 'No (Standalone)' })" -ForegroundColor White
Write-Host ""
Write-Host " Security Status:" -ForegroundColor Yellow
# Check VBS
try {
$vbs = Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard -ErrorAction SilentlyContinue
if ($vbs) {
Write-Host " VBS Enabled: $(if ($vbs.VirtualizationBasedSecurityStatus -eq 2) { 'Yes' } else { 'No' })" -ForegroundColor $(if ($vbs.VirtualizationBasedSecurityStatus -eq 2) { 'Green' } else { 'Red' })
}
}
catch { $null = $null } # Intentionally ignore VBS query errors
# Check Defender
try {
$defender = Get-MpComputerStatus -ErrorAction SilentlyContinue
if ($defender) {
Write-Host " Defender Active: $(if ($defender.AntivirusEnabled) { 'Yes' } else { 'No' })" -ForegroundColor $(if ($defender.AntivirusEnabled) { 'Green' } else { 'Red' })
}
}
catch { $null = $null } # Intentionally ignore Defender query errors
}
catch {
Write-Host " Failed to retrieve system information" -ForegroundColor Red
}
Write-Host ""
Write-Host "Press any key to exit..." -ForegroundColor Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
exit 0
}
"0" {
Write-Host "Exiting..." -ForegroundColor Yellow
exit 0
}
}
}
# Execute selected module(s)
try {
Write-Log -Level INFO -Message "Starting module execution: $Module" -Module "Main"
$result = Invoke-Hardening -Module $Module -DryRun:$DryRun
# Handle array return (pipeline contamination protection)
if ($result -is [array]) {
Write-Log -Level DEBUG -Message "Invoke-Hardening returned array ($($result.Count) items), using last element" -Module "Main"
$result = $result[-1]
}
# Display results
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " Execution Results" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
if ($result.Success) {
Write-Host "Status: SUCCESS" -ForegroundColor Green
Write-Host "Modules Executed: $($result.ModulesExecuted)" -ForegroundColor White
Write-Host "Duration: $($result.Duration.TotalSeconds) seconds" -ForegroundColor White
}
else {
Write-Host "Status: FAILED" -ForegroundColor Red
Write-Host "Errors: $($result.Errors.Count)" -ForegroundColor Red
if ($result.Errors.Count -gt 0) {
Write-Host ""
Write-Host "Error Details:" -ForegroundColor Red
foreach ($errMsg in $result.Errors) {
Write-Host " - $errMsg" -ForegroundColor Red
}
}
}
if ($result.Warnings.Count -gt 0) {
Write-Host ""
Write-Host "Warnings: $($result.Warnings.Count)" -ForegroundColor Yellow
foreach ($warning in $result.Warnings) {
Write-Host " - $warning" -ForegroundColor Yellow
}
}
Write-Host ""
Write-Host "Log file: $(Get-LogFilePath)" -ForegroundColor Cyan
Write-Host ""
if ($result.Success) {
Write-Log -Level SUCCESS -Message "Framework execution completed successfully" -Module "Main"
# Check if reboot is recommended (certain modules modify kernel/driver settings)
$rebootModules = @("SecurityBaseline", "AdvancedSecurity", "AntiAI")
$executedModules = if ($Module -eq "All") { $rebootModules } else { @($Module) }
$needsReboot = @($executedModules | Where-Object { $_ -in $rebootModules }).Count -gt 0
if ($needsReboot -and -not $DryRun) {
Write-Host ""
Write-Host "NOTE: A system reboot is recommended for all changes to take effect." -ForegroundColor Yellow
exit $script:EXIT_SUCCESS_REBOOT
}
else {
exit $script:EXIT_SUCCESS
}
}
else {
Write-Log -Level ERROR -Message "Framework execution completed with errors" -Module "Main"
exit $script:EXIT_ERROR_MODULE
}
}
catch {
Write-ErrorLog -Message "Fatal error during framework execution" -Module "Main" -ErrorRecord $_
Write-Host ""
Write-Host "FATAL ERROR: Unexpected exception during execution" -ForegroundColor Red
Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red
Write-Host ""
exit $script:EXIT_ERROR_FATAL
}