From 46d45c45c0420503fc1edc8489b4a2f6cbf4339b Mon Sep 17 00:00:00 2001 From: NexusOne23 Date: Mon, 22 Dec 2025 06:50:37 +0100 Subject: [PATCH 01/15] Add v2.2.2 release notes --- RELEASE_NOTES_v2.2.2.md | 160 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 RELEASE_NOTES_v2.2.2.md diff --git a/RELEASE_NOTES_v2.2.2.md b/RELEASE_NOTES_v2.2.2.md new file mode 100644 index 0000000..8974f0b --- /dev/null +++ b/RELEASE_NOTES_v2.2.2.md @@ -0,0 +1,160 @@ +# 🚀 NoID Privacy v2.2.2 - Performance Release +**Major Performance Fix for Firewall Snapshot Operations** + +This is a performance release with a critical fix for slow firewall backup operations that could take 60-120 seconds. + +## 🌟 Highlights + +- ✅ **Performance Fix** - Firewall snapshot 60-120s → 2-5s +- ✅ **633 Security Settings** across 7 independent modules +- ✅ **100% BAVR Coverage** - Backup, Apply, Verify, Restore +- ✅ **100% Restore Accuracy** - VM tested & verified +- ✅ **Version Alignment** - All 60+ framework files synchronized +- ✅ **GPL v3.0 License** - Dual-licensing available +- ✅ **Production-Ready** - Tested on Windows 11 24H2/25H2 + +--- + +## ⚡ What's New in v2.2.2 + +### Firewall Snapshot Performance Fix (Critical) + +| Before | After | +|--------|-------| +| 60-120 seconds | 2-5 seconds | + +- **Problem:** Firewall rules backup took 60-120 seconds, especially in offline mode +- **Root Cause:** `Get-NetFirewallPortFilter` was called individually for each of ~300+ firewall rules (~200ms per call) +- **Fix:** Batch query approach - load all port filters once into hashtable, then fast lookup by InstanceID +- **Affected Files:** + - `Modules/AdvancedSecurity/Private/Backup-AdvancedSecuritySettings.ps1` + - `Modules/AdvancedSecurity/Private/Disable-RiskyPorts.ps1` + +### Version Alignment + +- All 60+ framework files updated to v2.2.2 +- Module manifests (.psd1), module loaders (.psm1), core scripts, utilities, tests, and documentation synchronized + +--- + +## 📦 Module Overview + +| Module | Settings | Description | +|--------|----------|-------------| +| **SecurityBaseline** | 425 | Microsoft Security Baseline 25H2 | +| **ASR** | 19 | Attack Surface Reduction Rules | +| **DNS** | 5 | Secure DNS with DoH encryption | +| **Privacy** | 78 | Telemetry, Bloatware, OneDrive hardening | +| **AntiAI** | 32 | AI Lockdown (Recall, Copilot, Click to Do) | +| **EdgeHardening** | 24 | Microsoft Edge v139 Baseline | +| **AdvancedSecurity** | 50 | Beyond MS Baseline (15 features) | +| **TOTAL** | **633** | **Complete Hardening** | + +--- + +## 🚀 Quick Start + +### One-Liner Install: +```powershell +irm https://raw.githubusercontent.com/NexusOne23/noid-privacy/main/install.ps1 | iex +``` + +### Manual Install: +1. Download **Source code (zip)** below +2. Extract to a folder +3. Run `Start-NoIDPrivacy.bat` as Administrator + +### Verify After Installation: +```powershell +.\Tools\Verify-Complete-Hardening.ps1 + +# Expected output: +# SecurityBaseline: 425/425 verified +# ASR: 19/19 verified +# DNS: 5/5 verified +# Privacy: 78/78 verified +# AntiAI: 32/32 verified +# EdgeHardening: 24/24 verified +# AdvancedSecurity: 50/50 verified +# Total: 633/633 (100%) +``` + +--- + +## 🎯 System Requirements + +| Requirement | Specification | +|-------------|---------------| +| **OS** | Windows 11 24H2 (Build 26100+) or 25H2 (Build 26200+) | +| **PowerShell** | 5.1+ (built-in) | +| **Admin Rights** | Required | +| **TPM** | 2.0 (for BitLocker, Credential Guard, VBS) | +| **RAM** | 8 GB minimum (16 GB recommended for VBS) | + +> ⚠️ **Note:** Windows 11 23H2 and older are **not supported**. Please update to 24H2 or newer. + +--- + +## 🛡️ Antivirus Compatibility + +| Your Setup | What Happens | Coverage | +|------------|--------------|----------| +| **Defender Active** | All modules applied | 633 settings (100%) | +| **3rd-Party AV** | ASR skipped, all other modules applied | 614 settings (~97%) | + +--- + +## 📋 Full Changelog + +See [CHANGELOG.md](CHANGELOG.md) + +--- + +## 📜 License + +| Version | License | +|---------|---------| +| v1.8.3 and earlier | MIT License | +| v2.0.0 and later | GPL v3.0 + Commercial dual-licensing | + +See [LICENSE](LICENSE) + +--- + +## 🔐 Code Quality & Testing + +- **Testing:** Unit and integration tests available in `Tests/` directory +- **Verification:** 633 automated compliance checks in production +- **VM Tested:** Full Apply → Verify → Restore cycle verified +- **Performance:** Firewall operations now complete in seconds, not minutes +- **Version Alignment:** All 60+ files now have consistent version numbers +- **Analysis:** Run `.\Tests\Run-Tests.ps1` to validate yourself +- **Report vulnerabilities:** [Security Advisories](https://github.com/NexusOne23/noid-privacy/security/advisories) + +--- + +## 💬 Support & Community + +- 📖 **Documentation:** [README.md](README.md) +- 💬 **Discussions:** [GitHub Discussions](https://github.com/NexusOne23/noid-privacy/discussions) +- 🐛 **Issues:** [GitHub Issues](https://github.com/NexusOne23/noid-privacy/issues) +- 💼 **Commercial Licensing:** Contact via Discussions + +--- + +## ⚠️ Important Warnings + +- ⚠️ **Create a system backup** before running (CRITICAL!) +- ⚠️ **Test in a VM first** (recommended) +- ⚠️ **Domain-joined systems:** Coordinate with IT team +- ⚠️ **Read documentation** thoroughly + +--- + +
+ +**Made with 🛡️ for the Windows Security Community** + +**NexusOne23** • **v2.2.2** • **December 2025** + +
From 044cabf6c49a3b7be653faf569c8049382338703 Mon Sep 17 00:00:00 2001 From: NexusOne23 Date: Mon, 22 Dec 2025 15:51:11 +0100 Subject: [PATCH 02/15] chore: update bug_report template to v2.2.2 --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- RELEASE_NOTES_v2.2.2.md | 160 --------------------------- 2 files changed, 1 insertion(+), 161 deletions(-) delete mode 100644 RELEASE_NOTES_v2.2.2.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 76e3adb..f5e8f7f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -32,7 +32,7 @@ A clear description of what actually happened. - **CPU**: [e.g., AMD Ryzen 7 9800X3D] - **TPM**: [e.g., 2.0 Present] - **Third-Party AV**: [e.g., None, Windows Defender only] -- **Script Version**: [e.g., v2.2.1] +- **Script Version**: [e.g., v2.2.2] - **Execution Mode**: [Interactive / Direct / DryRun] **Get System Info:** diff --git a/RELEASE_NOTES_v2.2.2.md b/RELEASE_NOTES_v2.2.2.md deleted file mode 100644 index 8974f0b..0000000 --- a/RELEASE_NOTES_v2.2.2.md +++ /dev/null @@ -1,160 +0,0 @@ -# 🚀 NoID Privacy v2.2.2 - Performance Release -**Major Performance Fix for Firewall Snapshot Operations** - -This is a performance release with a critical fix for slow firewall backup operations that could take 60-120 seconds. - -## 🌟 Highlights - -- ✅ **Performance Fix** - Firewall snapshot 60-120s → 2-5s -- ✅ **633 Security Settings** across 7 independent modules -- ✅ **100% BAVR Coverage** - Backup, Apply, Verify, Restore -- ✅ **100% Restore Accuracy** - VM tested & verified -- ✅ **Version Alignment** - All 60+ framework files synchronized -- ✅ **GPL v3.0 License** - Dual-licensing available -- ✅ **Production-Ready** - Tested on Windows 11 24H2/25H2 - ---- - -## ⚡ What's New in v2.2.2 - -### Firewall Snapshot Performance Fix (Critical) - -| Before | After | -|--------|-------| -| 60-120 seconds | 2-5 seconds | - -- **Problem:** Firewall rules backup took 60-120 seconds, especially in offline mode -- **Root Cause:** `Get-NetFirewallPortFilter` was called individually for each of ~300+ firewall rules (~200ms per call) -- **Fix:** Batch query approach - load all port filters once into hashtable, then fast lookup by InstanceID -- **Affected Files:** - - `Modules/AdvancedSecurity/Private/Backup-AdvancedSecuritySettings.ps1` - - `Modules/AdvancedSecurity/Private/Disable-RiskyPorts.ps1` - -### Version Alignment - -- All 60+ framework files updated to v2.2.2 -- Module manifests (.psd1), module loaders (.psm1), core scripts, utilities, tests, and documentation synchronized - ---- - -## 📦 Module Overview - -| Module | Settings | Description | -|--------|----------|-------------| -| **SecurityBaseline** | 425 | Microsoft Security Baseline 25H2 | -| **ASR** | 19 | Attack Surface Reduction Rules | -| **DNS** | 5 | Secure DNS with DoH encryption | -| **Privacy** | 78 | Telemetry, Bloatware, OneDrive hardening | -| **AntiAI** | 32 | AI Lockdown (Recall, Copilot, Click to Do) | -| **EdgeHardening** | 24 | Microsoft Edge v139 Baseline | -| **AdvancedSecurity** | 50 | Beyond MS Baseline (15 features) | -| **TOTAL** | **633** | **Complete Hardening** | - ---- - -## 🚀 Quick Start - -### One-Liner Install: -```powershell -irm https://raw.githubusercontent.com/NexusOne23/noid-privacy/main/install.ps1 | iex -``` - -### Manual Install: -1. Download **Source code (zip)** below -2. Extract to a folder -3. Run `Start-NoIDPrivacy.bat` as Administrator - -### Verify After Installation: -```powershell -.\Tools\Verify-Complete-Hardening.ps1 - -# Expected output: -# SecurityBaseline: 425/425 verified -# ASR: 19/19 verified -# DNS: 5/5 verified -# Privacy: 78/78 verified -# AntiAI: 32/32 verified -# EdgeHardening: 24/24 verified -# AdvancedSecurity: 50/50 verified -# Total: 633/633 (100%) -``` - ---- - -## 🎯 System Requirements - -| Requirement | Specification | -|-------------|---------------| -| **OS** | Windows 11 24H2 (Build 26100+) or 25H2 (Build 26200+) | -| **PowerShell** | 5.1+ (built-in) | -| **Admin Rights** | Required | -| **TPM** | 2.0 (for BitLocker, Credential Guard, VBS) | -| **RAM** | 8 GB minimum (16 GB recommended for VBS) | - -> ⚠️ **Note:** Windows 11 23H2 and older are **not supported**. Please update to 24H2 or newer. - ---- - -## 🛡️ Antivirus Compatibility - -| Your Setup | What Happens | Coverage | -|------------|--------------|----------| -| **Defender Active** | All modules applied | 633 settings (100%) | -| **3rd-Party AV** | ASR skipped, all other modules applied | 614 settings (~97%) | - ---- - -## 📋 Full Changelog - -See [CHANGELOG.md](CHANGELOG.md) - ---- - -## 📜 License - -| Version | License | -|---------|---------| -| v1.8.3 and earlier | MIT License | -| v2.0.0 and later | GPL v3.0 + Commercial dual-licensing | - -See [LICENSE](LICENSE) - ---- - -## 🔐 Code Quality & Testing - -- **Testing:** Unit and integration tests available in `Tests/` directory -- **Verification:** 633 automated compliance checks in production -- **VM Tested:** Full Apply → Verify → Restore cycle verified -- **Performance:** Firewall operations now complete in seconds, not minutes -- **Version Alignment:** All 60+ files now have consistent version numbers -- **Analysis:** Run `.\Tests\Run-Tests.ps1` to validate yourself -- **Report vulnerabilities:** [Security Advisories](https://github.com/NexusOne23/noid-privacy/security/advisories) - ---- - -## 💬 Support & Community - -- 📖 **Documentation:** [README.md](README.md) -- 💬 **Discussions:** [GitHub Discussions](https://github.com/NexusOne23/noid-privacy/discussions) -- 🐛 **Issues:** [GitHub Issues](https://github.com/NexusOne23/noid-privacy/issues) -- 💼 **Commercial Licensing:** Contact via Discussions - ---- - -## ⚠️ Important Warnings - -- ⚠️ **Create a system backup** before running (CRITICAL!) -- ⚠️ **Test in a VM first** (recommended) -- ⚠️ **Domain-joined systems:** Coordinate with IT team -- ⚠️ **Read documentation** thoroughly - ---- - -
- -**Made with 🛡️ for the Windows Security Community** - -**NexusOne23** • **v2.2.2** • **December 2025** - -
From 79eb8100964d48e0a4689d379d1377c1c1cb3505 Mon Sep 17 00:00:00 2001 From: NexusOne23 Date: Mon, 22 Dec 2025 22:38:14 +0100 Subject: [PATCH 03/15] fix: HTML report shows all 633 settings + improved print layout --- Tools/Verify-Complete-Hardening.ps1 | 68 ++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/Tools/Verify-Complete-Hardening.ps1 b/Tools/Verify-Complete-Hardening.ps1 index 49e0346..6aadf2a 100644 --- a/Tools/Verify-Complete-Hardening.ps1 +++ b/Tools/Verify-Complete-Hardening.ps1 @@ -2991,8 +2991,8 @@ try { } .module-content { - max-height: 5000px; - overflow: hidden; + max-height: none; + overflow: visible; transition: max-height 0.3s ease; } @@ -3140,6 +3140,70 @@ try { .controls, .export-section { display: none; } + /* Balanced header for print */ + .header { + padding: 1.5rem 2rem; + page-break-inside: avoid; + } + .header h1 { + font-size: 1.8rem; + margin-bottom: 0.3rem; + } + .header .subtitle { + font-size: 1rem; + } + .header .badge { + margin-top: 0.5rem; + padding: 0.4rem 1.2rem; + font-size: 0.85rem; + } + /* Balanced meta-info for print */ + .meta-info { + padding: 1rem 1.5rem; + gap: 1rem; + page-break-inside: avoid; + } + .meta-label { + font-size: 0.65rem; + } + .meta-value { + font-size: 0.95rem; + } + /* Balanced dashboard for print */ + .dashboard { + padding: 1rem 1.5rem; + page-break-inside: avoid; + } + .stats-grid { + page-break-inside: avoid; + display: flex; + flex-wrap: nowrap; + gap: 1rem; + margin-bottom: 1rem; + } + .stat-card { + flex: 1; + min-width: 0; + padding: 1rem; + } + .stat-value { + font-size: 2rem; + } + .stat-label { + font-size: 0.75rem; + } + .progress-section { + margin: 0.75rem 0; + page-break-inside: avoid; + } + .progress-bar-container { + height: 40px; + } + .progress-bar-fill { + background: #10b981 !important; + -webkit-print-color-adjust: exact !important; + print-color-adjust: exact !important; + } .module-section { page-break-inside: avoid; } From 4c1af02ad93912106d4266a69cbe4b9cfcb3446f Mon Sep 17 00:00:00 2001 From: NexusOne23 Date: Mon, 22 Dec 2025 23:17:22 +0100 Subject: [PATCH 04/15] Security hardening: PATH hijack fix, SHA256 checksums, connectivity endpoint - Start-NoIDPrivacy.bat: Use absolute System32 path for powershell.exe - Core/Validator.ps1: Replace 8.8.8.8 with www.msftconnecttest.com - Tools/Generate-ReleaseChecksums.ps1: New script for release checksums - SECURITY.md: Updated verification instructions --- CHANGELOG.md | 5 -- Core/Validator.ps1 | 4 +- SECURITY.md | 8 +++- Start-NoIDPrivacy.bat | 4 +- Tools/Generate-ReleaseChecksums.ps1 | 72 +++++++++++++++++++++++++++++ 5 files changed, 83 insertions(+), 10 deletions(-) create mode 100644 Tools/Generate-ReleaseChecksums.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c244bc..4d62b97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,11 +63,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - All 7 registry policies confirmed correct per MS Policy CSP docs - Version numbers aligned across all 50+ files -### 🔐 Verify Download -``` -SHA256: fdb364d48e67a6889b44a519ba061cd570411158b8bdeb9b91ec845b7e270d96 -``` - --- ## [2.2.0] - 2025-12-08 diff --git a/Core/Validator.ps1 b/Core/Validator.ps1 index 186eca4..df69127 100644 --- a/Core/Validator.ps1 +++ b/Core/Validator.ps1 @@ -174,8 +174,8 @@ function Test-InternetConnectivity { param() try { - # Using Google DNS (8.8.8.8) - intentional for internet connectivity check - $response = Test-Connection -ComputerName "8.8.8.8" -Count 1 -Quiet -ErrorAction Stop + # Using Microsoft NCSI endpoint - same as Windows uses for connectivity detection + $response = Test-Connection -ComputerName "www.msftconnecttest.com" -Count 1 -Quiet -ErrorAction Stop return $response } catch { diff --git a/SECURITY.md b/SECURITY.md index 8f5a7d4..2044910 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -92,9 +92,15 @@ NoID Privacy implements multiple security layers: ### Before Running 1. ✅ **Verify Script Integrity** ```powershell - # Check file hash (coming soon - SHA256 checksums in releases) + # Compare against CHECKSUMS.sha256 from GitHub Release Get-FileHash .\NoIDPrivacy.ps1 -Algorithm SHA256 + + # Or verify the entire release folder: + Get-ChildItem *.ps1, *.psm1 | ForEach-Object { + "$((Get-FileHash $_.FullName -Algorithm SHA256).Hash.ToLower()) $($_.Name)" + } ``` + Each GitHub release includes a `CHECKSUMS.sha256` file with SHA256 hashes of all release files. 2. ✅ **Review Code** - This is open-source - read the code! diff --git a/Start-NoIDPrivacy.bat b/Start-NoIDPrivacy.bat index 09eb3c1..54309b6 100644 --- a/Start-NoIDPrivacy.bat +++ b/Start-NoIDPrivacy.bat @@ -23,7 +23,7 @@ if %errorLevel% == 0 ( REM Already admin, run PowerShell script directly echo Running NoID Privacy Interactive Menu with Administrator privileges... echo. - powershell.exe -ExecutionPolicy Bypass -NoProfile -File "%SCRIPT_DIR%NoIDPrivacy-Interactive.ps1" %* + "%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\powershell.exe" -ExecutionPolicy Bypass -NoProfile -File "%SCRIPT_DIR%NoIDPrivacy-Interactive.ps1" %* pause exit /b ) @@ -33,7 +33,7 @@ echo Requesting Administrator privileges... echo. REM Use PowerShell to elevate and run the script -powershell.exe -Command "Start-Process PowerShell.exe -ArgumentList '-ExecutionPolicy Bypass -NoProfile -File \"%SCRIPT_DIR%NoIDPrivacy-Interactive.ps1\" %*' -Verb RunAs" +"%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\powershell.exe" -Command "Start-Process '%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\powershell.exe' -ArgumentList '-ExecutionPolicy Bypass -NoProfile -File \"%SCRIPT_DIR%NoIDPrivacy-Interactive.ps1\" %*' -Verb RunAs" REM Exit this non-elevated instance exit /b diff --git a/Tools/Generate-ReleaseChecksums.ps1 b/Tools/Generate-ReleaseChecksums.ps1 new file mode 100644 index 0000000..3ddc0a5 --- /dev/null +++ b/Tools/Generate-ReleaseChecksums.ps1 @@ -0,0 +1,72 @@ +<# +.SYNOPSIS + Generates SHA256 checksums for release files. + +.DESCRIPTION + Creates a CHECKSUMS.sha256 file containing SHA256 hashes of all release files. + Used for verifying download integrity. + +.PARAMETER ReleasePath + Path to the release folder or ZIP file(s). + +.PARAMETER OutputFile + Output file for checksums. Default: CHECKSUMS.sha256 in the same directory. + +.EXAMPLE + .\Generate-ReleaseChecksums.ps1 -ReleasePath "C:\Release\NoIDPrivacy-v2.2.2" + +.EXAMPLE + .\Generate-ReleaseChecksums.ps1 -ReleasePath ".\NoIDPrivacy-v2.2.2.zip" +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [string]$ReleasePath, + + [Parameter(Mandatory = $false)] + [string]$OutputFile +) + +$ErrorActionPreference = 'Stop' + +Write-Host "`n=== NoID Privacy Release Checksum Generator ===" -ForegroundColor Cyan + +# Determine if path is file or directory +if (Test-Path $ReleasePath -PathType Container) { + $files = Get-ChildItem -Path $ReleasePath -File -Recurse | Where-Object { $_.Extension -in '.zip', '.exe', '.ps1', '.psm1' } + $basePath = $ReleasePath +} elseif (Test-Path $ReleasePath -PathType Leaf) { + $files = Get-Item $ReleasePath + $basePath = Split-Path $ReleasePath -Parent +} else { + Write-Error "Path not found: $ReleasePath" + exit 1 +} + +if (-not $OutputFile) { + $OutputFile = Join-Path $basePath "CHECKSUMS.sha256" +} + +Write-Host "Generating checksums for $($files.Count) file(s)..." -ForegroundColor Yellow + +$checksums = @() +$checksums += "# NoID Privacy Release Checksums" +$checksums += "# Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss UTC' -AsUTC)" +$checksums += "# Verify with: Get-FileHash -Algorithm SHA256 " +$checksums += "" + +foreach ($file in $files) { + Write-Host " Hashing: $($file.Name)" -ForegroundColor Gray + $hash = (Get-FileHash -Path $file.FullName -Algorithm SHA256).Hash.ToLower() + $relativePath = $file.Name + $checksums += "$hash $relativePath" +} + +$checksums | Out-File -FilePath $OutputFile -Encoding utf8 + +Write-Host "`nChecksums written to: $OutputFile" -ForegroundColor Green +Write-Host "`nContents:" -ForegroundColor Cyan +Get-Content $OutputFile | ForEach-Object { Write-Host " $_" } + +Write-Host "`n=== Done ===" -ForegroundColor Cyan From c4d6d9c995ff88f82b6e7fd3c7dccbe3e114eabf Mon Sep 17 00:00:00 2001 From: NexusOne23 Date: Wed, 24 Dec 2025 03:33:33 +0100 Subject: [PATCH 05/15] Add -NoReboot and -ForceReboot parameters to Restore-Session for GUI automation --- Core/Rollback.ps1 | 63 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/Core/Rollback.ps1 b/Core/Rollback.ps1 index fdce9b7..e9867c9 100644 --- a/Core/Rollback.ps1 +++ b/Core/Rollback.ps1 @@ -1033,11 +1033,23 @@ function Invoke-RestoreRebootPrompt { Offers immediate or deferred reboot with countdown. Uses validation loop for consistent behavior. + .PARAMETER NoReboot + Skip the reboot prompt entirely (for automation/GUI usage) + + .PARAMETER ForceReboot + Automatically reboot without prompting (for automation) + .OUTPUTS None #> [CmdletBinding()] - param() + param( + [Parameter(Mandatory = $false)] + [switch]$NoReboot, + + [Parameter(Mandatory = $false)] + [switch]$ForceReboot + ) Write-Host "" Write-Host "========================================" -ForegroundColor Cyan @@ -1077,7 +1089,32 @@ function Invoke-RestoreRebootPrompt { Write-Host "========================================" -ForegroundColor Cyan Write-Host "" - # Prompt user with validation loop + # Check if running in NonInteractive mode (e.g., from GUI) + $isNonInteractive = [Environment]::GetCommandLineArgs() -contains '-NonInteractive' + + # Handle -ForceReboot: immediately reboot without prompt + if ($ForceReboot) { + Write-Host "" + Write-Host "[>] ForceReboot specified - rebooting system now..." -ForegroundColor Yellow + Write-Host "" + Restart-Computer -Force + return + } + + # Handle -NoReboot or NonInteractive mode: skip the prompt + if ($NoReboot -or $isNonInteractive) { + Write-Host "" + if ($NoReboot) { + Write-Host "[!] NoReboot specified - reboot prompt skipped" -ForegroundColor Yellow + } else { + Write-Host "[!] Running in NonInteractive mode - reboot prompt skipped" -ForegroundColor Yellow + } + Write-Host " Please reboot manually to complete the restore." -ForegroundColor Gray + Write-Host "" + return + } + + # Interactive mode: prompt user do { Write-Host "Reboot now? [Y/N] (default: Y): " -NoNewline -ForegroundColor White $choice = Read-Host @@ -1175,8 +1212,8 @@ function Restore-AllBackups { Write-Log -Level WARNING -Message "Full rollback completed with some failures" -Module "Rollback" } - # Prompt for reboot after restore - Invoke-RestoreRebootPrompt + # Prompt for reboot after restore (pass through reboot parameters) + Invoke-RestoreRebootPrompt -NoReboot:$NoReboot -ForceReboot:$ForceReboot return $allSucceeded } @@ -1344,6 +1381,12 @@ function Restore-Session { .PARAMETER ModuleNames Optional array of specific module names to restore (restores all if not specified) + .PARAMETER NoReboot + Skip the reboot prompt entirely (for automation/GUI usage) + + .PARAMETER ForceReboot + Automatically reboot without prompting (for automation) + .OUTPUTS Boolean indicating overall success #> @@ -1354,7 +1397,13 @@ function Restore-Session { [string]$SessionPath, [Parameter(Mandatory = $false)] - [string[]]$ModuleNames + [string[]]$ModuleNames, + + [Parameter(Mandatory = $false)] + [switch]$NoReboot, + + [Parameter(Mandatory = $false)] + [switch]$ForceReboot ) if (-not (Test-Path $SessionPath)) { @@ -2690,8 +2739,8 @@ function Restore-Session { Write-Host "" Write-Host "" - # Prompt for reboot after restore - Invoke-RestoreRebootPrompt + # Prompt for reboot after restore (pass through reboot parameters) + Invoke-RestoreRebootPrompt -NoReboot:$NoReboot -ForceReboot:$ForceReboot # Final restore log entry $endTime = Get-Date From 74b73eda8134e91a73b76f5865850b141b6d9cf4 Mon Sep 17 00:00:00 2001 From: NexusOne23 Date: Wed, 24 Dec 2025 21:15:09 +0100 Subject: [PATCH 06/15] fix: cleanup loader lists, update New-DefaultConfig, fix CHANGELOG numbers - Remove non-existent Backup/Restore-AntiAISettings from AntiAI.psm1 loader - Remove non-existent Restore-PrivacySettings from Privacy.psm1 loader - Update New-DefaultConfig: EdgeHardening 20->24, AdvancedSecurity 36->50 - Add missing options: nonInteractive, autoConfirm, module-specific settings - Fix CHANGELOG.md: AntiAI 24->32, EdgeHardening 20->24, AdvancedSecurity 44->50 --- CHANGELOG.md | 6 +++--- Core/Config.ps1 | 33 +++++++++++++++++++++++++++------ Modules/AntiAI/AntiAI.psm1 | 2 -- Modules/Privacy/Privacy.psm1 | 1 - 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d62b97..0b0864a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -227,19 +227,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - OneDrive telemetry off (sync functional) - App permissions default-deny -**AntiAI** (24 policies) - AI Lockdown +**AntiAI** (32 policies) - AI Lockdown - Generative AI Master Switch (blocks ALL AI models system-wide) - Windows Recall (complete deactivation + component protection) - Windows Copilot (system-wide disabled + hardware key remapped) - Click to Do, Paint AI, Notepad AI, Settings Agent - all disabled -**EdgeHardening** (20 policies) - Microsoft Edge Security Baseline +**EdgeHardening** (24 policies) - Microsoft Edge Security Baseline - SmartScreen enforced, Tracking Prevention strict - SSL/TLS hardening, Extension security - IE Mode restrictions - Native PowerShell implementation (no LGPO.exe) -**AdvancedSecurity** (44 settings) - Beyond Microsoft Baseline +**AdvancedSecurity** (50 settings) - Beyond Microsoft Baseline - **SRP .lnk Protection (CVE-2025-9491)** - Zero-day mitigation for ClickFix malware - **RDP Hardening** - Disabled by default, TLS + NLA enforced - **Legacy Protocol Blocking** - SMBv1, NetBIOS, LLMNR, WPAD, PowerShell v2 diff --git a/Core/Config.ps1 b/Core/Config.ps1 index f6e7976..263d2ca 100644 --- a/Core/Config.ps1 +++ b/Core/Config.ps1 @@ -85,23 +85,30 @@ function New-DefaultConfig { enabled = $true priority = 1 status = "IMPLEMENTED" + bitLockerUSBEnforcement = $false } ASR = @{ enabled = $true priority = 2 status = "IMPLEMENTED" + usesManagementTools = $false + allowNewSoftware = $false + continueWithoutCloud = $true } DNS = @{ enabled = $true priority = 3 - provider = "" status = "IMPLEMENTED" + provider = "Quad9" + dohMode = "REQUIRE" } Privacy = @{ enabled = $true priority = 4 - mode = "" status = "IMPLEMENTED" + mode = "MSRecommended" + disableCloudClipboard = $true + removeBloatware = $true } AntiAI = @{ enabled = $true @@ -113,10 +120,11 @@ function New-DefaultConfig { enabled = $true priority = 6 status = "IMPLEMENTED" - description = "Microsoft Edge v139 Security Baseline: 20 security policies including SmartScreen enforcement, site isolation, SSL/TLS hardening, extension blocklist, IE Mode restrictions, and Spectre mitigations. No LGPO.exe dependency." + description = "Microsoft Edge v139 Security Baseline: 24 security policies" + allowExtensions = $true version = "2.2.2" baseline = "Edge v139" - policies = 20 + policies = 24 features = @{ smartscreen_enforcement = $true site_isolation = $true @@ -132,9 +140,16 @@ function New-DefaultConfig { enabled = $true priority = 7 status = "IMPLEMENTED" - description = "Advanced Security hardening beyond MS Baseline: RDP NLA/Disable, WDigest protection, Admin Shares disable, Risky ports/services, Legacy TLS disable, WPAD disable, PowerShell v2 removal, SRP .lnk protection, Windows Update (3 GUI settings), Finger Protocol block. Opt-in by design (use -SecurityProfile Balanced/Enterprise/Maximum)" + description = "Advanced Security hardening beyond MS Baseline" + securityProfile = "Balanced" + disableRDP = $true + forceAdminShares = $false + disableUPnP = $true + disableWirelessDisplay = $false + disableDiscoveryProtocols = $true + disableIPv6 = $false version = "2.2.2" - policies = 36 + policies = 50 features = @{ rdp_hardening = $true wdigest_protection = $true @@ -147,6 +162,10 @@ function New-DefaultConfig { srp_lnk_protection = $true windows_update_config = $true finger_protocol_block = $true + wireless_display_security = $true + discovery_protocols_security = $true + firewall_shields_up = $true + ipv6_disable = $true } profiles = @("Balanced", "Enterprise", "Maximum") } @@ -156,6 +175,8 @@ function New-DefaultConfig { createBackup = $true verboseLogging = $true autoReboot = $false + nonInteractive = $false + autoConfirm = $false } } diff --git a/Modules/AntiAI/AntiAI.psm1 b/Modules/AntiAI/AntiAI.psm1 index 7f0009d..7a7de05 100644 --- a/Modules/AntiAI/AntiAI.psm1 +++ b/Modules/AntiAI/AntiAI.psm1 @@ -22,8 +22,6 @@ $script:ModuleRoot = $PSScriptRoot # Import private functions $privateFunctions = @( - 'Backup-AntiAISettings' - 'Restore-AntiAISettings' 'Test-AntiAICompliance' 'Set-SystemAIModels' 'Disable-Recall' diff --git a/Modules/Privacy/Privacy.psm1 b/Modules/Privacy/Privacy.psm1 index dcb76e3..8021252 100644 --- a/Modules/Privacy/Privacy.psm1 +++ b/Modules/Privacy/Privacy.psm1 @@ -26,7 +26,6 @@ $script:ModuleRoot = $PSScriptRoot # Import private functions $privateFunctions = @( 'Backup-PrivacySettings', - 'Restore-PrivacySettings', 'Set-TelemetrySettings', 'Set-PersonalizationSettings', 'Set-AppPrivacySettings', From 8435dbe97b9e632c5ffa199720a4096d8549b107 Mon Sep 17 00:00:00 2001 From: NexusOne23 Date: Wed, 7 Jan 2026 18:27:32 +0100 Subject: [PATCH 07/15] fix: Replace broken .Split() with -split operator in Restore module selection The previous implementation used .Split(',', ';', ' ') which causes PowerShell to match the wrong .NET overload Split(string, Int32), interpreting ';' as a count parameter and throwing a System.Int32 conversion error. Replaced with native PowerShell -split operator using regex character class [,; ] which correctly splits on comma, semicolon, or space. Fixes: Restore Mode -> Manual Selection crash on any input Reported-by: KatCat2 --- NoIDPrivacy-Interactive.ps1 | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/NoIDPrivacy-Interactive.ps1 b/NoIDPrivacy-Interactive.ps1 index c22db65..2bd396f 100644 --- a/NoIDPrivacy-Interactive.ps1 +++ b/NoIDPrivacy-Interactive.ps1 @@ -75,11 +75,11 @@ function Write-Step { $symbol = switch ($Status) { "SUCCESS" { "[+]"; $color = "Green" } - "ERROR" { "[-]"; $color = "Red" } + "ERROR" { "[-]"; $color = "Red" } "WARNING" { "[!]"; $color = "Yellow" } - "INFO" { "[>]"; $color = "Cyan" } - "WAIT" { "[.]"; $color = "Gray" } - default { "[ ]"; $color = "White" } + "INFO" { "[>]"; $color = "Cyan" } + "WAIT" { "[.]"; $color = "Gray" } + default { "[ ]"; $color = "White" } } Write-ColorText $symbol -Color $color -NoNewline @@ -356,7 +356,8 @@ function Show-BackupList { # Force result to array to handle single-session case correctly if (Test-Path $backupPath) { $sessions = @(Get-BackupSessions -BackupDirectory $backupPath) - } else { + } + else { $sessions = @() } } @@ -382,8 +383,8 @@ function Show-BackupList { try { $age = (Get-Date) - $session.Timestamp $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" } + elseif ($age.TotalDays -lt 1) { "$([math]::Round($age.TotalHours)) hours ago" } + else { "$([math]::Round($age.TotalDays)) days ago" } } catch { $ageStr = "unknown age" @@ -795,7 +796,7 @@ function Invoke-RestoreWorkflow { if ([string]::IsNullOrWhiteSpace($modeInput)) { $modeInput = "A" } $modeInput = $modeInput.Trim().ToUpper() - if ($modeInput -in @('A','M')) { + if ($modeInput -in @('A', 'M')) { $restoreMode = $modeInput break } @@ -818,7 +819,7 @@ function Invoke-RestoreWorkflow { } $indices = @() - foreach ($token in $moduleInput.Split(',', ';', ' ')) { + foreach ($token in ($moduleInput -split '[,; ]')) { if (-not [string]::IsNullOrWhiteSpace($token)) { $parsed = 0 if ([int]::TryParse($token.Trim(), [ref]$parsed)) { @@ -837,7 +838,7 @@ function Invoke-RestoreWorkflow { } foreach ($i in $indices) { - $selectedModuleNames += $availableModules[$i-1].name + $selectedModuleNames += $availableModules[$i - 1].name } } } From da9f937ee838ce549091db3a552928b9182af533 Mon Sep 17 00:00:00 2001 From: NexusOne23 Date: Wed, 7 Jan 2026 18:46:14 +0100 Subject: [PATCH 08/15] release: v2.2.3 - Fix Restore Mode module selection crash CHANGELOG: - Fixed: Restore Mode manual module selection crash (Critical) - Root cause: .Split(',', ';', ' ') triggered wrong .NET overload - Fix: Replaced with native PowerShell -split '[,; ]' operator - Reported by: KatCat2 VERSION BUMP: 2.2.2 -> 2.2.3 - Updated 48 files with new version number - CHANGELOG.md: Added v2.2.3 release notes - README.md: Updated badge, module table, project status --- CHANGELOG.md | 17 + Core/Config.ps1 | 148 ++++----- Core/Framework.ps1 | 10 +- Core/Logger.ps1 | 38 +-- Core/NonInteractive.ps1 | 5 +- Core/Rollback.ps1 | 89 ++--- Core/Validator.ps1 | 64 ++-- Modules/ASR/ASR.psd1 | 30 +- Modules/ASR/ASR.psm1 | 2 +- .../AdvancedSecurity/AdvancedSecurity.psd1 | 4 +- .../AdvancedSecurity/AdvancedSecurity.psm1 | 2 +- Modules/AntiAI/AntiAI.psd1 | 20 +- Modules/AntiAI/AntiAI.psm1 | 4 +- Modules/AntiAI/Public/Invoke-AntiAI.ps1 | 13 +- Modules/DNS/DNS.psd1 | 2 +- Modules/DNS/DNS.psm1 | 2 +- Modules/EdgeHardening/EdgeHardening.psd1 | 30 +- Modules/EdgeHardening/EdgeHardening.psm1 | 2 +- .../Public/Invoke-EdgeHardening.ps1 | 2 +- .../Public/Test-EdgeHardening.ps1 | 6 +- Modules/Privacy/Privacy.psd1 | 26 +- Modules/Privacy/Privacy.psm1 | 2 +- .../Public/Invoke-SecurityBaseline.ps1 | 2 +- .../SecurityBaseline/SecurityBaseline.psd1 | 30 +- .../SecurityBaseline/SecurityBaseline.psm1 | 2 +- NoIDPrivacy-Interactive.ps1 | 8 +- NoIDPrivacy.ps1 | 50 +-- README.md | 34 +- Start-NoIDPrivacy.bat | 4 +- Tests/Run-Tests.ps1 | 2 +- Tests/Setup-TestEnvironment.ps1 | 2 +- Tests/Unit/ASR.Tests.ps1 | 2 +- Tests/Unit/AdvancedSecurity.Tests.ps1 | 2 +- Tests/Unit/AntiAI.Tests.ps1 | 2 +- Tests/Unit/DNS.Tests.ps1 | 2 +- Tests/Unit/EdgeHardening.Tests.ps1 | 2 +- Tests/Unit/ModuleTemplate.Tests.ps1 | 2 +- Tests/Unit/Privacy.Tests.ps1 | 2 +- Tools/Generate-ReleaseChecksums.ps1 | 10 +- Tools/Parse-EdgeBaseline.ps1 | 29 +- Tools/Parse-SecurityBaseline.ps1 | 69 ++-- Tools/Verify-Complete-Hardening.ps1 | 305 +++++++++--------- Utils/Compatibility.ps1 | 2 +- Utils/Dependencies.ps1 | 30 +- Utils/Hardware.ps1 | 78 ++--- Utils/Registry.ps1 | 2 +- Utils/Service.ps1 | 12 +- config.json | 14 +- 48 files changed, 628 insertions(+), 589 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b0864a..31b9e90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- +## [2.2.3] - 2025-01-07 + +### 🔨 Bugfix Release + +**Critical bugfix for Restore Mode manual module selection.** + +### 🔨 Fixed + +**Restore Mode Module Selection Crash (Critical)** +- Fixed: Selecting `[M] Restore only SELECTED modules` and entering any module number caused a fatal PowerShell error +- Root cause: `.Split(',', ';', ' ')` triggered wrong .NET overload `Split(string, Int32)`, interpreting `;` as count parameter +- Fix: Replaced with native PowerShell `-split '[,; ]'` operator +- Impact: Manual module selection in Restore workflow now works correctly +- Reported by: KatCat2 + +--- + ## [2.2.2] - 2025-12-22 ### 🚀 Performance Release diff --git a/Core/Config.ps1 b/Core/Config.ps1 index 263d2ca..be9c143 100644 --- a/Core/Config.ps1 +++ b/Core/Config.ps1 @@ -8,7 +8,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+ #> @@ -79,104 +79,104 @@ function New-DefaultConfig { ) $defaultConfig = @{ - version = "2.2.2" + version = "2.2.3" modules = @{ SecurityBaseline = @{ - enabled = $true - priority = 1 - status = "IMPLEMENTED" + enabled = $true + priority = 1 + status = "IMPLEMENTED" bitLockerUSBEnforcement = $false } - ASR = @{ - enabled = $true - priority = 2 - status = "IMPLEMENTED" - usesManagementTools = $false - allowNewSoftware = $false + ASR = @{ + enabled = $true + priority = 2 + status = "IMPLEMENTED" + usesManagementTools = $false + allowNewSoftware = $false continueWithoutCloud = $true } - DNS = @{ - enabled = $true + DNS = @{ + enabled = $true priority = 3 - status = "IMPLEMENTED" + status = "IMPLEMENTED" provider = "Quad9" - dohMode = "REQUIRE" + dohMode = "REQUIRE" } - Privacy = @{ - enabled = $true - priority = 4 - status = "IMPLEMENTED" - mode = "MSRecommended" + Privacy = @{ + enabled = $true + priority = 4 + status = "IMPLEMENTED" + mode = "MSRecommended" disableCloudClipboard = $true - removeBloatware = $true + removeBloatware = $true } - AntiAI = @{ - enabled = $true - priority = 5 - status = "IMPLEMENTED" + AntiAI = @{ + enabled = $true + priority = 5 + status = "IMPLEMENTED" description = "Disable all Windows 11 AI features (Recall, Copilot, Paint AI, etc.)" } - EdgeHardening = @{ - enabled = $true - priority = 6 - status = "IMPLEMENTED" - description = "Microsoft Edge v139 Security Baseline: 24 security policies" + EdgeHardening = @{ + enabled = $true + priority = 6 + status = "IMPLEMENTED" + description = "Microsoft Edge v139 Security Baseline: 24 security policies" allowExtensions = $true - version = "2.2.2" - baseline = "Edge v139" - policies = 24 - features = @{ - smartscreen_enforcement = $true - site_isolation = $true - ssl_error_blocking = $true - extension_blocklist = $true - ie_mode_restrictions = $true - spectre_mitigations = $true - application_encryption = $true + version = "2.2.3" + baseline = "Edge v139" + policies = 24 + features = @{ + smartscreen_enforcement = $true + site_isolation = $true + ssl_error_blocking = $true + extension_blocklist = $true + ie_mode_restrictions = $true + spectre_mitigations = $true + application_encryption = $true auth_scheme_restrictions = $true } } AdvancedSecurity = @{ - enabled = $true - priority = 7 - status = "IMPLEMENTED" - description = "Advanced Security hardening beyond MS Baseline" - securityProfile = "Balanced" - disableRDP = $true - forceAdminShares = $false - disableUPnP = $true - disableWirelessDisplay = $false + enabled = $true + priority = 7 + status = "IMPLEMENTED" + description = "Advanced Security hardening beyond MS Baseline" + securityProfile = "Balanced" + disableRDP = $true + forceAdminShares = $false + disableUPnP = $true + disableWirelessDisplay = $false disableDiscoveryProtocols = $true - disableIPv6 = $false - version = "2.2.2" - policies = 50 - features = @{ - rdp_hardening = $true - wdigest_protection = $true - admin_shares_disable = $true - risky_ports_closure = $true - risky_services_stop = $true - legacy_tls_disable = $true - wpad_disable = $true - powershell_v2_removal = $true - srp_lnk_protection = $true - windows_update_config = $true - finger_protocol_block = $true - wireless_display_security = $true + disableIPv6 = $false + version = "2.2.3" + policies = 50 + features = @{ + rdp_hardening = $true + wdigest_protection = $true + admin_shares_disable = $true + risky_ports_closure = $true + risky_services_stop = $true + legacy_tls_disable = $true + wpad_disable = $true + powershell_v2_removal = $true + srp_lnk_protection = $true + windows_update_config = $true + finger_protocol_block = $true + wireless_display_security = $true discovery_protocols_security = $true - firewall_shields_up = $true - ipv6_disable = $true + firewall_shields_up = $true + ipv6_disable = $true } - profiles = @("Balanced", "Enterprise", "Maximum") + profiles = @("Balanced", "Enterprise", "Maximum") } } options = @{ - dryRun = $false - createBackup = $true + dryRun = $false + createBackup = $true verboseLogging = $true - autoReboot = $false + autoReboot = $false nonInteractive = $false - autoConfirm = $false + autoConfirm = $false } } @@ -434,7 +434,7 @@ function Get-EnabledModules { # Check if module is actually implemented if (Test-ModuleAvailability -ModuleName $moduleName) { $enabledModules += [PSCustomObject]@{ - Name = $moduleName + Name = $moduleName Priority = $moduleConfig.priority } } diff --git a/Core/Framework.ps1 b/Core/Framework.ps1 index 8e28443..352f248 100644 --- a/Core/Framework.ps1 +++ b/Core/Framework.ps1 @@ -8,7 +8,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+ .EXAMPLE @@ -24,7 +24,7 @@ # All configuration comes from config.json via Initialize-Config. # Script-level variables -$script:FrameworkVersion = "2.2.2" +$script:FrameworkVersion = "2.2.3" $script:FrameworkRoot = Split-Path -Parent $PSScriptRoot $script:ExecutionStartTime = Get-Date @@ -409,8 +409,8 @@ function Invoke-Hardening { else { # CLI mode: Auto-detect session type based on module count $autoSessionType = if ($modulesToExecute.Count -ge 7) { "wizard" } - elseif ($modulesToExecute.Count -eq 1) { "advanced" } - else { "manual" } + elseif ($modulesToExecute.Count -eq 1) { "advanced" } + else { "manual" } Set-SessionType -SessionType $autoSessionType Write-Log -Level DEBUG -Message "Session type auto-detected: $autoSessionType (based on $($modulesToExecute.Count) modules)" -Module "Framework" } @@ -451,7 +451,7 @@ function Invoke-Hardening { $ruleCount = $ruleIds.Count $preFrameworkSnapshot = @{ - ASR = @{ + ASR = @{ RuleIds = $ruleIds RuleActions = $ruleActions SnapshotDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" diff --git a/Core/Logger.ps1 b/Core/Logger.ps1 index e0b2061..ee15c75 100644 --- a/Core/Logger.ps1 +++ b/Core/Logger.ps1 @@ -8,7 +8,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+ #> @@ -26,10 +26,10 @@ enum LogLevel { # NOTE: Must use Get-Variable to check existence (direct access fails in Strict Mode) if (-not (Get-Variable -Name 'LoggerConfig' -Scope Global -ErrorAction SilentlyContinue)) { $global:LoggerConfig = @{ - LogFilePath = "" - MinimumLevel = [LogLevel]::INFO - EnableConsole = $true - EnableFile = $true + LogFilePath = "" + MinimumLevel = [LogLevel]::INFO + EnableConsole = $true + EnableFile = $true TimestampFormat = "yyyy-MM-dd HH:mm:ss" } } @@ -193,10 +193,10 @@ function Write-Log { # Write to console with color coding (suppress DEBUG-level on console) if ($global:LoggerConfig.EnableConsole -and $Level -ge [LogLevel]::INFO) { $consoleColor = switch ($Level) { - ([LogLevel]::DEBUG) { "Gray" } - ([LogLevel]::INFO) { "White" } + ([LogLevel]::DEBUG) { "Gray" } + ([LogLevel]::INFO) { "White" } ([LogLevel]::WARNING) { "Yellow" } - ([LogLevel]::ERROR) { "Red" } + ([LogLevel]::ERROR) { "Red" } ([LogLevel]::SUCCESS) { "Green" } default { "White" } } @@ -245,15 +245,15 @@ function Get-ErrorContext { ) $context = @{ - Message = "" - Exception = "" - Category = "" + Message = "" + Exception = "" + Category = "" TargetObject = "" - ScriptName = "" - LineNumber = 0 - Command = "" - StackTrace = "" - Summary = "" + ScriptName = "" + LineNumber = 0 + Command = "" + StackTrace = "" + Summary = "" } if ($null -eq $ErrorRecord) { @@ -271,13 +271,15 @@ function Get-ErrorContext { if ($ErrorRecord.InvocationInfo) { $context.ScriptName = if ($ErrorRecord.InvocationInfo.ScriptName) { Split-Path -Leaf $ErrorRecord.InvocationInfo.ScriptName - } else { + } + else { "N/A" } $context.LineNumber = $ErrorRecord.InvocationInfo.ScriptLineNumber $context.Command = if ($ErrorRecord.InvocationInfo.MyCommand) { $ErrorRecord.InvocationInfo.MyCommand.Name - } else { + } + else { "N/A" } } diff --git a/Core/NonInteractive.ps1 b/Core/NonInteractive.ps1 index 9cf5a28..26ba39b 100644 --- a/Core/NonInteractive.ps1 +++ b/Core/NonInteractive.ps1 @@ -12,7 +12,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Usage in modules: 1. Call Test-NonInteractiveMode to check if prompts should be skipped @@ -194,7 +194,8 @@ function Write-NonInteractiveDecision { $message = if ($null -ne $Value) { "[GUI] $Decision : $Value" - } else { + } + else { "[GUI] $Decision" } diff --git a/Core/Rollback.ps1 b/Core/Rollback.ps1 index e9867c9..5baea6d 100644 --- a/Core/Rollback.ps1 +++ b/Core/Rollback.ps1 @@ -8,7 +8,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+ #> @@ -64,7 +64,7 @@ function Initialize-BackupSystem { displayName = "" # Auto-generated based on modules sessionType = "unknown" # wizard | advanced | manual timestamp = Get-Date -Format "o" - frameworkVersion = "2.2.2" + frameworkVersion = "2.2.3" modules = @() totalItems = 0 restorable = $true @@ -116,13 +116,13 @@ function Update-SessionDisplayName { # Calculate ACTUAL settings count (not backup items!) # Each module applies a specific number of settings (Paranoid mode = max): $settingsPerModule = @{ - "SecurityBaseline" = 425 # 335 Registry + 67 Security Template + 23 Audit - "ASR" = 19 # 19 ASR Rules - "DNS" = 5 # 5 DNS Settings - "Privacy" = 78 # 54 Registry (MSRecommended) + 24 Bloatware - "AntiAI" = 32 # 32 Registry Policies (15 features) - "EdgeHardening" = 24 # 24 Edge Policies (22-23 applied depending on extensions) - "AdvancedSecurity" = 50 # 50 Advanced Settings (15 features incl. Discovery Protocols + IPv6) + "SecurityBaseline" = 425 # 335 Registry + 67 Security Template + 23 Audit + "ASR" = 19 # 19 ASR Rules + "DNS" = 5 # 5 DNS Settings + "Privacy" = 78 # 54 Registry (MSRecommended) + 24 Bloatware + "AntiAI" = 32 # 32 Registry Policies (15 features) + "EdgeHardening" = 24 # 24 Edge Policies (22-23 applied depending on extensions) + "AdvancedSecurity" = 50 # 50 Advanced Settings (15 features incl. Discovery Protocols + IPv6) } $totalSettings = 0 @@ -382,10 +382,10 @@ function Backup-RegistryKey { try { $emptyMarker = @{ - KeyPath = $KeyPath + KeyPath = $KeyPath BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" - State = "NotExisted" - Message = "Registry key did not exist before hardening - must be deleted during restore" + State = "NotExisted" + Message = "Registry key did not exist before hardening - must be deleted during restore" } | ConvertTo-Json $markerFile = Join-Path $backupFolder "$safeBackupName`_EMPTY.json" @@ -935,10 +935,10 @@ function Restore-FromBackup { try { # Convert reg.exe path to PowerShell path $psKeyPath = $keyPathToRestore -replace 'HKEY_LOCAL_MACHINE', 'HKLM:' ` - -replace 'HKEY_CURRENT_USER', 'HKCU:' ` - -replace 'HKEY_CLASSES_ROOT', 'HKCR:' ` - -replace 'HKEY_USERS', 'HKU:' ` - -replace 'HKEY_CURRENT_CONFIG', 'HKCC:' + -replace 'HKEY_CURRENT_USER', 'HKCU:' ` + -replace 'HKEY_CLASSES_ROOT', 'HKCR:' ` + -replace 'HKEY_USERS', 'HKU:' ` + -replace 'HKEY_CURRENT_CONFIG', 'HKCC:' if (Test-Path $psKeyPath) { Write-Log -Level INFO -Message "Deleting existing protected key: $psKeyPath before re-import." -Module "Rollback" @@ -1106,7 +1106,8 @@ function Invoke-RestoreRebootPrompt { Write-Host "" if ($NoReboot) { Write-Host "[!] NoReboot specified - reboot prompt skipped" -ForegroundColor Yellow - } else { + } + else { Write-Host "[!] Running in NonInteractive mode - reboot prompt skipped" -ForegroundColor Yellow } Write-Host " Please reboot manually to complete the restore." -ForegroundColor Gray @@ -1421,7 +1422,8 @@ function Restore-Session { Write-RestoreLog -Level INFO -Message "Session Path: $SessionPath" if ($ModuleNames) { Write-RestoreLog -Level INFO -Message "Specific Modules: $($ModuleNames -join ', ')" - } else { + } + else { Write-RestoreLog -Level INFO -Message "Restoring: ALL modules" } Write-RestoreLog -Level INFO -Message "========================================" @@ -1849,7 +1851,7 @@ function Restore-Session { for ($i = 0; $i -lt $preFramework.ASR.RuleIds.Count; $i++) { if ($preFramework.ASR.RuleActions[$i] -ne 0) { $asrRulesToRestore += @{ - GUID = $preFramework.ASR.RuleIds[$i] + GUID = $preFramework.ASR.RuleIds[$i] Action = $preFramework.ASR.RuleActions[$i] } } @@ -1892,8 +1894,8 @@ function Restore-Session { $ruleActions = $asrRulesToRestore | ForEach-Object { $_.Action } Set-MpPreference -AttackSurfaceReductionRules_Ids $ruleIds ` - -AttackSurfaceReductionRules_Actions $ruleActions ` - -ErrorAction Stop + -AttackSurfaceReductionRules_Actions $ruleActions ` + -ErrorAction Stop $sourceDesc = if ($usePreFramework) { "PreFramework snapshot (TRUE pre-hardening)" } else { "ASR_ActiveConfiguration.json" } Write-Log -Level SUCCESS -Message "ASR rules restored via Set-MpPreference ($($asrRulesToRestore.Count) active rules from $sourceDesc)" -Module "Rollback" @@ -1961,7 +1963,8 @@ function Restore-Session { Write-Log -Level SUCCESS -Message "Explorer Advanced settings restored via PowerShell" -Module "Rollback" } } - } catch { + } + catch { Write-Log -Level WARNING -Message "PowerShell-based Explorer restore failed: $($_.Exception.Message)" -Module "Rollback" } } @@ -1985,10 +1988,10 @@ function Restore-Session { } $regType = switch ($entry.Type) { - "DWord" { "DWord" } - "String" { "String" } + "DWord" { "DWord" } + "String" { "String" } "MultiString" { "MultiString" } - default { "String" } + default { "String" } } $existing = Get-ItemProperty -Path $keyPath -Name $entry.Name -ErrorAction SilentlyContinue @@ -2227,13 +2230,13 @@ function Restore-Session { } $regType = switch ($entry.Type) { - "DWord" { "DWord" } - "String" { "String" } + "DWord" { "DWord" } + "String" { "String" } "MultiString" { "MultiString" } "ExpandString" { "ExpandString" } - "Binary" { "Binary" } - "QWord" { "QWord" } - default { "String" } + "Binary" { "Binary" } + "QWord" { "QWord" } + default { "String" } } New-ItemProperty -Path $entry.Path -Name $entry.Name -Value $entry.Value -PropertyType $regType -Force -ErrorAction Stop | Out-Null @@ -2295,7 +2298,7 @@ function Restore-Session { "HKCU:\Software\Microsoft\Windows\CurrentVersion\SystemSettings\AccountNotifications", "HKCU:\Software\Microsoft\Windows\CurrentVersion\UserProfileEngagement", "HKCU:\SOFTWARE\Microsoft\Personalization\Settings", - # NEW: Input Personalization Settings (v2.2.2 - FIX missing HKCU restore) + # NEW: Input Personalization Settings (v2.2.3 - FIX missing HKCU restore) "HKCU:\SOFTWARE\Microsoft\InputPersonalization", "HKCU:\SOFTWARE\Microsoft\InputPersonalization\TrainedDataStore", "HKCU:\Software\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\appDiagnostics" @@ -2312,7 +2315,8 @@ function Restore-Session { Remove-ItemProperty -Path $keyPath -Name $prop -ErrorAction SilentlyContinue } } - } catch { + } + catch { Write-Log -Level DEBUG -Message "Could not clear $keyPath : $_" -Module "Rollback" } } @@ -2329,12 +2333,12 @@ function Restore-Session { } $regType = switch ($entry.Type) { - "DWord" { "DWord" } - "String" { "String" } + "DWord" { "DWord" } + "String" { "String" } "MultiString" { "MultiString" } "ExpandString" { "ExpandString" } - "Binary" { "Binary" } - default { "String" } + "Binary" { "Binary" } + default { "String" } } New-ItemProperty -Path $entry.Path -Name $entry.Name -Value $entry.Value -PropertyType $regType -Force -ErrorAction Stop | Out-Null @@ -2512,13 +2516,13 @@ function Restore-Session { } $regType = switch ($entry.Type) { - "DWord" { "DWord" } - "String" { "String" } + "DWord" { "DWord" } + "String" { "String" } "MultiString" { "MultiString" } "ExpandString" { "ExpandString" } - "Binary" { "Binary" } - "QWord" { "QWord" } - default { "String" } + "Binary" { "Binary" } + "QWord" { "QWord" } + default { "String" } } New-ItemProperty -Path $entry.Path -Name $entry.Name -Value $entry.Value -PropertyType $regType -Force -ErrorAction Stop | Out-Null @@ -2726,7 +2730,8 @@ function Restore-Session { Write-Host " All security settings have been reverted to backup state" -ForegroundColor White Write-Host " Modules restored: $($reversedModules.Count) | Total items: $($manifest.totalItems)" -ForegroundColor Gray Write-Host "" - } else { + } + else { Write-Host "" Write-Host " RESTORE COMPLETED WITH ISSUES " -ForegroundColor Yellow Write-Host "" diff --git a/Core/Validator.ps1 b/Core/Validator.ps1 index df69127..2319f25 100644 --- a/Core/Validator.ps1 +++ b/Core/Validator.ps1 @@ -8,7 +8,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+ #> @@ -27,9 +27,9 @@ function Test-Prerequisites { Write-Log -Level INFO -Message "Starting prerequisite validation" -Module "Validator" $result = [PSCustomObject]@{ - Success = $true - Errors = @() - Warnings = @() + Success = $true + Errors = @() + Warnings = @() SystemInfo = $null } @@ -133,11 +133,11 @@ function Get-WindowsVersion { } return [PSCustomObject]@{ - Version = $versionName - BuildNumber = $buildNumber - IsWindows11 = $isWindows11 - IsSupported = $isSupported - Edition = $os.Caption + Version = $versionName + BuildNumber = $buildNumber + IsWindows11 = $isWindows11 + IsSupported = $isSupported + Edition = $os.Caption Architecture = $os.OSArchitecture } } @@ -200,26 +200,26 @@ function Test-TPMAvailable { if ($null -eq $tpm) { return [PSCustomObject]@{ - Present = $false - Version = "N/A" - Enabled = $false + Present = $false + Version = "N/A" + Enabled = $false Activated = $false } } return [PSCustomObject]@{ - Present = $tpm.TpmPresent - Version = if ($tpm.ManufacturerVersion) { $tpm.ManufacturerVersion } else { "2.0" } - Enabled = $tpm.TpmEnabled + Present = $tpm.TpmPresent + Version = if ($tpm.ManufacturerVersion) { $tpm.ManufacturerVersion } else { "2.0" } + Enabled = $tpm.TpmEnabled Activated = $tpm.TpmActivated } } catch { Write-Log -Level WARNING -Message "Unable to check TPM status: $_" -Module "Validator" return [PSCustomObject]@{ - Present = $false - Version = "Unknown" - Enabled = $false + Present = $false + Version = "Unknown" + Enabled = $false Activated = $false } } @@ -294,14 +294,14 @@ function Get-SystemInfo { $internet = Test-InternetConnectivity return [PSCustomObject]@{ - OS = $osInfo - TPM = $tpmInfo - SecureBoot = $secureBoot - Virtualization = $virtualization - IsAdministrator = $isAdmin + OS = $osInfo + TPM = $tpmInfo + SecureBoot = $secureBoot + Virtualization = $virtualization + IsAdministrator = $isAdmin DiskSpaceAvailable = $diskSpace - InternetConnected = $internet - PowerShellVersion = $PSVersionTable.PSVersion.ToString() + InternetConnected = $internet + PowerShellVersion = $PSVersionTable.PSVersion.ToString() } } @@ -332,9 +332,9 @@ function Test-DomainJoined { $result = [PSCustomObject]@{ IsDomainJoined = $isDomainJoined - DomainName = if ($isDomainJoined) { $computerSystem.Domain } else { "N/A" } - Workgroup = if (-not $isDomainJoined) { $computerSystem.Workgroup } else { "N/A" } - UserConfirmed = $false + DomainName = if ($isDomainJoined) { $computerSystem.Domain } else { "N/A" } + Workgroup = if (-not $isDomainJoined) { $computerSystem.Workgroup } else { "N/A" } + UserConfirmed = $false } if ($isDomainJoined) { @@ -384,9 +384,9 @@ function Test-DomainJoined { Write-Log -Level ERROR -Message "Failed to check domain status: $_" -Module "Validator" -Exception $_.Exception return [PSCustomObject]@{ IsDomainJoined = $false - DomainName = "Error" - Workgroup = "Error" - UserConfirmed = $false + DomainName = "Error" + Workgroup = "Error" + UserConfirmed = $false } } } @@ -416,7 +416,7 @@ function Confirm-SystemBackup { Write-Log -Level INFO -Message "Backup recommendation: non-interactive confirmation (no prompt shown)" -Module "Validator" $result = [PSCustomObject]@{ - UserConfirmed = $true + UserConfirmed = $true BackupRecommended = $true } diff --git a/Modules/ASR/ASR.psd1 b/Modules/ASR/ASR.psd1 index 0484784..db4391f 100644 --- a/Modules/ASR/ASR.psd1 +++ b/Modules/ASR/ASR.psd1 @@ -1,31 +1,31 @@ @{ - RootModule = 'ASR.psm1' - ModuleVersion = '2.2.2' - GUID = 'b2c3d4e5-f6a7-8901-bcde-f23456789012' - Author = 'NexusOne23' - CompanyName = 'Open Source Project' - Copyright = '(c) 2025 NexusOne23. Licensed under GPL-3.0.' - Description = 'Attack Surface Reduction (ASR) - All 19 Microsoft Defender ASR rules in Block mode for maximum protection against modern threats' + RootModule = 'ASR.psm1' + ModuleVersion = '2.2.3' + GUID = 'b2c3d4e5-f6a7-8901-bcde-f23456789012' + Author = 'NexusOne23' + CompanyName = 'Open Source Project' + Copyright = '(c) 2025 NexusOne23. Licensed under GPL-3.0.' + Description = 'Attack Surface Reduction (ASR) - All 19 Microsoft Defender ASR rules in Block mode for maximum protection against modern threats' PowerShellVersion = '5.1' - RequiredModules = @() + RequiredModules = @() FunctionsToExport = @( 'Invoke-ASRRules' ) - CmdletsToExport = @() + CmdletsToExport = @() VariablesToExport = @() - AliasesToExport = @() + AliasesToExport = @() - PrivateData = @{ + PrivateData = @{ PSData = @{ - Tags = @('Security', 'ASR', 'AttackSurfaceReduction', 'Defender', 'Windows11', 'Ransomware') - LicenseUri = '' - ProjectUri = '' + Tags = @('Security', 'ASR', 'AttackSurfaceReduction', 'Defender', 'Windows11', 'Ransomware') + LicenseUri = '' + ProjectUri = '' ReleaseNotes = @" -v2.2.2 - Production Release +v2.2.3 - Production Release - All 19 ASR rules implementation - Hybrid approach: Registry backup + Set-MpPreference application - SCCM/Configuration Manager detection diff --git a/Modules/ASR/ASR.psm1 b/Modules/ASR/ASR.psm1 index fbaa557..642924f 100644 --- a/Modules/ASR/ASR.psm1 +++ b/Modules/ASR/ASR.psm1 @@ -11,7 +11,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+, Administrator privileges, Windows Defender #> diff --git a/Modules/AdvancedSecurity/AdvancedSecurity.psd1 b/Modules/AdvancedSecurity/AdvancedSecurity.psd1 index 0c1c8a9..d12bf76 100644 --- a/Modules/AdvancedSecurity/AdvancedSecurity.psd1 +++ b/Modules/AdvancedSecurity/AdvancedSecurity.psd1 @@ -2,7 +2,7 @@ # Module manifest for AdvancedSecurity # Version - ModuleVersion = '2.2.2' + ModuleVersion = '2.2.3' # Unique ID GUID = 'e7f5a3d2-8c9b-4f1e-a6d3-9b2c8f4e5a1d' @@ -48,7 +48,7 @@ LicenseUri = '' ProjectUri = '' ReleaseNotes = @' -v2.2.2 (2025-12-08) +v2.2.3 (2025-12-08) - Production release of AdvancedSecurity module - 49 advanced hardening settings implemented (was 36) - NEW: Wireless Display (Miracast) security hardening diff --git a/Modules/AdvancedSecurity/AdvancedSecurity.psm1 b/Modules/AdvancedSecurity/AdvancedSecurity.psm1 index 0df4d65..1ded715 100644 --- a/Modules/AdvancedSecurity/AdvancedSecurity.psm1 +++ b/Modules/AdvancedSecurity/AdvancedSecurity.psm1 @@ -1,5 +1,5 @@ # AdvancedSecurity Module Loader -# Version: 2.2.2 +# Version: 2.2.3 # Description: Advanced Security Hardening - Beyond Microsoft Security Baseline # Get module path diff --git a/Modules/AntiAI/AntiAI.psd1 b/Modules/AntiAI/AntiAI.psd1 index a96fdd9..47dc6e7 100644 --- a/Modules/AntiAI/AntiAI.psd1 +++ b/Modules/AntiAI/AntiAI.psd1 @@ -1,21 +1,21 @@ @{ - RootModule = 'AntiAI.psm1' - ModuleVersion = '2.2.2' - GUID = 'f8e9d7c6-5b4a-3c2d-1e0f-9a8b7c6d5e4f' - Author = 'NexusOne23' - CompanyName = 'Open Source Project' - Copyright = '(c) 2025 NexusOne23. Licensed under GPL-3.0.' - Description = 'Comprehensive Windows 11 AI deactivation - Disables all 15 AI features using official Microsoft policies (Recall, Copilot, Paint AI, Notepad AI, Click to Do, Settings Agent, etc.). Maximum compliance mode with enterprise-grade Recall protection.' + RootModule = 'AntiAI.psm1' + ModuleVersion = '2.2.3' + GUID = 'f8e9d7c6-5b4a-3c2d-1e0f-9a8b7c6d5e4f' + Author = 'NexusOne23' + CompanyName = 'Open Source Project' + Copyright = '(c) 2025 NexusOne23. Licensed under GPL-3.0.' + Description = 'Comprehensive Windows 11 AI deactivation - Disables all 15 AI features using official Microsoft policies (Recall, Copilot, Paint AI, Notepad AI, Click to Do, Settings Agent, etc.). Maximum compliance mode with enterprise-grade Recall protection.' PowerShellVersion = '5.1' FunctionsToExport = @( 'Invoke-AntiAI' ) - PrivateData = @{ + PrivateData = @{ PSData = @{ - Tags = @('Windows11', 'AI', 'Privacy', 'Security', 'Recall', 'Copilot', 'AntiAI') - ProjectUri = 'https://github.com/yourusername/NoIDPrivacy' + Tags = @('Windows11', 'AI', 'Privacy', 'Security', 'Recall', 'Copilot', 'AntiAI') + ProjectUri = 'https://github.com/yourusername/NoIDPrivacy' ReleaseNotes = @' v1.0.0 - Initial Release - Disables 8+ Windows 11 AI features using official Microsoft policies diff --git a/Modules/AntiAI/AntiAI.psm1 b/Modules/AntiAI/AntiAI.psm1 index 7a7de05..274d6c3 100644 --- a/Modules/AntiAI/AntiAI.psm1 +++ b/Modules/AntiAI/AntiAI.psm1 @@ -11,7 +11,7 @@ .NOTES Module: AntiAI - Version: 2.2.2 + Version: 2.2.3 Author: NoID Privacy #> @@ -27,7 +27,7 @@ $privateFunctions = @( 'Disable-Recall' 'Set-RecallProtection' 'Disable-Copilot' - 'Disable-CopilotAdvanced' # NEW v2.2.2: URI handlers, Edge sidebar, Recall export + 'Disable-CopilotAdvanced' # NEW v2.2.3: URI handlers, Edge sidebar, Recall export 'Disable-ClickToDo' 'Disable-SettingsAgent' 'Disable-ExplorerAI' # NEW: File Explorer AI Actions menu diff --git a/Modules/AntiAI/Public/Invoke-AntiAI.ps1 b/Modules/AntiAI/Public/Invoke-AntiAI.ps1 index 67fd3e0..43bbf61 100644 --- a/Modules/AntiAI/Public/Invoke-AntiAI.ps1 +++ b/Modules/AntiAI/Public/Invoke-AntiAI.ps1 @@ -52,7 +52,7 @@ .NOTES Author: NoID Privacy - Version: 2.2.2 + Version: 2.2.3 Requires: Windows 11 24H2 or later, Administrator privileges Impact: All AI features completely disabled, reboot required #> @@ -70,7 +70,7 @@ function Invoke-AntiAI { Write-Host "" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan - Write-Host " ANTI-AI MODULE v2.2.2" -ForegroundColor Cyan + Write-Host " ANTI-AI MODULE v2.2.3" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan Write-Host "" Write-Host "Disables 15 AI features (32 policies):" -ForegroundColor White @@ -171,7 +171,7 @@ function Invoke-AntiAI { @{ Path = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\Paint"; Name = "DisableImageCreator"; Type = "DWord" }, @{ Path = "HKLM:\SOFTWARE\Policies\WindowsNotepad"; Name = "DisableAIFeatures"; Type = "DWord" }, @{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"; Name = "DisableSettingsAgent"; Type = "DWord" }, - # NEW v2.2.2: Advanced Copilot Blocking + # NEW v2.2.3: Advanced Copilot Blocking @{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI"; Name = "AllowRecallExport"; Type = "DWord" }, @{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Edge"; Name = "EdgeSidebarEnabled"; Type = "DWord" }, @{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Edge"; Name = "ShowHubsSidebar"; Type = "DWord" }, @@ -196,7 +196,7 @@ function Invoke-AntiAI { $prop = Get-ItemProperty -Path $t.Path -Name $t.Name -ErrorAction SilentlyContinue if ($null -ne $prop -and $prop.PSObject.Properties.Name -contains $t.Name) { $entry.Exists = $true - $entry.Value = $prop.$($t.Name) + $entry.Value = $prop.$($t.Name) } } } @@ -272,7 +272,8 @@ function Invoke-AntiAI { Register-Backup -Type "AntiAI" -Data $expJson -Name "Explorer_Advanced_Device_JSON" | Out-Null } } - } catch { + } + catch { Write-Host " WARNING: Failed to create JSON backup for Explorer Advanced: $_" -ForegroundColor Yellow } } @@ -355,7 +356,7 @@ function Invoke-AntiAI { } # ============================================================================ - # ADVANCED COPILOT BLOCKING (NEW v2.2.2) + # ADVANCED COPILOT BLOCKING (NEW v2.2.3) # ============================================================================ Write-Host "" Write-Host " [Advanced Copilot Blocks]" -ForegroundColor Cyan diff --git a/Modules/DNS/DNS.psd1 b/Modules/DNS/DNS.psd1 index f1ce468..347dddb 100644 --- a/Modules/DNS/DNS.psd1 +++ b/Modules/DNS/DNS.psd1 @@ -2,7 +2,7 @@ # Module manifest for DNS module RootModule = 'DNS.psm1' - ModuleVersion = '2.2.2' + ModuleVersion = '2.2.3' GUID = 'a8f7b3c9-4e5d-4a2b-9c1d-8f3e5a7b9c2d' Author = 'NexusOne23' CompanyName = 'Open Source Project' diff --git a/Modules/DNS/DNS.psm1 b/Modules/DNS/DNS.psm1 index 2a976ed..47cf81f 100644 --- a/Modules/DNS/DNS.psm1 +++ b/Modules/DNS/DNS.psm1 @@ -12,7 +12,7 @@ .NOTES Author: NoID Privacy - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+, Administrator privileges #> diff --git a/Modules/EdgeHardening/EdgeHardening.psd1 b/Modules/EdgeHardening/EdgeHardening.psd1 index dfb6941..4f3a282 100644 --- a/Modules/EdgeHardening/EdgeHardening.psd1 +++ b/Modules/EdgeHardening/EdgeHardening.psd1 @@ -1,30 +1,30 @@ @{ # Script module or binary module file associated with this manifest - RootModule = 'EdgeHardening.psm1' + RootModule = 'EdgeHardening.psm1' # Version number of this module - ModuleVersion = '2.2.2' + ModuleVersion = '2.2.3' # ID used to uniquely identify this module - GUID = '8e3f4c2a-9b1d-4e7a-a2c5-6f8b3d9e1a4c' + GUID = '8e3f4c2a-9b1d-4e7a-a2c5-6f8b3d9e1a4c' # Author of this module - Author = 'NexusOne23' + Author = 'NexusOne23' # Company or vendor of this module - CompanyName = 'Open Source Project' + CompanyName = 'Open Source Project' # Copyright statement for this module - Copyright = '(c) 2025 NexusOne23. Licensed under GPL-3.0.' + Copyright = '(c) 2025 NexusOne23. Licensed under GPL-3.0.' # Description of the functionality provided by this module - Description = 'Microsoft Edge Security Hardening based on MS Edge v139 Security Baseline. Applies 24 security policies to harden Microsoft Edge browser using native PowerShell (no LGPO.exe dependency). Includes SmartScreen enforcement, site isolation, SSL/TLS hardening, extension blocking, and IE mode restrictions.' + Description = 'Microsoft Edge Security Hardening based on MS Edge v139 Security Baseline. Applies 24 security policies to harden Microsoft Edge browser using native PowerShell (no LGPO.exe dependency). Includes SmartScreen enforcement, site isolation, SSL/TLS hardening, extension blocking, and IE mode restrictions.' # Minimum version of the PowerShell engine required by this module PowerShellVersion = '5.1' # Modules that must be imported into the global environment prior to importing this module - RequiredModules = @() + RequiredModules = @() # Functions to export from this module FunctionsToExport = @( @@ -33,22 +33,22 @@ ) # Cmdlets to export from this module - CmdletsToExport = @() + CmdletsToExport = @() # Variables to export from this module VariablesToExport = @() # Aliases to export from this module - AliasesToExport = @() + AliasesToExport = @() # Private data to pass to the module specified in RootModule/ModuleToProcess - PrivateData = @{ + PrivateData = @{ PSData = @{ - Tags = @('Security', 'Edge', 'Browser', 'Hardening', 'Baseline', 'Windows11', 'Privacy') - LicenseUri = '' - ProjectUri = '' + Tags = @('Security', 'Edge', 'Browser', 'Hardening', 'Baseline', 'Windows11', 'Privacy') + LicenseUri = '' + ProjectUri = '' ReleaseNotes = @" -v2.2.2 - Production Release +v2.2.3 - Production Release - Microsoft Edge v139 Security Baseline implementation - 20 security policies (native PowerShell, no LGPO.exe) - SmartScreen enforcement with override prevention diff --git a/Modules/EdgeHardening/EdgeHardening.psm1 b/Modules/EdgeHardening/EdgeHardening.psm1 index b732a67..1346092 100644 --- a/Modules/EdgeHardening/EdgeHardening.psm1 +++ b/Modules/EdgeHardening/EdgeHardening.psm1 @@ -16,7 +16,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+, Administrator privileges #> diff --git a/Modules/EdgeHardening/Public/Invoke-EdgeHardening.ps1 b/Modules/EdgeHardening/Public/Invoke-EdgeHardening.ps1 index 1ae15af..58b8ea2 100644 --- a/Modules/EdgeHardening/Public/Invoke-EdgeHardening.ps1 +++ b/Modules/EdgeHardening/Public/Invoke-EdgeHardening.ps1 @@ -48,7 +48,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+, Administrator privileges IMPORTANT: This applies Microsoft's recommended security baseline. diff --git a/Modules/EdgeHardening/Public/Test-EdgeHardening.ps1 b/Modules/EdgeHardening/Public/Test-EdgeHardening.ps1 index b0a698d..b6f55e0 100644 --- a/Modules/EdgeHardening/Public/Test-EdgeHardening.ps1 +++ b/Modules/EdgeHardening/Public/Test-EdgeHardening.ps1 @@ -23,7 +23,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Can be run without Administrator privileges #> @@ -103,8 +103,8 @@ function Test-EdgeHardening { return [PSCustomObject]@{ Compliant = $false - Message = "Test failed: $($_.Exception.Message)" - Details = @() + Message = "Test failed: $($_.Exception.Message)" + Details = @() } } } diff --git a/Modules/Privacy/Privacy.psd1 b/Modules/Privacy/Privacy.psd1 index 4198a66..7aabd85 100644 --- a/Modules/Privacy/Privacy.psd1 +++ b/Modules/Privacy/Privacy.psd1 @@ -1,11 +1,11 @@ @{ - RootModule = 'Privacy.psm1' - ModuleVersion = '2.2.2' - GUID = 'a9f7c8d3-2e5b-4a1f-9c3d-7e8f5a6b2c4d' - Author = 'NexusOne23' - CompanyName = 'Open Source Project' - Copyright = '(c) 2025 NexusOne23. Licensed under GPL-3.0.' - Description = 'Privacy & Telemetry hardening module with Bloatware removal and OneDrive configuration. Supports 3 modes: MSRecommended (default), Strict (maximum privacy, apps still work), and Paranoid (hardcore).' + RootModule = 'Privacy.psm1' + ModuleVersion = '2.2.3' + GUID = 'a9f7c8d3-2e5b-4a1f-9c3d-7e8f5a6b2c4d' + Author = 'NexusOne23' + CompanyName = 'Open Source Project' + Copyright = '(c) 2025 NexusOne23. Licensed under GPL-3.0.' + Description = 'Privacy & Telemetry hardening module with Bloatware removal and OneDrive configuration. Supports 3 modes: MSRecommended (default), Strict (maximum privacy, apps still work), and Paranoid (hardcore).' PowerShellVersion = '5.1' @@ -15,15 +15,15 @@ 'Test-PrivacyCompliance' ) - CmdletsToExport = @() + CmdletsToExport = @() VariablesToExport = @() - AliasesToExport = @() + AliasesToExport = @() - PrivateData = @{ + PrivateData = @{ PSData = @{ - Tags = @('Privacy', 'Telemetry', 'Bloatware', 'OneDrive', 'Windows11', 'Security') - LicenseUri = 'https://github.com/yourusername/NoIDPrivacyPro/blob/main/LICENSE' - ProjectUri = 'https://github.com/yourusername/NoIDPrivacyPro' + Tags = @('Privacy', 'Telemetry', 'Bloatware', 'OneDrive', 'Windows11', 'Security') + LicenseUri = 'https://github.com/yourusername/NoIDPrivacyPro/blob/main/LICENSE' + ProjectUri = 'https://github.com/yourusername/NoIDPrivacyPro' ReleaseNotes = 'Initial release - Privacy module with 3-mode support' } } diff --git a/Modules/Privacy/Privacy.psm1 b/Modules/Privacy/Privacy.psm1 index 8021252..6e96d95 100644 --- a/Modules/Privacy/Privacy.psm1 +++ b/Modules/Privacy/Privacy.psm1 @@ -16,7 +16,7 @@ .NOTES Module: Privacy - Version: 2.2.2 + Version: 2.2.3 Author: NoID Privacy #> diff --git a/Modules/SecurityBaseline/Public/Invoke-SecurityBaseline.ps1 b/Modules/SecurityBaseline/Public/Invoke-SecurityBaseline.ps1 index b5ba2c3..cb0e6a3 100644 --- a/Modules/SecurityBaseline/Public/Invoke-SecurityBaseline.ps1 +++ b/Modules/SecurityBaseline/Public/Invoke-SecurityBaseline.ps1 @@ -44,7 +44,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 - Self-Contained Edition + Version: 2.2.3 - Self-Contained Edition Requires: PowerShell 5.1+, Administrator privileges BREAKING CHANGE from v1.0: diff --git a/Modules/SecurityBaseline/SecurityBaseline.psd1 b/Modules/SecurityBaseline/SecurityBaseline.psd1 index 3392997..cf6b04e 100644 --- a/Modules/SecurityBaseline/SecurityBaseline.psd1 +++ b/Modules/SecurityBaseline/SecurityBaseline.psd1 @@ -1,32 +1,32 @@ @{ - RootModule = 'SecurityBaseline.psm1' - ModuleVersion = '2.2.2' - GUID = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890' - Author = 'NexusOne23' - CompanyName = 'Open Source Project' - Copyright = '(c) 2025 NexusOne23. Licensed under GPL-3.0.' - Description = 'Microsoft Security Baseline for Windows 11 25H2 - 425 hardening settings implementing enterprise-grade security standards. Self-contained, no LGPO.exe required. (437 entries parsed, 12 are INF metadata)' + RootModule = 'SecurityBaseline.psm1' + ModuleVersion = '2.2.3' + GUID = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890' + Author = 'NexusOne23' + CompanyName = 'Open Source Project' + Copyright = '(c) 2025 NexusOne23. Licensed under GPL-3.0.' + Description = 'Microsoft Security Baseline for Windows 11 25H2 - 425 hardening settings implementing enterprise-grade security standards. Self-contained, no LGPO.exe required. (437 entries parsed, 12 are INF metadata)' PowerShellVersion = '5.1' - RequiredModules = @() + RequiredModules = @() FunctionsToExport = @( 'Invoke-SecurityBaseline', 'Restore-SecurityBaseline' ) - CmdletsToExport = @() + CmdletsToExport = @() VariablesToExport = @() - AliasesToExport = @() + AliasesToExport = @() - PrivateData = @{ + PrivateData = @{ PSData = @{ - Tags = @('Security', 'Hardening', 'Windows11', 'Baseline', 'Microsoft', 'Enterprise') - LicenseUri = '' - ProjectUri = '' + Tags = @('Security', 'Hardening', 'Windows11', 'Baseline', 'Microsoft', 'Enterprise') + LicenseUri = '' + ProjectUri = '' ReleaseNotes = @" -v2.2.2 - Self-Contained Edition +v2.2.3 - Self-Contained Edition - NO LGPO.exe REQUIRED! Fully self-contained implementation - 425 Microsoft Security Baseline settings for Windows 11 25H2 - 335 Registry policies (Computer + User) diff --git a/Modules/SecurityBaseline/SecurityBaseline.psm1 b/Modules/SecurityBaseline/SecurityBaseline.psm1 index a8579fc..74ee338 100644 --- a/Modules/SecurityBaseline/SecurityBaseline.psm1 +++ b/Modules/SecurityBaseline/SecurityBaseline.psm1 @@ -13,7 +13,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+, Administrator privileges #> diff --git a/NoIDPrivacy-Interactive.ps1 b/NoIDPrivacy-Interactive.ps1 index 2bd396f..fb30c6e 100644 --- a/NoIDPrivacy-Interactive.ps1 +++ b/NoIDPrivacy-Interactive.ps1 @@ -19,7 +19,7 @@ resulting from its use. USE AT YOUR OWN RISK. Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+, Administrator For CLI mode use: NoIDPrivacy.ps1 -Module #> @@ -30,7 +30,7 @@ # No parameters - interactive mode only $ErrorActionPreference = 'Stop' -$Host.UI.RawUI.WindowTitle = "NoID Privacy v2.2.2" +$Host.UI.RawUI.WindowTitle = "NoID Privacy v2.2.3" # Set script root path (required by modules to load configs) $script:RootPath = $PSScriptRoot @@ -90,7 +90,7 @@ function Write-Banner { Clear-Host Write-Host "" Write-Host " ========================================" -ForegroundColor Cyan - Write-Host " NoID Privacy v2.2.2 " -ForegroundColor Cyan + Write-Host " NoID Privacy v2.2.3 " -ForegroundColor Cyan Write-Host " ========================================" -ForegroundColor Cyan Write-Host "" Write-Host " Professional Windows 11 Security & Privacy Hardening Framework" -ForegroundColor Gray @@ -105,7 +105,7 @@ function Write-Banner { $osBuild = if ($os) { $os.BuildNumber } else { $null } $psVersion = $PSVersionTable.PSVersion.ToString() - $envLine = " Version 2.2.2" + $envLine = " Version 2.2.3" if ($osBuild) { $envLine += " | Windows Build $osBuild" } diff --git a/NoIDPrivacy.ps1 b/NoIDPrivacy.ps1 index e32b83e..a114a19 100644 --- a/NoIDPrivacy.ps1 +++ b/NoIDPrivacy.ps1 @@ -50,7 +50,7 @@ resulting from its use. USE AT YOUR OWN RISK. Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+, Administrator privileges, Windows 11 License: GPL-3.0 (Core CLI). See LICENSE for full terms. @@ -111,13 +111,13 @@ $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_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: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 @@ -135,7 +135,7 @@ try { $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 "=== NoID Privacy Framework v2.2.3 ===" -Module "Main" Write-Log -Level INFO -Message "Starting framework initialization..." -Module "Main" # Load other Core modules @@ -216,7 +216,7 @@ catch { # Display banner Write-Host "" Write-Host "========================================" -ForegroundColor Cyan -Write-Host " NoID Privacy - v2.2.2" -ForegroundColor Cyan +Write-Host " NoID Privacy - v2.2.3" -ForegroundColor Cyan Write-Host " Windows 11 Security Hardening" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan Write-Host "" @@ -255,23 +255,23 @@ if (-not $Module) { 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')) { + 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')) + } 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" } + "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" { + "V" { # Verify all settings Write-Host "" Write-Host "Running complete verification..." -ForegroundColor Cyan @@ -291,7 +291,7 @@ if (-not $Module) { $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") exit 0 } - "R" { + "R" { # Restore from backup - Interactive session selection from disk Write-Host "" Write-Host "========================================" -ForegroundColor Cyan @@ -365,7 +365,7 @@ if (-not $Module) { $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") exit 0 } - "B" { + "B" { # List backups Write-Host "" Write-Host "========================================" -ForegroundColor Cyan @@ -387,8 +387,8 @@ if (-not $Module) { 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" } + 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 @@ -404,7 +404,7 @@ if (-not $Module) { $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") exit 0 } - "I" { + "I" { # System information Write-Host "" Write-Host "========================================" -ForegroundColor Cyan @@ -451,7 +451,7 @@ if (-not $Module) { $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") exit 0 } - "0" { + "0" { Write-Host "Exiting..." -ForegroundColor Yellow exit 0 } diff --git a/README.md b/README.md index b65fbee..63a9fb5 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ [![PowerShell](https://img.shields.io/badge/PowerShell-5.1%2B-blue.svg?logo=powershell)](https://github.com/PowerShell/PowerShell) [![Windows 11](https://img.shields.io/badge/Windows%2011-25H2-0078D4.svg?logo=windows11)](https://www.microsoft.com/windows/) [![License](https://img.shields.io/badge/license-GPL--3.0-green.svg?logo=gnu)](LICENSE) -[![Version](https://img.shields.io/badge/version-2.2.2-blue.svg)](CHANGELOG.md) +[![Version](https://img.shields.io/badge/version-2.2.3-blue.svg)](CHANGELOG.md) [![Status](https://img.shields.io/badge/status-production--ready-brightgreen.svg)]() --- @@ -391,13 +391,13 @@ cd noid-privacy | Module | Settings | Description | Status | |--------|----------|-------------|--------| -| **SecurityBaseline** | 425 | Microsoft Security Baseline 25H2 | v2.2.2 | -| **ASR** | 19 | Attack Surface Reduction Rules | v2.2.2 | -| **DNS** | 5 | Secure DNS with DoH encryption | v2.2.2 | -| **Privacy** | 78 | Telemetry, Bloatware, OneDrive hardening (Strict) | v2.2.2 | -| **AntiAI** | 32 | AI lockdown (15 features, 32 compliance checks) | v2.2.2 | -| **EdgeHardening** | 24 | Microsoft Edge security (24 policies) | v2.2.2 | -| **AdvancedSecurity** | 50 | Beyond MS Baseline (SRP, Legacy protocols, Wireless Display, Discovery Protocols, IPv6) | v2.2.2 | +| **SecurityBaseline** | 425 | Microsoft Security Baseline 25H2 | v2.2.3 | +| **ASR** | 19 | Attack Surface Reduction Rules | v2.2.3 | +| **DNS** | 5 | Secure DNS with DoH encryption | v2.2.3 | +| **Privacy** | 78 | Telemetry, Bloatware, OneDrive hardening (Strict) | v2.2.3 | +| **AntiAI** | 32 | AI lockdown (15 features, 32 compliance checks) | v2.2.3 | +| **EdgeHardening** | 24 | Microsoft Edge security (24 policies) | v2.2.3 | +| **AdvancedSecurity** | 50 | Beyond MS Baseline (SRP, Legacy protocols, Wireless Display, Discovery Protocols, IPv6) | v2.2.3 | | **TOTAL** | **633** | **Complete Framework (Paranoid mode)** | **Production** | **Release Highlights:** @@ -852,10 +852,15 @@ The authors are not responsible for any damage or data loss. ## 📈 Project Status -**Current Version:** 2.2.2 -**Last Updated:** December 22, 2025 +**Current Version:** 2.2.3 +**Last Updated:** January 7, 2026 **Status:** Production-Ready +### Release Highlights v2.2.3 + +- **Critical Fix:** Restore Mode manual module selection crash +- Fix: `.Split()` wrong .NET overload → `-split` operator + ### Release Highlights v2.2.2 - **Performance:** Firewall snapshot 60-120s → 2-5s (batch query fix) @@ -867,15 +872,6 @@ The authors are not responsible for any damage or data loss. - **Fix:** `.Count` property bug in 5 files (Where-Object single-object results) - **Improved:** ASR prompt text ("untrusted" → "new software" - more neutral) -### Release Highlights v2.2.0 - -- 630+ settings (expanded from 580+) -- NonInteractive mode for GUI integration -- Third-party AV detection and graceful ASR skip -- AntiAI enhanced to 32 policies (was 24) -- Pre-Framework ASR snapshot -- Smart Registry Backup with JSON fallback - 📋 [See Full Changelog](CHANGELOG.md) --- diff --git a/Start-NoIDPrivacy.bat b/Start-NoIDPrivacy.bat index 54309b6..4411509 100644 --- a/Start-NoIDPrivacy.bat +++ b/Start-NoIDPrivacy.bat @@ -7,12 +7,12 @@ REM This script launches NoIDPrivacy-Interactive.ps1 with REM Administrator privileges (auto-elevation). REM REM Author: NexusOne23 -REM Version: 2.2.2 +REM Version: 2.2.3 REM ======================================== setlocal -title NoID Privacy v2.2.2 +title NoID Privacy v2.2.3 REM Get the directory where this batch file is located set "SCRIPT_DIR=%~dp0" diff --git a/Tests/Run-Tests.ps1 b/Tests/Run-Tests.ps1 index c519315..1202887 100644 --- a/Tests/Run-Tests.ps1 +++ b/Tests/Run-Tests.ps1 @@ -17,7 +17,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+, Pester 5.0+ .EXAMPLE diff --git a/Tests/Setup-TestEnvironment.ps1 b/Tests/Setup-TestEnvironment.ps1 index d1205a0..92c958b 100644 --- a/Tests/Setup-TestEnvironment.ps1 +++ b/Tests/Setup-TestEnvironment.ps1 @@ -8,7 +8,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+ .EXAMPLE diff --git a/Tests/Unit/ASR.Tests.ps1 b/Tests/Unit/ASR.Tests.ps1 index 18f38a2..f612724 100644 --- a/Tests/Unit/ASR.Tests.ps1 +++ b/Tests/Unit/ASR.Tests.ps1 @@ -8,7 +8,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: Pester 5.0+ #> diff --git a/Tests/Unit/AdvancedSecurity.Tests.ps1 b/Tests/Unit/AdvancedSecurity.Tests.ps1 index aa1111c..f238956 100644 --- a/Tests/Unit/AdvancedSecurity.Tests.ps1 +++ b/Tests/Unit/AdvancedSecurity.Tests.ps1 @@ -8,7 +8,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: Pester 5.0+ #> diff --git a/Tests/Unit/AntiAI.Tests.ps1 b/Tests/Unit/AntiAI.Tests.ps1 index 7903243..99243f9 100644 --- a/Tests/Unit/AntiAI.Tests.ps1 +++ b/Tests/Unit/AntiAI.Tests.ps1 @@ -8,7 +8,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: Pester 5.0+ #> diff --git a/Tests/Unit/DNS.Tests.ps1 b/Tests/Unit/DNS.Tests.ps1 index 35e959d..3e762e7 100644 --- a/Tests/Unit/DNS.Tests.ps1 +++ b/Tests/Unit/DNS.Tests.ps1 @@ -8,7 +8,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: Pester 5.0+ #> diff --git a/Tests/Unit/EdgeHardening.Tests.ps1 b/Tests/Unit/EdgeHardening.Tests.ps1 index e81dc85..20b3afd 100644 --- a/Tests/Unit/EdgeHardening.Tests.ps1 +++ b/Tests/Unit/EdgeHardening.Tests.ps1 @@ -8,7 +8,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: Pester 5.0+ #> diff --git a/Tests/Unit/ModuleTemplate.Tests.ps1 b/Tests/Unit/ModuleTemplate.Tests.ps1 index e420dbe..7bdf242 100644 --- a/Tests/Unit/ModuleTemplate.Tests.ps1 +++ b/Tests/Unit/ModuleTemplate.Tests.ps1 @@ -8,7 +8,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: Pester 5.0+ #> diff --git a/Tests/Unit/Privacy.Tests.ps1 b/Tests/Unit/Privacy.Tests.ps1 index d363d90..d29b352 100644 --- a/Tests/Unit/Privacy.Tests.ps1 +++ b/Tests/Unit/Privacy.Tests.ps1 @@ -8,7 +8,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: Pester 5.0+ #> diff --git a/Tools/Generate-ReleaseChecksums.ps1 b/Tools/Generate-ReleaseChecksums.ps1 index 3ddc0a5..8c2dc24 100644 --- a/Tools/Generate-ReleaseChecksums.ps1 +++ b/Tools/Generate-ReleaseChecksums.ps1 @@ -13,10 +13,10 @@ Output file for checksums. Default: CHECKSUMS.sha256 in the same directory. .EXAMPLE - .\Generate-ReleaseChecksums.ps1 -ReleasePath "C:\Release\NoIDPrivacy-v2.2.2" + .\Generate-ReleaseChecksums.ps1 -ReleasePath "C:\Release\NoIDPrivacy-v2.2.3" .EXAMPLE - .\Generate-ReleaseChecksums.ps1 -ReleasePath ".\NoIDPrivacy-v2.2.2.zip" + .\Generate-ReleaseChecksums.ps1 -ReleasePath ".\NoIDPrivacy-v2.2.3.zip" #> [CmdletBinding()] @@ -36,10 +36,12 @@ Write-Host "`n=== NoID Privacy Release Checksum Generator ===" -ForegroundColor if (Test-Path $ReleasePath -PathType Container) { $files = Get-ChildItem -Path $ReleasePath -File -Recurse | Where-Object { $_.Extension -in '.zip', '.exe', '.ps1', '.psm1' } $basePath = $ReleasePath -} elseif (Test-Path $ReleasePath -PathType Leaf) { +} +elseif (Test-Path $ReleasePath -PathType Leaf) { $files = Get-Item $ReleasePath $basePath = Split-Path $ReleasePath -Parent -} else { +} +else { Write-Error "Path not found: $ReleasePath" exit 1 } diff --git a/Tools/Parse-EdgeBaseline.ps1 b/Tools/Parse-EdgeBaseline.ps1 index 2484765..c252314 100644 --- a/Tools/Parse-EdgeBaseline.ps1 +++ b/Tools/Parse-EdgeBaseline.ps1 @@ -18,7 +18,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+ .EXAMPLE @@ -142,21 +142,26 @@ function Read-PolFile { # Parse based on type switch ($type) { - 1 { # REG_SZ (String) + 1 { + # REG_SZ (String) $data = [System.Text.Encoding]::Unicode.GetString($dataBytes).TrimEnd([char]0) } - 2 { # REG_EXPAND_SZ + 2 { + # REG_EXPAND_SZ $data = [System.Text.Encoding]::Unicode.GetString($dataBytes).TrimEnd([char]0) } - 3 { # REG_BINARY + 3 { + # REG_BINARY $data = $dataBytes } - 4 { # REG_DWORD + 4 { + # REG_DWORD if ($dataBytes.Length -ge 4) { $data = [BitConverter]::ToInt32($dataBytes, 0) } } - 7 { # REG_MULTI_SZ + 7 { + # REG_MULTI_SZ $data = [System.Text.Encoding]::Unicode.GetString($dataBytes).TrimEnd([char]0) -split '\x00' } default { @@ -172,9 +177,9 @@ function Read-PolFile { # Add entry $entries += [PSCustomObject]@{ - KeyName = $keyName + KeyName = $keyName ValueName = $valueName - Type = switch ($type) { + Type = switch ($type) { 1 { "REG_SZ" } 2 { "REG_EXPAND_SZ" } 3 { "REG_BINARY" } @@ -183,7 +188,7 @@ function Read-PolFile { 11 { "REG_QWORD" } default { "Unknown($type)" } } - Data = $data + Data = $data } } @@ -273,9 +278,9 @@ if ($allComputerPolicies.Count -gt 0) { # Create summary $summary = [PSCustomObject]@{ TotalEdgePolicies = $allComputerPolicies.Count - ParsedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" - BaselineVersion = "Edge v139" - RegistryPaths = ($allComputerPolicies | Select-Object -ExpandProperty KeyName -Unique | Sort-Object) + ParsedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + BaselineVersion = "Edge v139" + RegistryPaths = ($allComputerPolicies | Select-Object -ExpandProperty KeyName -Unique | Sort-Object) } $summaryFile = Join-Path $OutputPath "Summary.json" diff --git a/Tools/Parse-SecurityBaseline.ps1 b/Tools/Parse-SecurityBaseline.ps1 index 3d0f09c..2afc22b 100644 --- a/Tools/Parse-SecurityBaseline.ps1 +++ b/Tools/Parse-SecurityBaseline.ps1 @@ -25,7 +25,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+ .EXAMPLE @@ -150,21 +150,26 @@ function Read-PolFile { # Parse based on type switch ($type) { - 1 { # REG_SZ (String) + 1 { + # REG_SZ (String) $data = [System.Text.Encoding]::Unicode.GetString($dataBytes).TrimEnd([char]0) } - 2 { # REG_EXPAND_SZ + 2 { + # REG_EXPAND_SZ $data = [System.Text.Encoding]::Unicode.GetString($dataBytes).TrimEnd([char]0) } - 3 { # REG_BINARY + 3 { + # REG_BINARY $data = $dataBytes } - 4 { # REG_DWORD + 4 { + # REG_DWORD if ($dataBytes.Length -ge 4) { $data = [BitConverter]::ToInt32($dataBytes, 0) } } - 7 { # REG_MULTI_SZ + 7 { + # REG_MULTI_SZ $data = [System.Text.Encoding]::Unicode.GetString($dataBytes).TrimEnd([char]0) -split '\x00' } default { @@ -180,9 +185,9 @@ function Read-PolFile { # Add entry $entries += [PSCustomObject]@{ - KeyName = $keyName + KeyName = $keyName ValueName = $valueName - Type = switch ($type) { + Type = switch ($type) { 1 { "REG_SZ" } 2 { "REG_EXPAND_SZ" } 3 { "REG_BINARY" } @@ -191,7 +196,7 @@ function Read-PolFile { 11 { "REG_QWORD" } default { "Unknown($type)" } } - Data = $data + Data = $data } } @@ -296,10 +301,10 @@ function Read-AuditCsv { # Skip header row $policies = $csv | Select-Object -Skip 1 | ForEach-Object { [PSCustomObject]@{ - Subcategory = $_.'Subcategory' - SubcategoryGUID = $_.'Subcategory GUID' + Subcategory = $_.'Subcategory' + SubcategoryGUID = $_.'Subcategory GUID' InclusionSetting = $_.'Inclusion Setting' - SettingValue = $_.'Setting Value' + SettingValue = $_.'Setting Value' } } @@ -346,17 +351,17 @@ $gpoMapping = @{ $gpoPath = Join-Path $BaselinePath "GPOs" $allSettings = @{ - RegistryPolicies = @{ + RegistryPolicies = @{ Computer = @() - User = @() + User = @() } SecurityTemplates = @{} - AuditPolicies = @() - Summary = @{ + AuditPolicies = @() + Summary = @{ TotalRegistrySettings = 0 TotalSecuritySettings = 0 - TotalAuditPolicies = 0 - TotalSettings = 0 + TotalAuditPolicies = 0 + TotalSettings = 0 } } @@ -381,11 +386,11 @@ foreach ($guid in $gpoMapping.Keys) { foreach ($entry in $entries) { $allSettings.RegistryPolicies.Computer += [PSCustomObject]@{ - GPO = $gpoName - KeyName = $entry.KeyName + GPO = $gpoName + KeyName = $entry.KeyName ValueName = $entry.ValueName - Type = $entry.Type - Data = $entry.Data + Type = $entry.Type + Data = $entry.Data } } @@ -401,11 +406,11 @@ foreach ($guid in $gpoMapping.Keys) { foreach ($entry in $entries) { $allSettings.RegistryPolicies.User += [PSCustomObject]@{ - GPO = $gpoName - KeyName = $entry.KeyName + GPO = $gpoName + KeyName = $entry.KeyName ValueName = $entry.ValueName - Type = $entry.Type - Data = $entry.Data + Type = $entry.Type + Data = $entry.Data } } @@ -435,11 +440,11 @@ foreach ($guid in $gpoMapping.Keys) { foreach ($policy in $policies) { $allSettings.AuditPolicies += [PSCustomObject]@{ - GPO = $gpoName - Subcategory = $policy.Subcategory - SubcategoryGUID = $policy.SubcategoryGUID + GPO = $gpoName + Subcategory = $policy.Subcategory + SubcategoryGUID = $policy.SubcategoryGUID InclusionSetting = $policy.InclusionSetting - SettingValue = $policy.SettingValue + SettingValue = $policy.SettingValue } } @@ -452,8 +457,8 @@ foreach ($guid in $gpoMapping.Keys) { # Calculate total $allSettings.Summary.TotalSettings = $allSettings.Summary.TotalRegistrySettings + - $allSettings.Summary.TotalSecuritySettings + - $allSettings.Summary.TotalAuditPolicies +$allSettings.Summary.TotalSecuritySettings + +$allSettings.Summary.TotalAuditPolicies # Save outputs Write-Host "Saving parsed settings..." -ForegroundColor Cyan diff --git a/Tools/Verify-Complete-Hardening.ps1 b/Tools/Verify-Complete-Hardening.ps1 index 6aadf2a..533bebf 100644 --- a/Tools/Verify-Complete-Hardening.ps1 +++ b/Tools/Verify-Complete-Hardening.ps1 @@ -27,7 +27,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 #> #Requires -Version 5.1 @@ -130,10 +130,10 @@ function Get-RegistryChecksFromJson { # Skip metadata and excluded categories # NOTE: EnterpriseProtection is NOT skipped - it contains valid registry paths! if ($propName -in @('Mode', 'Description', 'BestFor', 'Warnings', 'Services', 'ScheduledTasks', - 'Summary', 'AutomaticallyBlockedByMasterSwitch', 'ModuleName', 'Version', - 'TotalFeatures', 'TotalPolicies', 'URIHandlers', 'Note', 'FilePath', - 'HostsEntries', 'CloudBased', 'RequiresReboot', - 'RequiresADMX', 'Impact', 'Name')) { + 'Summary', 'AutomaticallyBlockedByMasterSwitch', 'ModuleName', 'Version', + 'TotalFeatures', 'TotalPolicies', 'URIHandlers', 'Note', 'FilePath', + 'HostsEntries', 'CloudBased', 'RequiresReboot', + 'RequiresADMX', 'Impact', 'Name')) { continue } @@ -962,78 +962,16 @@ try { $currentASRIds = $mpPreference.AttackSurfaceReductionRules_Ids $currentASRActions = $mpPreference.AttackSurfaceReductionRules_Actions - # Load expected ASR rules - JSON is array directly - $asrRules = Get-Content (Join-Path $asrConfigPath "ASR-Rules.json") -Raw | ConvertFrom-Json + # Load expected ASR rules - JSON is array directly + $asrRules = Get-Content (Join-Path $asrConfigPath "ASR-Rules.json") -Raw | ConvertFrom-Json - $asrFailed = @() - $asrPassed = @() + $asrFailed = @() + $asrPassed = @() - # Check if ASR rules are configured at all - if ($null -eq $currentASRIds -or $currentASRIds.Count -eq 0) { - # No ASR rules configured - mark all as failed - foreach ($rule in $asrRules) { - $results.Failed++ - $expectedActionText = if ($rule.Action -eq 1) { "Block" } elseif ($rule.Action -eq 2) { "Audit" } else { "Disabled" } - $asrFailed += [PSCustomObject]@{ - Rule = $rule.Name - GUID = $rule.GUID - Expected = $expectedActionText - Actual = "Not configured" - } - } - } - else { - # Rules where both BLOCK (1) and AUDIT (2) are considered "Pass" - # These are user-configurable rules where either mode is valid - $flexibleRules = @( - "d1e49aac-8f56-4280-b9ba-993a6d77406c", # PSExec/WMI (Management Tools) - "01443614-cd74-433a-b99e-2ecdc07bfc25" # Prevalence (New/Unknown Software) - ) - - foreach ($rule in $asrRules) { - # Case-insensitive GUID matching (Get-MpPreference may return different case) - $index = -1 - for ($i = 0; $i -lt $currentASRIds.Count; $i++) { - if ($currentASRIds[$i] -eq $rule.GUID) { - $index = $i - break - } - } - - if ($index -ge 0) { - $actualAction = $currentASRActions[$index] - $expectedAction = $rule.Action - - # Check if this is a flexible rule (Block or Audit both count as Pass) - $isFlexibleRule = $flexibleRules -contains $rule.GUID - $isActiveMode = $actualAction -in @(1, 2) # Block or Audit - - # For flexible rules: Pass if Block OR Audit - # For other rules: Pass only if exact match - $rulePassed = if ($isFlexibleRule) { $isActiveMode } else { $actualAction -eq $expectedAction } - - if ($rulePassed) { - $results.Verified++ - $actionText = if ($actualAction -eq 1) { "Block" } elseif ($actualAction -eq 2) { "Audit" } else { "Disabled" } - $asrPassed += [PSCustomObject]@{ - Rule = $rule.Name - Expected = $actionText - Actual = $actionText - } - } - else { - $results.Failed++ - $expectedActionText = if ($expectedAction -eq 1) { "Block" } elseif ($expectedAction -eq 2) { "Audit" } else { "Disabled" } - $actualActionText = if ($actualAction -eq 1) { "Block" } elseif ($actualAction -eq 2) { "Audit" } else { "Disabled" } - $asrFailed += [PSCustomObject]@{ - Rule = $rule.Name - GUID = $rule.GUID - Expected = $expectedActionText - Actual = $actualActionText - } - } - } - else { + # Check if ASR rules are configured at all + if ($null -eq $currentASRIds -or $currentASRIds.Count -eq 0) { + # No ASR rules configured - mark all as failed + foreach ($rule in $asrRules) { $results.Failed++ $expectedActionText = if ($rule.Action -eq 1) { "Block" } elseif ($rule.Action -eq 2) { "Audit" } else { "Disabled" } $asrFailed += [PSCustomObject]@{ @@ -1044,28 +982,90 @@ try { } } } - } - - # Add to AllSettings for HTML report - $asrPassedCount = $results.ASRRules - $asrFailed.Count - $results.AllSettings += [PSCustomObject]@{ - Category = "ASR" - Total = $results.ASRRules - Passed = $asrPassedCount - Failed = $asrFailed.Count - PassedDetails = $asrPassed - FailedDetails = $asrFailed - } - - if ($asrFailed.Count -gt 0) { - $results.FailedSettings += [PSCustomObject]@{ - Category = "ASR" - Count = $asrFailed.Count - Details = $asrFailed + else { + # Rules where both BLOCK (1) and AUDIT (2) are considered "Pass" + # These are user-configurable rules where either mode is valid + $flexibleRules = @( + "d1e49aac-8f56-4280-b9ba-993a6d77406c", # PSExec/WMI (Management Tools) + "01443614-cd74-433a-b99e-2ecdc07bfc25" # Prevalence (New/Unknown Software) + ) + + foreach ($rule in $asrRules) { + # Case-insensitive GUID matching (Get-MpPreference may return different case) + $index = -1 + for ($i = 0; $i -lt $currentASRIds.Count; $i++) { + if ($currentASRIds[$i] -eq $rule.GUID) { + $index = $i + break + } + } + + if ($index -ge 0) { + $actualAction = $currentASRActions[$index] + $expectedAction = $rule.Action + + # Check if this is a flexible rule (Block or Audit both count as Pass) + $isFlexibleRule = $flexibleRules -contains $rule.GUID + $isActiveMode = $actualAction -in @(1, 2) # Block or Audit + + # For flexible rules: Pass if Block OR Audit + # For other rules: Pass only if exact match + $rulePassed = if ($isFlexibleRule) { $isActiveMode } else { $actualAction -eq $expectedAction } + + if ($rulePassed) { + $results.Verified++ + $actionText = if ($actualAction -eq 1) { "Block" } elseif ($actualAction -eq 2) { "Audit" } else { "Disabled" } + $asrPassed += [PSCustomObject]@{ + Rule = $rule.Name + Expected = $actionText + Actual = $actionText + } + } + else { + $results.Failed++ + $expectedActionText = if ($expectedAction -eq 1) { "Block" } elseif ($expectedAction -eq 2) { "Audit" } else { "Disabled" } + $actualActionText = if ($actualAction -eq 1) { "Block" } elseif ($actualAction -eq 2) { "Audit" } else { "Disabled" } + $asrFailed += [PSCustomObject]@{ + Rule = $rule.Name + GUID = $rule.GUID + Expected = $expectedActionText + Actual = $actualActionText + } + } + } + else { + $results.Failed++ + $expectedActionText = if ($rule.Action -eq 1) { "Block" } elseif ($rule.Action -eq 2) { "Audit" } else { "Disabled" } + $asrFailed += [PSCustomObject]@{ + Rule = $rule.Name + GUID = $rule.GUID + Expected = $expectedActionText + Actual = "Not configured" + } + } + } } - } - Write-Host " ASR Rules: $($results.ASRRules - $asrFailed.Count)/$($results.ASRRules) verified" -ForegroundColor $(if ($asrFailed.Count -eq 0) { "Green" } else { "Yellow" }) + # Add to AllSettings for HTML report + $asrPassedCount = $results.ASRRules - $asrFailed.Count + $results.AllSettings += [PSCustomObject]@{ + Category = "ASR" + Total = $results.ASRRules + Passed = $asrPassedCount + Failed = $asrFailed.Count + PassedDetails = $asrPassed + FailedDetails = $asrFailed + } + + if ($asrFailed.Count -gt 0) { + $results.FailedSettings += [PSCustomObject]@{ + Category = "ASR" + Count = $asrFailed.Count + Details = $asrFailed + } + } + + Write-Host " ASR Rules: $($results.ASRRules - $asrFailed.Count)/$($results.ASRRules) verified" -ForegroundColor $(if ($asrFailed.Count -eq 0) { "Green" } else { "Yellow" }) } # End of else (Defender active) } catch { @@ -1303,8 +1303,8 @@ try { if ($entry.ServerAddresses -and $entry.ServerAddresses.Count -gt 0) { # Check if it's not DHCP (empty or localhost fallback) $isDHCP = ($entry.ServerAddresses.Count -eq 0) -or - ($entry.ServerAddresses -contains '127.0.0.1') -or - ($entry.AddressOrigin -eq 'DHCP') + ($entry.ServerAddresses -contains '127.0.0.1') -or + ($entry.AddressOrigin -eq 'DHCP') if (-not $isDHCP) { $staticDNS = $true @@ -1796,10 +1796,10 @@ try { # We validate both Enabled=0 and DisabledByDefault=1 per version/component $tlsChecks = @( # Enabled flags - @{ Path = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server"; Name = "Enabled"; Expected = 0; Desc = "TLS 1.0 Server Disabled"; Optional = $false } - @{ Path = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client"; Name = "Enabled"; Expected = 0; Desc = "TLS 1.0 Client Disabled"; Optional = $false } - @{ Path = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server"; Name = "Enabled"; Expected = 0; Desc = "TLS 1.1 Server Disabled"; Optional = $false } - @{ Path = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client"; Name = "Enabled"; Expected = 0; Desc = "TLS 1.1 Client Disabled"; Optional = $false } + @{ Path = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server"; Name = "Enabled"; Expected = 0; Desc = "TLS 1.0 Server Disabled"; Optional = $false } + @{ Path = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client"; Name = "Enabled"; Expected = 0; Desc = "TLS 1.0 Client Disabled"; Optional = $false } + @{ Path = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server"; Name = "Enabled"; Expected = 0; Desc = "TLS 1.1 Server Disabled"; Optional = $false } + @{ Path = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client"; Name = "Enabled"; Expected = 0; Desc = "TLS 1.1 Client Disabled"; Optional = $false } # DisabledByDefault flags @{ Path = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server"; Name = "DisabledByDefault"; Expected = 1; Desc = "TLS 1.0 Server DisabledByDefault"; Optional = $false } @{ Path = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client"; Name = "DisabledByDefault"; Expected = 1; Desc = "TLS 1.0 Client DisabledByDefault"; Optional = $false } @@ -1812,24 +1812,24 @@ try { # Reference: https://learn.microsoft.com/en-us/troubleshoot/windows-server/networking/disable-http-proxy-auth-features # NOTE: HKCU AutoDetect is set per-user via HKU in Apply, verified separately below $wpadChecks = @( - @{ Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\WinHttp"; Name = "DisableWpad"; Expected = 1; Desc = "WPAD Disabled (Official MS Key)"; Optional = $false } - @{ Path = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Wpad"; Name = "WpadOverride"; Expected = 1; Desc = "WPAD Disabled (WpadOverride)"; Optional = $false } - @{ Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings"; Name = "AutoDetect"; Expected = 0; Desc = "WPAD AutoDetect (HKLM)"; Optional = $false } + @{ Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\WinHttp"; Name = "DisableWpad"; Expected = 1; Desc = "WPAD Disabled (Official MS Key)"; Optional = $false } + @{ Path = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Wpad"; Name = "WpadOverride"; Expected = 1; Desc = "WPAD Disabled (WpadOverride)"; Optional = $false } + @{ Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings"; Name = "AutoDetect"; Expected = 0; Desc = "WPAD AutoDetect (HKLM)"; Optional = $false } ) # SRP Root Policy (2 checks) - ALWAYS required for CVE-2025-9491 mitigation $srpRootChecks = @( - @{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Safer\CodeIdentifiers"; Name = "DefaultLevel"; Expected = 262144; Desc = "SRP DefaultLevel (Unrestricted)"; Optional = $false } - @{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Safer\CodeIdentifiers"; Name = "TransparentEnabled"; Expected = 1; Desc = "SRP TransparentEnabled"; Optional = $false } + @{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Safer\CodeIdentifiers"; Name = "DefaultLevel"; Expected = 262144; Desc = "SRP DefaultLevel (Unrestricted)"; Optional = $false } + @{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Safer\CodeIdentifiers"; Name = "TransparentEnabled"; Expected = 1; Desc = "SRP TransparentEnabled"; Optional = $false } ) # Firewall Shields Up (1 check) - Maximum profile only, blocks ALL incoming on Public network # Optional = true because it's only applied for Maximum profile (user choice) $shieldsUpCheck = @{ - Path = "HKLM:\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\PublicProfile" - Name = "DoNotAllowExceptions" + Path = "HKLM:\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\PublicProfile" + Name = "DoNotAllowExceptions" Expected = 1 - Desc = "Firewall Shields Up (Maximum only)" + Desc = "Firewall Shields Up (Maximum only)" Optional = $true } @@ -1837,10 +1837,10 @@ try { # Optional = true because only applied for Maximum profile (user choice) # Check 1: mDNS disabled via registry $discoveryMdnsCheck = @{ - Path = "HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters" - Name = "EnableMDNS" + Path = "HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters" + Name = "EnableMDNS" Expected = 0 - Desc = "Discovery Protocols: mDNS Disabled (Maximum only)" + Desc = "Discovery Protocols: mDNS Disabled (Maximum only)" Optional = $true } @@ -1850,10 +1850,10 @@ try { # IPv6 Disable (mitm6 attack mitigation) - Maximum profile only # Optional = true because only applied for Maximum profile (user choice) $ipv6Check = @{ - Path = "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters" - Name = "DisabledComponents" + Path = "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters" + Name = "DisabledComponents" Expected = 255 # 0xFF = completely disabled - Desc = "IPv6 Disabled (mitm6 mitigation, Maximum only)" + Desc = "IPv6 Disabled (mitm6 mitigation, Maximum only)" Optional = $true } @@ -1888,10 +1888,10 @@ try { # Windows Update (4 Checks) - ALWAYS required - matches AdvancedSecurity module Config/WindowsUpdate.json $wuChecks = @( - @{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"; Name = "AllowOptionalContent"; Expected = 1; Desc = "WU: Get latest updates immediately (Policy)"; Optional = $false } - @{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"; Name = "SetAllowOptionalContent"; Expected = 1; Desc = "WU: AllowOptionalContent Policy Flag"; Optional = $false } - @{ Path = "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings"; Name = "AllowMUUpdateService"; Expected = 1; Desc = "WU: Microsoft Update (Office, drivers)"; Optional = $false } - @{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DeliveryOptimization"; Name = "DODownloadMode"; Expected = 0; Desc = "WU: P2P Delivery Optimization OFF"; Optional = $false } + @{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"; Name = "AllowOptionalContent"; Expected = 1; Desc = "WU: Get latest updates immediately (Policy)"; Optional = $false } + @{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"; Name = "SetAllowOptionalContent"; Expected = 1; Desc = "WU: AllowOptionalContent Policy Flag"; Optional = $false } + @{ Path = "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings"; Name = "AllowMUUpdateService"; Expected = 1; Desc = "WU: Microsoft Update (Office, drivers)"; Optional = $false } + @{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DeliveryOptimization"; Name = "DODownloadMode"; Expected = 0; Desc = "WU: P2P Delivery Optimization OFF"; Optional = $false } ) # Finger Protocol (1 check) - verify outbound firewall rule created by AdvancedSecurity @@ -1911,7 +1911,8 @@ try { else { $actualDesc = if ($portFilter) { "Protocol=$($portFilter.Protocol), RemotePort=$($portFilter.RemotePort)" - } else { + } + else { "No port filter" } } @@ -2050,8 +2051,8 @@ try { $hkuPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings" $userSIDs = Get-ChildItem -Path "HKU:\" -ErrorAction SilentlyContinue | - Where-Object { $_.PSChildName -match '^S-1-5-21-' -and $_.PSChildName -notmatch '_Classes$' } | - Select-Object -ExpandProperty PSChildName + Where-Object { $_.PSChildName -match '^S-1-5-21-' -and $_.PSChildName -notmatch '_Classes$' } | + Select-Object -ExpandProperty PSChildName $hkuCompliant = $true $hkuActualValue = "All users compliant" @@ -2222,10 +2223,10 @@ try { $totalAdapters = $adapters.Count $disabledCount = 0 - $nonCompliant = @() + $nonCompliant = @() foreach ($adapter in $adapters) { - $adapterName = if ($adapter.Description.Length -gt 40) { $adapter.Description.Substring(0,37) + "..." } else { $adapter.Description } + $adapterName = if ($adapter.Description.Length -gt 40) { $adapter.Description.Substring(0, 37) + "..." } else { $adapter.Description } if ($adapter.TcpipNetbiosOptions -eq 2) { $disabledCount++ } @@ -2545,7 +2546,7 @@ catch { # drift from per-category counters in case some success paths didn't # manually increment $results.Verified. $results.TotalSettings = ($results.AllSettings | Measure-Object -Property Total -Sum).Sum -$results.Verified = ($results.AllSettings | Measure-Object -Property Passed -Sum).Sum +$results.Verified = ($results.AllSettings | Measure-Object -Property Passed -Sum).Sum $results.Duration = (Get-Date) - $startTime @@ -3244,7 +3245,7 @@ try {
-

NoID Privacy v2.2.2

+

NoID Privacy v2.2.3

Complete Hardening Compliance Report

All $totalSettings Settings Verified
@@ -3264,7 +3265,7 @@ try {
Framework Version - NoID Privacy v2.2.2 + NoID Privacy v2.2.3
@@ -3423,11 +3424,11 @@ try { $friendlyName = $zoneSettingNames[$settingName] if ($friendlyName) { $zoneName = if ($pathInfo -like "*Zones\\0*") { "My Computer" } - elseif ($pathInfo -like "*Zones\\1*") { "Local Intranet" } - elseif ($pathInfo -like "*Zones\\2*") { "Trusted Sites" } - elseif ($pathInfo -like "*Zones\\3*") { "Internet" } - elseif ($pathInfo -like "*Zones\\4*") { "Restricted Sites" } - else { "Zone" } + elseif ($pathInfo -like "*Zones\\1*") { "Local Intranet" } + elseif ($pathInfo -like "*Zones\\2*") { "Trusted Sites" } + elseif ($pathInfo -like "*Zones\\3*") { "Internet" } + elseif ($pathInfo -like "*Zones\\4*") { "Restricted Sites" } + else { "Zone" } $settingName = "[$zoneName] $friendlyName" } } @@ -3440,14 +3441,14 @@ try { elseif (($settingName -eq "iexplore.exe" -or $settingName -eq "explorer.exe") -and $pathInfo -like "*FeatureControl*") { # IE FeatureControl settings $featureNames = @{ - "FEATURE_DISABLE_MK_PROTOCOL" = "Disable MK Protocol (Security)" - "FEATURE_MIME_HANDLING" = "MIME Handling Security" - "FEATURE_MIME_SNIFFING" = "MIME Sniffing Protection" + "FEATURE_DISABLE_MK_PROTOCOL" = "Disable MK Protocol (Security)" + "FEATURE_MIME_HANDLING" = "MIME Handling Security" + "FEATURE_MIME_SNIFFING" = "MIME Sniffing Protection" "FEATURE_RESTRICT_ACTIVEXINSTALL" = "Restrict ActiveX Install" - "FEATURE_RESTRICT_FILEDOWNLOAD" = "Restrict File Download" - "FEATURE_SECURITYBAND" = "Security Band (Info Bar)" - "FEATURE_WINDOW_RESTRICTIONS" = "Window Restrictions (Pop-up Block)" - "FEATURE_ZONE_ELEVATION" = "Zone Elevation Block" + "FEATURE_RESTRICT_FILEDOWNLOAD" = "Restrict File Download" + "FEATURE_SECURITYBAND" = "Security Band (Info Bar)" + "FEATURE_WINDOW_RESTRICTIONS" = "Window Restrictions (Pop-up Block)" + "FEATURE_ZONE_ELEVATION" = "Zone Elevation Block" } $processName = if ($settingName -eq "iexplore.exe") { "IE" } else { "Explorer" } foreach ($feature in $featureNames.Keys) { @@ -3585,11 +3586,11 @@ try { $friendlyName = $zoneSettingNames[$settingName] if ($friendlyName) { $zoneName = if ($pathInfo -like "*Zones\\0*" -or $pathInfo -like "*Zones\0*") { "My Computer" } - elseif ($pathInfo -like "*Zones\\1*" -or $pathInfo -like "*Zones\1*") { "Local Intranet" } - elseif ($pathInfo -like "*Zones\\2*" -or $pathInfo -like "*Zones\2*") { "Trusted Sites" } - elseif ($pathInfo -like "*Zones\\3*" -or $pathInfo -like "*Zones\3*") { "Internet" } - elseif ($pathInfo -like "*Zones\\4*" -or $pathInfo -like "*Zones\4*") { "Restricted Sites" } - else { "Zone" } + elseif ($pathInfo -like "*Zones\\1*" -or $pathInfo -like "*Zones\1*") { "Local Intranet" } + elseif ($pathInfo -like "*Zones\\2*" -or $pathInfo -like "*Zones\2*") { "Trusted Sites" } + elseif ($pathInfo -like "*Zones\\3*" -or $pathInfo -like "*Zones\3*") { "Internet" } + elseif ($pathInfo -like "*Zones\\4*" -or $pathInfo -like "*Zones\4*") { "Restricted Sites" } + else { "Zone" } $settingName = "[$zoneName] $friendlyName" } } @@ -3602,14 +3603,14 @@ try { elseif (($settingName -eq "iexplore.exe" -or $settingName -eq "explorer.exe") -and $pathInfo -like "*FeatureControl*") { # IE FeatureControl settings $featureNames = @{ - "FEATURE_DISABLE_MK_PROTOCOL" = "Disable MK Protocol (Security)" - "FEATURE_MIME_HANDLING" = "MIME Handling Security" - "FEATURE_MIME_SNIFFING" = "MIME Sniffing Protection" + "FEATURE_DISABLE_MK_PROTOCOL" = "Disable MK Protocol (Security)" + "FEATURE_MIME_HANDLING" = "MIME Handling Security" + "FEATURE_MIME_SNIFFING" = "MIME Sniffing Protection" "FEATURE_RESTRICT_ACTIVEXINSTALL" = "Restrict ActiveX Install" - "FEATURE_RESTRICT_FILEDOWNLOAD" = "Restrict File Download" - "FEATURE_SECURITYBAND" = "Security Band (Info Bar)" - "FEATURE_WINDOW_RESTRICTIONS" = "Window Restrictions (Pop-up Block)" - "FEATURE_ZONE_ELEVATION" = "Zone Elevation Block" + "FEATURE_RESTRICT_FILEDOWNLOAD" = "Restrict File Download" + "FEATURE_SECURITYBAND" = "Security Band (Info Bar)" + "FEATURE_WINDOW_RESTRICTIONS" = "Window Restrictions (Pop-up Block)" + "FEATURE_ZONE_ELEVATION" = "Zone Elevation Block" } $processName = if ($settingName -eq "iexplore.exe") { "IE" } else { "Explorer" } foreach ($feature in $featureNames.Keys) { @@ -3706,7 +3707,7 @@ try { diff --git a/Utils/Compatibility.ps1 b/Utils/Compatibility.ps1 index a355b5b..82689de 100644 --- a/Utils/Compatibility.ps1 +++ b/Utils/Compatibility.ps1 @@ -8,7 +8,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+ #> diff --git a/Utils/Dependencies.ps1 b/Utils/Dependencies.ps1 index 9c79de2..fd1077e 100644 --- a/Utils/Dependencies.ps1 +++ b/Utils/Dependencies.ps1 @@ -7,7 +7,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+ #> @@ -55,9 +55,9 @@ function Test-SecEditAvailable { $result = [PSCustomObject]@{ Available = $false - Path = $null - Version = $null - Error = $null + Path = $null + Version = $null + Error = $null } try { @@ -105,9 +105,9 @@ function Test-AuditPolAvailable { $result = [PSCustomObject]@{ Available = $false - Path = $null - Version = $null - Error = $null + Path = $null + Version = $null + Error = $null } try { @@ -154,10 +154,10 @@ function Test-WindowsDefenderAvailable { param() $result = [PSCustomObject]@{ - Available = $false + Available = $false ServiceRunning = $false - ServiceName = "WinDefend" - Error = $null + ServiceName = "WinDefend" + Error = $null } try { @@ -198,16 +198,16 @@ function Test-AllDependencies { param() $result = [PSCustomObject]@{ - AllAvailable = $true + AllAvailable = $true SecurityBaseline = @{ - secedit = $null + secedit = $null auditpol = $null } - ASR = @{ + ASR = @{ defender = $null } - MissingCritical = @() - MissingOptional = @() + MissingCritical = @() + MissingOptional = @() } # Check secedit.exe (CRITICAL for SecurityBaseline) diff --git a/Utils/Hardware.ps1 b/Utils/Hardware.ps1 index 4bae556..01f11c0 100644 --- a/Utils/Hardware.ps1 +++ b/Utils/Hardware.ps1 @@ -8,7 +8,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+ #> @@ -25,24 +25,24 @@ function Test-VBSCapable { param() $requirements = @{ - UEFI = Test-UEFIBoot - SecureBoot = Test-SecureBootEnabled - TPM = (Test-TPMAvailable).Present + UEFI = Test-UEFIBoot + SecureBoot = Test-SecureBootEnabled + TPM = (Test-TPMAvailable).Present Virtualization = Test-VirtualizationEnabled - Windows11 = (Get-WindowsVersion).IsWindows11 + Windows11 = (Get-WindowsVersion).IsWindows11 } $allMet = $requirements.UEFI -and $requirements.SecureBoot -and ` - $requirements.TPM -and $requirements.Virtualization -and ` - $requirements.Windows11 + $requirements.TPM -and $requirements.Virtualization -and ` + $requirements.Windows11 return [PSCustomObject]@{ - Capable = $allMet - UEFI = $requirements.UEFI - SecureBoot = $requirements.SecureBoot - TPM = $requirements.TPM + Capable = $allMet + UEFI = $requirements.UEFI + SecureBoot = $requirements.SecureBoot + TPM = $requirements.TPM Virtualization = $requirements.Virtualization - Windows11 = $requirements.Windows11 + Windows11 = $requirements.Windows11 } } @@ -90,13 +90,13 @@ function Get-CPUInfo { $cpu = Get-CimInstance -ClassName Win32_Processor -ErrorAction Stop | Select-Object -First 1 return [PSCustomObject]@{ - Name = $cpu.Name - Manufacturer = $cpu.Manufacturer - Cores = $cpu.NumberOfCores - LogicalProcessors = $cpu.NumberOfLogicalProcessors - MaxClockSpeed = $cpu.MaxClockSpeed + Name = $cpu.Name + Manufacturer = $cpu.Manufacturer + Cores = $cpu.NumberOfCores + LogicalProcessors = $cpu.NumberOfLogicalProcessors + MaxClockSpeed = $cpu.MaxClockSpeed VirtualizationEnabled = $cpu.VirtualizationFirmwareEnabled - Architecture = $cpu.Architecture + Architecture = $cpu.Architecture } } catch { @@ -123,9 +123,9 @@ function Get-MemoryInfo { return [PSCustomObject]@{ TotalPhysicalMemoryGB = [math]::Round($cs.TotalPhysicalMemory / 1GB, 2) - FreePhysicalMemoryGB = [math]::Round($os.FreePhysicalMemory / 1MB / 1024, 2) - TotalVirtualMemoryGB = [math]::Round($os.TotalVirtualMemorySize / 1MB / 1024, 2) - FreeVirtualMemoryGB = [math]::Round($os.FreeVirtualMemory / 1MB / 1024, 2) + FreePhysicalMemoryGB = [math]::Round($os.FreePhysicalMemory / 1MB / 1024, 2) + TotalVirtualMemoryGB = [math]::Round($os.TotalVirtualMemorySize / 1MB / 1024, 2) + FreeVirtualMemoryGB = [math]::Round($os.FreeVirtualMemory / 1MB / 1024, 2) } } catch { @@ -192,16 +192,16 @@ function Get-WindowsEditionInfo { $supportsBitLocker = -not $isHome return [PSCustomObject]@{ - Caption = $os.Caption - Version = $os.Version - BuildNumber = $os.BuildNumber - IsHome = $isHome - IsPro = $isPro - IsEnterprise = $isEnterprise - IsEducation = $isEducation + Caption = $os.Caption + Version = $os.Version + BuildNumber = $os.BuildNumber + IsHome = $isHome + IsPro = $isPro + IsEnterprise = $isEnterprise + IsEducation = $isEducation SupportsCredentialGuard = $supportsCredentialGuard - SupportsAppLocker = $supportsAppLocker - SupportsBitLocker = $supportsBitLocker + SupportsAppLocker = $supportsAppLocker + SupportsBitLocker = $supportsBitLocker } } catch { @@ -223,16 +223,16 @@ function Get-HardwareReport { param() return [PSCustomObject]@{ - OS = Get-WindowsVersion - Edition = Get-WindowsEditionInfo - CPU = Get-CPUInfo - Memory = Get-MemoryInfo - UEFI = Test-UEFIBoot - SecureBoot = Test-SecureBootEnabled - TPM = Test-TPMAvailable + OS = Get-WindowsVersion + Edition = Get-WindowsEditionInfo + CPU = Get-CPUInfo + Memory = Get-MemoryInfo + UEFI = Test-UEFIBoot + SecureBoot = Test-SecureBootEnabled + TPM = Test-TPMAvailable Virtualization = Test-VirtualizationEnabled - VBSCapable = Test-VBSCapable - SSD = Test-SSDDrive + VBSCapable = Test-VBSCapable + SSD = Test-SSDDrive } } diff --git a/Utils/Registry.ps1 b/Utils/Registry.ps1 index 1d80a70..b9da902 100644 --- a/Utils/Registry.ps1 +++ b/Utils/Registry.ps1 @@ -8,7 +8,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+ #> diff --git a/Utils/Service.ps1 b/Utils/Service.ps1 index 898c4d2..b225000 100644 --- a/Utils/Service.ps1 +++ b/Utils/Service.ps1 @@ -8,7 +8,7 @@ .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: PowerShell 5.1+ #> @@ -213,12 +213,12 @@ function Get-ServiceStatus { $serviceWmi = Get-CimInstance -ClassName Win32_Service -Filter "Name='$ServiceName'" -ErrorAction Stop return [PSCustomObject]@{ - Name = $service.Name + Name = $service.Name DisplayName = $service.DisplayName - Status = $service.Status - StartType = $service.StartType - StartMode = $serviceWmi.StartMode - PathName = $serviceWmi.PathName + Status = $service.Status + StartType = $service.StartType + StartMode = $serviceWmi.StartMode + PathName = $serviceWmi.PathName Description = $serviceWmi.Description } } diff --git a/config.json b/config.json index 34183c1..5c42d3d 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,5 @@ { - "version": "2.2.2", + "version": "2.2.3", "modules": { "SecurityBaseline": { "enabled": true, @@ -48,7 +48,7 @@ "description": "Microsoft Edge v139 Security Baseline: 24 security policies", "_comment": "Interactive: Allow extensions (Y/N, default: Y)", "allowExtensions": true, - "version": "2.2.2", + "version": "2.2.3", "baseline": "Edge v139", "policies": 24, "features": { @@ -75,7 +75,7 @@ "disableWirelessDisplay": false, "disableDiscoveryProtocols": true, "disableIPv6": false, - "version": "2.2.2", + "version": "2.2.3", "policies": 50, "features": { "rdp_hardening": true, @@ -94,7 +94,11 @@ "firewall_shields_up": true, "ipv6_disable": true }, - "profiles": ["Balanced", "Enterprise", "Maximum"] + "profiles": [ + "Balanced", + "Enterprise", + "Maximum" + ] } }, "options": { @@ -106,4 +110,4 @@ "autoConfirm": false, "_comment": "nonInteractive=true: Skip all Read-Host prompts, use config values instead" } -} +} \ No newline at end of file From 1d90ff59fcce401e5aea28f8cae09bc3da2dfdbf Mon Sep 17 00:00:00 2001 From: NexusOne23 Date: Wed, 7 Jan 2026 18:54:57 +0100 Subject: [PATCH 09/15] chore: Update framework architecture asset for v2.2.3 --- assets/framework-architecture.png | Bin 46595 -> 47147 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/framework-architecture.png b/assets/framework-architecture.png index b875faf307e80fd687f69235085617cff02e1874..638ae1a9cf37c416deaeb938f570de361027e574 100644 GIT binary patch literal 47147 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 literal 46595 zcmdSB2{c>n+c&Dy)2CWI9Z(b_k9h&>$m|`dj%0Zi#ju((`?Y@EU1E_2uPJVjv~-hk(-A0@%UjLZRsr7 zjaL71B-1)>_dVNb&ZC@;eCPQ>v@eAoH5|nAdR00�*L%y}uQpJkaVqx+|GJn4XgK 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 Date: Fri, 13 Feb 2026 16:59:37 +0100 Subject: [PATCH 10/15] docs: add sister project link to NoID Privacy for Linux --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 63a9fb5..f4a7c06 100644 --- a/README.md +++ b/README.md @@ -787,6 +787,18 @@ Logs/NoIDPrivacy_YYYYMMDD_HHMMSS.log --- +## 🔗 Sister Project: NoID Privacy for Linux + +**[NoID Privacy for Linux](https://github.com/NexusOne23/noid-privacy-linux)** — Privacy & Security Audit for Linux Desktops + +- 300+ checks across 42 sections — pure Bash, zero dependencies +- Browser privacy, app telemetry, VPN kill-switch, webcam/bluetooth audits +- **`--ai` flag** generates a ready-to-paste prompt for AI-powered remediation + +**Two operating systems. One mission: Your privacy.** + +--- + ## 📜 License ### Dual-License Model From e1320c43404a547c3f851a4157cd1c9eb9553abe Mon Sep 17 00:00:00 2001 From: NexusOne23 Date: Fri, 13 Feb 2026 17:03:35 +0100 Subject: [PATCH 11/15] =?UTF-8?q?=F0=9F=93=9D=20Fix=20broken=20Troubleshoo?= =?UTF-8?q?ting=20anchor=20link=20in=20README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #troubleshooting → #-troubleshooting (GitHub strips emoji to dash prefix) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f4a7c06..c83f442 100644 --- a/README.md +++ b/README.md @@ -769,7 +769,7 @@ Logs/NoIDPrivacy_YYYYMMDD_HHMMSS.log - **[Features](Docs/FEATURES.md)** - Complete 630+ setting reference - **[Changelog](CHANGELOG.md)** - Version history - **[Quick Start](#-quick-start)** - Installation guide (see above) -- **[Troubleshooting](#troubleshooting)** - Common issues (see above) +- **[Troubleshooting](#-troubleshooting)** - Common issues (see above) ### 💬 Community From 902d63f83760a609c6e37576b5cd42201271591f Mon Sep 17 00:00:00 2001 From: NexusOne23 Date: Fri, 13 Feb 2026 17:09:18 +0100 Subject: [PATCH 12/15] docs: add NoID Privacy ecosystem table with Android app --- README.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index c83f442..84537c6 100644 --- a/README.md +++ b/README.md @@ -787,15 +787,13 @@ Logs/NoIDPrivacy_YYYYMMDD_HHMMSS.log --- -## 🔗 Sister Project: NoID Privacy for Linux +## 🔗 The NoID Privacy Ecosystem -**[NoID Privacy for Linux](https://github.com/NexusOne23/noid-privacy-linux)** — Privacy & Security Audit for Linux Desktops - -- 300+ checks across 42 sections — pure Bash, zero dependencies -- Browser privacy, app telemetry, VPN kill-switch, webcam/bluetooth audits -- **`--ai` flag** generates a ready-to-paste prompt for AI-powered remediation - -**Two operating systems. One mission: Your privacy.** +| Platform | Link | +|----------|------| +| 🪟 **Windows** | You're here! | +| 🐧 **Linux** | [NoID Privacy for Linux](https://github.com/NexusOne23/noid-privacy-linux) — 300+ checks, 42 sections, `--ai` flag for AI-powered fixes | +| 📱 **Android** | [NoID Privacy on Google Play](https://play.google.com/store/apps/details?id=com.noid.privacy) | --- From 86d76cedc9eae57bee7d60da57c7b80e15ccb8c1 Mon Sep 17 00:00:00 2001 From: NexusOne23 Date: Fri, 13 Feb 2026 17:13:18 +0100 Subject: [PATCH 13/15] docs: add Android app description to ecosystem table --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 84537c6..d1d3dc2 100644 --- a/README.md +++ b/README.md @@ -793,7 +793,7 @@ Logs/NoIDPrivacy_YYYYMMDD_HHMMSS.log |----------|------| | 🪟 **Windows** | You're here! | | 🐧 **Linux** | [NoID Privacy for Linux](https://github.com/NexusOne23/noid-privacy-linux) — 300+ checks, 42 sections, `--ai` flag for AI-powered fixes | -| 📱 **Android** | [NoID Privacy on Google Play](https://play.google.com/store/apps/details?id=com.noid.privacy) | +| 📱 **Android** | [NoID Privacy on Google Play](https://play.google.com/store/apps/details?id=com.noid.privacy) — 81 checks, 10 categories, permission audit, Chrome hardening, anti-theft | --- From 3bae0057eac3741d98d6e8b53eff183ed31cc117 Mon Sep 17 00:00:00 2001 From: NexusOne23 Date: Fri, 27 Feb 2026 11:29:42 +0100 Subject: [PATCH 14/15] =?UTF-8?q?fix:=20SetMaximumStorageSpaceForRecallSna?= =?UTF-8?q?pshots=20value=2010=20=E2=86=92=2010240=20(MB)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Microsoft CSP stores Recall snapshot storage in MB, not GB. 10 GB = 10240 MB. Fixes apply, verify, and compliance check. Closes #14 Co-Authored-By: Claude Opus 4.6 --- Docs/FEATURES.md | 2 +- Modules/AntiAI/Config/AntiAI-Settings.json | 4 ++-- Modules/AntiAI/Private/Set-RecallProtection.ps1 | 10 +++++----- Modules/AntiAI/Private/Test-AntiAICompliance.ps1 | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Docs/FEATURES.md b/Docs/FEATURES.md index 4065224..7f56abf 100644 --- a/Docs/FEATURES.md +++ b/Docs/FEATURES.md @@ -283,7 +283,7 @@ HKCU:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI\DisableRecallDataProviders = HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI\SetDenyAppListForRecall = [...] HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI\SetDenyUriListForRecall = [...] HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI\SetMaximumStorageDurationForRecallSnapshots = 30 -HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI\SetMaximumStorageSpaceForRecallSnapshots = 10 +HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI\SetMaximumStorageSpaceForRecallSnapshots = 10240 HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI\TurnOffWindowsCopilot = 1 HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsCopilot\TurnOffWindowsCopilot = 1 HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsCopilot\ShowCopilotButton = 0 diff --git a/Modules/AntiAI/Config/AntiAI-Settings.json b/Modules/AntiAI/Config/AntiAI-Settings.json index ebb1293..a8a9266 100644 --- a/Modules/AntiAI/Config/AntiAI-Settings.json +++ b/Modules/AntiAI/Config/AntiAI-Settings.json @@ -100,8 +100,8 @@ }, "SetMaximumStorageSpaceForRecallSnapshots": { "Type": "DWord", - "Value": 10, - "Description": "Maximum snapshot storage: 10 GB (Choices: 10/25/50/75/100/150 GB, 0=OS default)" + "Value": 10240, + "Description": "Maximum snapshot storage: 10 GB = 10240 MB (Choices: 10240/25600/51200/76800/102400/153600 MB, 0=OS default)" } } } diff --git a/Modules/AntiAI/Private/Set-RecallProtection.ps1 b/Modules/AntiAI/Private/Set-RecallProtection.ps1 index cd5ab56..292ebfd 100644 --- a/Modules/AntiAI/Private/Set-RecallProtection.ps1 +++ b/Modules/AntiAI/Private/Set-RecallProtection.ps1 @@ -95,14 +95,14 @@ function Set-RecallProtection { Write-Log -Level DEBUG -Message "Set max snapshot retention: 30 days" -Module "AntiAI" $result.Applied++ - # 4. Storage Space Limit - Max 10 GB + # 4. Storage Space Limit - Max 10 GB (10240 MB per MS CSP) $existing = Get-ItemProperty -Path $regPath -Name "SetMaximumStorageSpaceForRecallSnapshots" -ErrorAction SilentlyContinue if ($null -ne $existing) { - Set-ItemProperty -Path $regPath -Name "SetMaximumStorageSpaceForRecallSnapshots" -Value 10 -Force + Set-ItemProperty -Path $regPath -Name "SetMaximumStorageSpaceForRecallSnapshots" -Value 10240 -Force } else { - New-ItemProperty -Path $regPath -Name "SetMaximumStorageSpaceForRecallSnapshots" -Value 10 -PropertyType DWord -Force | Out-Null + New-ItemProperty -Path $regPath -Name "SetMaximumStorageSpaceForRecallSnapshots" -Value 10240 -PropertyType DWord -Force | Out-Null } - Write-Log -Level DEBUG -Message "Set max snapshot storage: 10 GB" -Module "AntiAI" + Write-Log -Level DEBUG -Message "Set max snapshot storage: 10 GB (10240 MB)" -Module "AntiAI" $result.Applied++ # Verify @@ -111,7 +111,7 @@ function Set-RecallProtection { $verified = ($null -ne $values.SetDenyAppListForRecall) -and ($null -ne $values.SetDenyUriListForRecall) -and ($values.SetMaximumStorageDurationForRecallSnapshots -eq 30) -and - ($values.SetMaximumStorageSpaceForRecallSnapshots -eq 10) + ($values.SetMaximumStorageSpaceForRecallSnapshots -eq 10240) if ($verified) { Write-Log -Level DEBUG -Message "Verification SUCCESS: All Recall protection policies applied" -Module "AntiAI" diff --git a/Modules/AntiAI/Private/Test-AntiAICompliance.ps1 b/Modules/AntiAI/Private/Test-AntiAICompliance.ps1 index abecfaa..794c838 100644 --- a/Modules/AntiAI/Private/Test-AntiAICompliance.ps1 +++ b/Modules/AntiAI/Private/Test-AntiAICompliance.ps1 @@ -235,7 +235,7 @@ $protectionChecks = @( (Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" -Name "SetDenyAppListForRecall" -ExpectedValue $expectedDenyApps -Description "App Deny List"), (Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" -Name "SetDenyUriListForRecall" -ExpectedValue $expectedDenyUris -Description "URI Deny List"), (Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" -Name "SetMaximumStorageDurationForRecallSnapshots" -ExpectedValue 30 -Description "Max Retention: 30 days"), - (Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" -Name "SetMaximumStorageSpaceForRecallSnapshots" -ExpectedValue 10 -Description "Max Storage: 10 GB") + (Test-RegistryValue -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" -Name "SetMaximumStorageSpaceForRecallSnapshots" -ExpectedValue 10240 -Description "Max Storage: 10 GB (10240 MB)") ) foreach ($check in $protectionChecks) { $results.Details += $check From 815a7e39d0abef00d7bc1714042ffcba3be59bf3 Mon Sep 17 00:00:00 2001 From: NexusOne23 Date: Thu, 5 Mar 2026 08:37:07 +0100 Subject: [PATCH 15/15] =?UTF-8?q?chore:=20complete=20version=20alignment?= =?UTF-8?q?=202.2.2=20=E2=86=92=202.2.3=20across=20all=20module=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps remaining 13 files that still referenced v2.2.2: - 3 AdvancedSecurity config JSONs (AdminShares, Credentials, RDP) - 4 AdvancedSecurity PS1 headers (Block-FingerProtocol, Set-SRPRules, Set-WindowsUpdate, Invoke-AdvancedSecurity) - 2 AntiAI PS1 headers (Disable-CopilotAdvanced, Test-AntiAICompliance) - 1 Privacy runtime output (Invoke-PrivacyHardening) - CONTRIBUTING.md templates + FEATURES.md docs - bug_report.md issue template Historical annotations in Backup-PrivacySettings.ps1 ("added in v2.2.2") intentionally preserved as they document feature introduction dates. Co-Authored-By: Claude Opus 4.6 --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- CONTRIBUTING.md | 12 +++++----- Docs/FEATURES.md | 22 +++++++++---------- .../AdvancedSecurity/Config/AdminShares.json | 2 +- .../AdvancedSecurity/Config/Credentials.json | 2 +- Modules/AdvancedSecurity/Config/RDP.json | 2 +- .../Private/Block-FingerProtocol.ps1 | 2 +- .../AdvancedSecurity/Private/Set-SRPRules.ps1 | 2 +- .../Private/Set-WindowsUpdate.ps1 | 2 +- .../Public/Invoke-AdvancedSecurity.ps1 | 2 +- .../Private/Disable-CopilotAdvanced.ps1 | 2 +- .../AntiAI/Private/Test-AntiAICompliance.ps1 | 2 +- .../Public/Invoke-PrivacyHardening.ps1 | 2 +- 13 files changed, 28 insertions(+), 28 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index f5e8f7f..00cd2aa 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -32,7 +32,7 @@ A clear description of what actually happened. - **CPU**: [e.g., AMD Ryzen 7 9800X3D] - **TPM**: [e.g., 2.0 Present] - **Third-Party AV**: [e.g., None, Windows Defender only] -- **Script Version**: [e.g., v2.2.2] +- **Script Version**: [e.g., v2.2.3] - **Execution Mode**: [Interactive / Direct / DryRun] **Get System Info:** diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 37349e1..1d70898 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -68,7 +68,7 @@ Modules/ ``` Modules/AdvancedSecurity/ -├── AdvancedSecurity.psd1 # Manifest with version 2.2.2 +├── AdvancedSecurity.psd1 # Manifest with version 2.2.3 ├── AdvancedSecurity.psm1 # Loads Private/*.ps1 and Public/*.ps1 ├── Config/ │ ├── RDP.json # RDP hardening config @@ -105,7 +105,7 @@ Modules/AdvancedSecurity/ ```powershell @{ RootModule = 'YourModule.psm1' - ModuleVersion = '2.2.2' + ModuleVersion = '2.2.3' GUID = 'YOUR-GUID-HERE' # Generate with [guid]::NewGuid() Author = 'Your Name' CompanyName = 'NoID Privacy' @@ -128,7 +128,7 @@ Modules/AdvancedSecurity/ Tags = @('Security', 'Hardening', 'Windows11') ProjectUri = 'https://github.com/yourusername/noid-privacy' ReleaseNotes = @" -v2.2.2 - Initial Release +v2.2.3 - Initial Release - Feature 1 - Feature 2 "@ @@ -141,7 +141,7 @@ v2.2.2 - Initial Release ```powershell @{ RootModule = 'AdvancedSecurity.psm1' - ModuleVersion = '2.2.2' + ModuleVersion = '2.2.3' GUID = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890' Author = 'NexusOne23' Description = 'Advanced Security hardening beyond Microsoft Security Baseline' @@ -155,7 +155,7 @@ v2.2.2 - Initial Release PSData = @{ Tags = @('Security', 'Hardening', 'RDP', 'TLS', 'Windows11') ReleaseNotes = @" -v2.2.2 - Production Release +v2.2.3 - Production Release - RDP NLA enforcement + optional complete disable - WDigest credential protection - Administrative shares disable (domain-aware) @@ -781,4 +781,4 @@ mkdir "Modules\YourModule\Config" --- -**Questions? Study AdvancedSecurity v2.2.2 - it's the reference implementation!** 🎯 +**Questions? Study AdvancedSecurity v2.2.3 - it's the reference implementation!** 🎯 diff --git a/Docs/FEATURES.md b/Docs/FEATURES.md index 7f56abf..3540755 100644 --- a/Docs/FEATURES.md +++ b/Docs/FEATURES.md @@ -1,6 +1,6 @@ # NoID Privacy - Complete Feature List -**Framework Version:** v2.2.2 +**Framework Version:** v2.2.3 **Total Security Settings:** 633 (Paranoid mode) **Modules:** 7 (All Production-Ready) **Last Updated:** December 22, 2025 @@ -11,13 +11,13 @@ | Module | Settings | Status | Description | |--------|----------|--------|-------------| -| **SecurityBaseline** | 425 | ✅ v2.2.2 | Microsoft Security Baseline for Windows 11 v25H2 | -| **ASR** | 19 | ✅ v2.2.2 | Attack Surface Reduction rules | -| **DNS** | 5 | ✅ v2.2.2 | Secure DNS with DoH encryption | -| **Privacy** | 78 | ✅ v2.2.2 | Telemetry control, OneDrive hardening (Strict: 70 Registry + 2 Services + 6 OneDrive) | -| **AntiAI** | 32 | ✅ v2.2.2 | AI lockdown (15 features, 32 compliance checks) | -| **EdgeHardening** | 24 | ✅ v2.2.2 | Microsoft Edge browser security (24 policies) | -| **AdvancedSecurity** | 50 | ✅ v2.2.2 | Advanced hardening beyond MS Baseline (incl. Wireless Display, Discovery Protocols, IPv6) | +| **SecurityBaseline** | 425 | ✅ v2.2.3 | Microsoft Security Baseline for Windows 11 v25H2 | +| **ASR** | 19 | ✅ v2.2.3 | Attack Surface Reduction rules | +| **DNS** | 5 | ✅ v2.2.3 | Secure DNS with DoH encryption | +| **Privacy** | 78 | ✅ v2.2.3 | Telemetry control, OneDrive hardening (Strict: 70 Registry + 2 Services + 6 OneDrive) | +| **AntiAI** | 32 | ✅ v2.2.3 | AI lockdown (15 features, 32 compliance checks) | +| **EdgeHardening** | 24 | ✅ v2.2.3 | Microsoft Edge browser security (24 policies) | +| **AdvancedSecurity** | 50 | ✅ v2.2.3 | Advanced hardening beyond MS Baseline (incl. Wireless Display, Discovery Protocols, IPv6) | | **TOTAL** | **633** | ✅ **100%** | **Complete Framework (Paranoid mode)** | --- @@ -238,7 +238,7 @@ Clipchamp.Clipchamp, SpotifyAB.SpotifyMusic ## 🤖 Module 5: AntiAI (32 Policies) -**Description:** Disable 15 Windows AI features via 32 registry policies (v2.2.2) +**Description:** Disable 15 Windows AI features via 32 registry policies (v2.2.3) ### 15 AI Features Disabled: @@ -724,7 +724,7 @@ Some UI elements in Paint and Photos apps may **still be visible** but non-funct ``` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -NoID Privacy v2.2.2 +NoID Privacy v2.2.3 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Total Settings: 633 ✅ @@ -745,4 +745,4 @@ Framework Completion: 🎉 100% COMPLETE --- **Last Updated:** December 22, 2025 -**Framework Version:** v2.2.2 +**Framework Version:** v2.2.3 diff --git a/Modules/AdvancedSecurity/Config/AdminShares.json b/Modules/AdvancedSecurity/Config/AdminShares.json index eb0da25..e804e8f 100644 --- a/Modules/AdvancedSecurity/Config/AdminShares.json +++ b/Modules/AdvancedSecurity/Config/AdminShares.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Administrative Shares Configuration", "description": "Configuration for disabling administrative shares (C$, ADMIN$, etc.) to prevent lateral movement", - "version": "2.2.2", + "version": "2.2.3", "Administrative_Shares": { "description": "Disable automatic creation and remove existing administrative shares", diff --git a/Modules/AdvancedSecurity/Config/Credentials.json b/Modules/AdvancedSecurity/Config/Credentials.json index 20b8bda..391b215 100644 --- a/Modules/AdvancedSecurity/Config/Credentials.json +++ b/Modules/AdvancedSecurity/Config/Credentials.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Credential Protection Configuration", "description": "Configuration for credential hardening including WDigest protection", - "version": "2.2.2", + "version": "2.2.3", "WDigest_Protection": { "description": "Prevent WDigest from storing plaintext passwords in LSASS memory", diff --git a/Modules/AdvancedSecurity/Config/RDP.json b/Modules/AdvancedSecurity/Config/RDP.json index 8fc81fe..27e224c 100644 --- a/Modules/AdvancedSecurity/Config/RDP.json +++ b/Modules/AdvancedSecurity/Config/RDP.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "title": "RDP Hardening Configuration", "description": "Configuration for RDP (Remote Desktop Protocol) hardening including NLA enforcement and optional complete disable", - "version": "2.2.2", + "version": "2.2.3", "NLA_Enforcement": { "description": "Network Level Authentication (NLA) enforcement settings", diff --git a/Modules/AdvancedSecurity/Private/Block-FingerProtocol.ps1 b/Modules/AdvancedSecurity/Private/Block-FingerProtocol.ps1 index 77fb865..7e1080f 100644 --- a/Modules/AdvancedSecurity/Private/Block-FingerProtocol.ps1 +++ b/Modules/AdvancedSecurity/Private/Block-FingerProtocol.ps1 @@ -21,7 +21,7 @@ function Block-FingerProtocol { .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: Administrator privileges REFERENCES: diff --git a/Modules/AdvancedSecurity/Private/Set-SRPRules.ps1 b/Modules/AdvancedSecurity/Private/Set-SRPRules.ps1 index 665b771..89446e2 100644 --- a/Modules/AdvancedSecurity/Private/Set-SRPRules.ps1 +++ b/Modules/AdvancedSecurity/Private/Set-SRPRules.ps1 @@ -27,7 +27,7 @@ function Set-SRPRules { .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: Administrator privileges REFERENCES: diff --git a/Modules/AdvancedSecurity/Private/Set-WindowsUpdate.ps1 b/Modules/AdvancedSecurity/Private/Set-WindowsUpdate.ps1 index 7891e1e..7940d6f 100644 --- a/Modules/AdvancedSecurity/Private/Set-WindowsUpdate.ps1 +++ b/Modules/AdvancedSecurity/Private/Set-WindowsUpdate.ps1 @@ -22,7 +22,7 @@ function Set-WindowsUpdate { .NOTES Author: NexusOne23 - Version: 2.2.2 + Version: 2.2.3 Requires: Administrator privileges Based on: Windows Settings > Windows Update > Advanced options #> diff --git a/Modules/AdvancedSecurity/Public/Invoke-AdvancedSecurity.ps1 b/Modules/AdvancedSecurity/Public/Invoke-AdvancedSecurity.ps1 index e534b4a..ec27d65 100644 --- a/Modules/AdvancedSecurity/Public/Invoke-AdvancedSecurity.ps1 +++ b/Modules/AdvancedSecurity/Public/Invoke-AdvancedSecurity.ps1 @@ -11,7 +11,7 @@ function Invoke-AdvancedSecurity { - Enterprise: Conservative approach with domain-safety checks - Maximum: Maximum hardening for air-gapped/high-security environments - Features implemented (v2.2.2): + Features implemented (v2.2.3): - RDP NLA enforcement + optional complete disable - WDigest credential protection - Administrative shares disable (domain-aware) diff --git a/Modules/AntiAI/Private/Disable-CopilotAdvanced.ps1 b/Modules/AntiAI/Private/Disable-CopilotAdvanced.ps1 index bc77e65..6c08840 100644 --- a/Modules/AntiAI/Private/Disable-CopilotAdvanced.ps1 +++ b/Modules/AntiAI/Private/Disable-CopilotAdvanced.ps1 @@ -40,7 +40,7 @@ .NOTES Requires Administrator privileges. - Part of NoID Privacy AntiAI Module v2.2.2 + Part of NoID Privacy AntiAI Module v2.2.3 #> function Disable-CopilotAdvanced { [CmdletBinding()] diff --git a/Modules/AntiAI/Private/Test-AntiAICompliance.ps1 b/Modules/AntiAI/Private/Test-AntiAICompliance.ps1 index 794c838..936de75 100644 --- a/Modules/AntiAI/Private/Test-AntiAICompliance.ps1 +++ b/Modules/AntiAI/Private/Test-AntiAICompliance.ps1 @@ -42,7 +42,7 @@ .NOTES Author: NoID Privacy - Version: 2.2.2 (Extended validation) + Version: 2.2.3 (Extended validation) Requires: Windows 11 24H2+, Administrator privileges #> diff --git a/Modules/Privacy/Public/Invoke-PrivacyHardening.ps1 b/Modules/Privacy/Public/Invoke-PrivacyHardening.ps1 index b56e655..a082c68 100644 --- a/Modules/Privacy/Public/Invoke-PrivacyHardening.ps1 +++ b/Modules/Privacy/Public/Invoke-PrivacyHardening.ps1 @@ -354,7 +354,7 @@ function Invoke-PrivacyHardening { $bloatwareListPath = Join-Path $moduleBackupPath "REMOVED_APPS_LIST.txt" $listContent = @() $listContent += "================================================================" - $listContent += " REMOVED APPS - NoID Privacy v2.2.2" + $listContent += " REMOVED APPS - NoID Privacy v2.2.3" $listContent += " Session: $(Split-Path $moduleBackupPath -Leaf)" $listContent += " Date: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" $listContent += "================================================================"