diff --git a/.github/workflows/env-matrix.yml b/.github/workflows/env-matrix.yml new file mode 100644 index 0000000..bd8d386 --- /dev/null +++ b/.github/workflows/env-matrix.yml @@ -0,0 +1,52 @@ +name: Environment Compatibility Matrix + +on: + push: + branches: [ "🌕Nextgen", "📦Current" ] + pull_request: + branches: [ "🌕Nextgen", "📦Current" ] + +jobs: + matrix-test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [windows-2019, windows-2022] + shell: [pwsh, powershell] + admin: [true, false] + locale: [en-US, de-DE] + name: Env: ${{ matrix.os }} | Shell: ${{ matrix.shell }} | Admin: ${{ matrix.admin }} | Locale: ${{ matrix.locale }} + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set locale + run: | + Set-WinSystemLocale ${{ matrix.locale }} + shell: powershell + continue-on-error: true + + - name: Run as admin (if required) + if: ${{ matrix.admin == 'true' }} + run: | + Start-Process -FilePath ${{ matrix.shell }} -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File windows-telemetry-blocker.ps1 -dryrun' -Verb RunAs + shell: powershell + continue-on-error: true + + - name: Run as non-admin (if required) + if: ${{ matrix.admin == 'false' }} + run: | + ${{ matrix.shell }} -NoProfile -ExecutionPolicy Bypass -File windows-telemetry-blocker.ps1 -dryrun + shell: powershell + continue-on-error: true + + - name: Upload logs and report + uses: actions/upload-artifact@v4 + with: + name: logs-${{ matrix.os }}-${{ matrix.shell }}-${{ matrix.admin }}-${{ matrix.locale }} + path: | + telemetry-blocker.log + telemetry-blocker-errors.log + telemetry-blocker-report.md + if-no-files-found: ignore \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..2c41efd --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,27 @@ +# Security Policy + +## Supported Versions +- Only the latest release is actively supported for security updates. +- Older versions may not receive security patches. + +## Reporting a Vulnerability +If you discover a security vulnerability, please do **not** open a public issue. Instead, report it privately: + +- Email: security@n0thorizon.dev (or use GitHub's private vulnerability reporting) +- Include a detailed description, steps to reproduce, and any relevant logs or screenshots. +- We will respond as quickly as possible and coordinate a fix. + +## Security Best Practices +- Always download releases from the official repository. +- Review scripts before running, especially if you modify them. +- Run the script in a test environment before deploying to production systems. +- Keep your system and PowerShell up to date. + +## Disclosure Policy +- We follow responsible disclosure. Vulnerabilities will be fixed promptly and disclosed after a patch is released. + +## Hall of Fame +- Security researchers who responsibly disclose vulnerabilities may be credited here (with permission). + +--- +For any other security concerns, contact the maintainer directly. diff --git a/modules/common.ps1 b/modules/common.ps1 index 3bc5c2a..cf7f8a0 100644 --- a/modules/common.ps1 +++ b/modules/common.ps1 @@ -1,18 +1,50 @@ # Common functions for all modules + +# Enhanced Write-ModuleLog: robust fallback, cross-env function Write-ModuleLog { param([string]$msg) - if (Get-Command Write-Log -ErrorAction SilentlyContinue) { - Write-Log $msg + try { + if (Get-Command Write-Log -ErrorAction SilentlyContinue) { + Write-Log $msg + } else { + $logFile = Join-Path $PSScriptRoot '..' 'telemetry-blocker.log' + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + "$timestamp $msg" | Out-File -FilePath $logFile -Append -Encoding utf8 + } + } catch { + Write-Host "[LOG ERROR] $msg" -ForegroundColor Red } } +# Enhanced Set-RegistryValue: supports more types, error handling, cross-env function Set-RegistryValue { - param($Path, $Name, $Value, $Type = "DWord") + param( + [Parameter(Mandatory)]$Path, + [Parameter(Mandatory)]$Name, + [Parameter(Mandatory)]$Value, + [ValidateSet("DWord","QWord","String","ExpandString","Binary","MultiString")] + [string]$Type = "DWord" + ) if ($global:dryrun) { Write-Host "[DRY-RUN] Would set $Path\$Name = $Value ($Type)" -ForegroundColor DarkYellow - Write-ModuleLog "[DRY-RUN] Would set $Path\$Name = $Value ($Type)" + Write-ModuleLog "[DRY-RUN] Would set $($Path)\$($Name) = $Value ($Type)" } else { - Set-ItemProperty -Path $Path -Name $Name -Value $Value -Type $Type - Write-ModuleLog "Set $Path\$Name = $Value ($Type)" + try { + if ($Type -eq "DWord" -or $Type -eq "QWord") { + Set-ItemProperty -Path $Path -Name $Name -Value ([Convert]::ToInt64($Value)) -Type $Type + } elseif ($Type -eq "String" -or $Type -eq "ExpandString") { + Set-ItemProperty -Path $Path -Name $Name -Value "$Value" -Type $Type + } elseif ($Type -eq "Binary") { + Set-ItemProperty -Path $Path -Name $Name -Value ([byte[]]$Value) -Type $Type + } elseif ($Type -eq "MultiString") { + Set-ItemProperty -Path $Path -Name $Name -Value ([string[]]$Value) -Type $Type + } else { + Set-ItemProperty -Path $Path -Name $Name -Value $Value -Type $Type + } + Write-ModuleLog "Set $($Path)\$($Name) = $Value ($Type)" + } catch { + Write-Host "[ERROR] Failed to set $($Path)\$($Name): $_" -ForegroundColor Red + Write-ModuleLog "[ERROR] Failed to set $($Path)\$($Name): $_" + } } } diff --git a/run.bat b/run.bat index 5d79ea9..a1fb2a6 100644 --- a/run.bat +++ b/run.bat @@ -1,10 +1,94 @@ @echo off +:: Windows Telemetry Blocker Launcher (Critical Features Enhanced) +:: Ensures admin, supports spaces in paths, and works from any directory + +setlocal +set "SCRIPT_DIR=%~dp0" +set "PS_SCRIPT=%SCRIPT_DIR%windows-telemetry-blocker.ps1" + +:: --- Critical: Check for script existence --- +if not exist "%PS_SCRIPT%" ( + echo [FATAL] Main PowerShell script not found: %PS_SCRIPT% + echo Please ensure all files are extracted and try again. + pause + exit /b 1 +) + +:: --- Critical: Print script version if available --- +for /f "tokens=3 delims=' " %%A in ('findstr /C:"$ScriptVersion = '" "%PS_SCRIPT%"') do set SCRIPT_VERSION=%%A +if defined SCRIPT_VERSION ( + echo [INFO] Script version: %SCRIPT_VERSION% +) + +:: QoL: Clear screen and color title (if supported) +cls title Windows Telemetry Blocker echo =================================== echo Windows Telemetry Blocker echo =================================== echo. -echo Starting script with administrator privileges... +echo [INFO] This launcher will ensure you have admin rights and run the script with the best available PowerShell. +echo [INFO] If you see errors, right-click and 'Run as administrator'. +echo [INFO] Logs and reports will be saved in the script folder. echo. -PowerShell -ExecutionPolicy Bypass -Command "& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%~dp0windows-telemetry-blocker.ps1""' -Verb RunAs}" \ No newline at end of file +:: Check for admin rights +net session >nul 2>&1 +if %errorlevel% neq 0 ( + echo [WARN] Requesting administrator privileges... + PowerShell -ExecutionPolicy Bypass -Command "Start-Process '%~dpnx0' -Verb RunAs" + exit /b +) + +echo [OK] Running with administrator privileges. +echo. + +:: --- Critical: Check PowerShell version --- +set "PS_VER_OK=0" +where pwsh >nul 2>&1 +if %errorlevel%==0 ( + for /f "delims=" %%V in ('pwsh -NoProfile -Command "$PSVersionTable.PSVersion.ToString()"') do set PSVER=%%V + echo [INFO] Using PowerShell Core (pwsh) version %PSVER%... + set "PS_VER_OK=1" + pwsh -NoProfile -ExecutionPolicy Bypass -File "%PS_SCRIPT%" + set "PS_EXIT=%ERRORLEVEL%" +) else ( + where powershell >nul 2>&1 + if %errorlevel%==0 ( + for /f "delims=" %%V in ('powershell -NoProfile -Command "$PSVersionTable.PSVersion.ToString()"') do set PSVER=%%V + echo [INFO] Using Windows PowerShell version %PSVER%... + set "PS_VER_OK=1" + powershell -NoProfile -ExecutionPolicy Bypass -File "%PS_SCRIPT%" + set "PS_EXIT=%ERRORLEVEL%" + ) else ( + echo [ERROR] PowerShell is not installed or not in PATH. + pause + exit /b 1 + ) +) +if "%PS_VER_OK%" == "0" ( + echo [FATAL] No compatible PowerShell found. + pause + exit /b 1 +) + +:: --- Critical: Pause and show result, print log/report if error --- +if "%PS_EXIT%" NEQ "0" ( + echo. + echo [ERROR] The script exited with error code %PS_EXIT%. + if exist "%SCRIPT_DIR%telemetry-blocker-errors.log" ( + echo --- Error Log --- + type "%SCRIPT_DIR%telemetry-blocker-errors.log" + ) + if exist "%SCRIPT_DIR%telemetry-blocker-report.md" ( + echo --- Report --- + type "%SCRIPT_DIR%telemetry-blocker-report.md" + ) + echo Please check the log and report files for details. +) else ( + echo. + echo [SUCCESS] Script completed. Review the report and logs for results. +) +echo. +pause +endlocal \ No newline at end of file diff --git a/telemetry-blocker-report.md b/telemetry-blocker-report.md new file mode 100644 index 0000000..8faec06 --- /dev/null +++ b/telemetry-blocker-report.md @@ -0,0 +1,16 @@ +# Windows Telemetry Blocker - Change Report + +This report will be generated by the script after each run (or dry-run), summarizing all actions taken or that would be taken. + +- **Windows Version:** +- **Build Number:** +- **Script Version:** +- **Execution Time:** +- **Modules Run:** +- **Summary:** +- **Errors:** +- **Rollback Modules:** + +--- + +*This file is a template. The script will overwrite or update a file like this after each run.* diff --git a/windows-telemetry-blocker.ps1 b/windows-telemetry-blocker.ps1 index c74f7a3..aef1e61 100644 --- a/windows-telemetry-blocker.ps1 +++ b/windows-telemetry-blocker.ps1 @@ -8,8 +8,10 @@ $ScriptVersion = 'nextgen-0.1-DB6' param( [switch]$all, [string[]]$modules, + [string[]]$exclude, [switch]$interactive, - [switch]$dryrun + [switch]$dryrun, + [switch]$whatif ) # --- Version Banner --- @@ -26,15 +28,43 @@ Write-Host "Script started at: $(Get-Date)" -ForegroundColor Yellow Write-Host "Running from: $PSScriptRoot" -ForegroundColor Yellow Write-Host "================================`n" +# Registry backup/export before changes +function Export-RegistryBackup { + $backupDir = Join-Path $PSScriptRoot "registry-backups" + if (-not (Test-Path $backupDir)) { New-Item -ItemType Directory -Path $backupDir | Out-Null } + $timestamp = Get-Date -Format 'yyyyMMdd_HHmmss' + $backupFile = Join-Path $backupDir "regbackup_$timestamp.reg" + Write-Host "Exporting registry backup to $backupFile ..." -ForegroundColor Cyan + reg export HKLM $backupFile /y | Out-Null + Write-Host "✓ Registry backup complete." -ForegroundColor Green +} + +# Only export backup if not dryrun +if (-not $dryrun) { Export-RegistryBackup } + + # Logging setup $logFile = Join-Path $PSScriptRoot "telemetry-blocker.log" function Write-Log { - param([string]$msg) + param([string]$msg, [switch]$Error) $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" - "$timestamp $msg" | Out-File -FilePath $logFile -Append -Encoding utf8 + $entry = "$timestamp $msg" + $entry | Out-File -FilePath $logFile -Append -Encoding utf8 + if ($Error) { + $errorLogFile = Join-Path $PSScriptRoot "telemetry-blocker-errors.log" + $entry | Out-File -FilePath $errorLogFile -Append -Encoding utf8 + } } +# Log Windows version and build number at the start +$osInfo = Get-CimInstance -ClassName Win32_OperatingSystem +$winVersion = $osInfo.Version +$winBuild = $osInfo.BuildNumber Write-Log "=== Script started ===" +Write-Log "Windows Version: $winVersion" +Write-Log "Windows Build: $winBuild" +Write-Log "Script Version: $ScriptVersion" +$reportFile = Join-Path $PSScriptRoot "telemetry-blocker-report.md" # Check if modules directory exists $modulesDir = Join-Path $PSScriptRoot "modules" @@ -378,6 +408,7 @@ foreach ($mod in $toRunResolved) { } } + $endTime = Get-Date $duration = $endTime - $startTime Write-Stats "Execution started: $startTime" @@ -408,5 +439,44 @@ Write-Host "Error log: $errorLogFile" -ForegroundColor Yellow if ($rollbackModules.Count -gt 0) { Write-Host "Rollback modules executed: $($rollbackModules -join ', ')" -ForegroundColor Red } + +# --- Generate Markdown/HTML Report --- +$reportContent = @() +$reportContent += "# Windows Telemetry Blocker - Change Report" +$reportContent += "" +$reportContent += "**Date:** $(Get-Date)" +$reportContent += "**Script Version:** $ScriptVersion" +$reportContent += "**Windows Version:** $winVersion" +$reportContent += "**Windows Build:** $winBuild" +$reportContent += "**Execution Time:** $($duration.ToString())" +$reportContent += "" +$reportContent += "## Modules Run" +$moduleKeys = $moduleResults.Keys +foreach ($mod in $moduleKeys) { + $res = $moduleResults[$mod] + $line = "- $mod $($res.Status) (Start: $($res.Start), End: $($res.End))" + if ($res.Error) { $line += " - Error: $($res.Error)" } + $reportContent += $line +} +$reportContent += "" +$reportContent += "## Summary" +foreach ($item in $summary) { + $reportContent += "- $item" +} +$reportContent += "" +if ($rollbackModules.Count -gt 0) { + $reportContent += "## Rollback Modules" + $reportContent += "- $($rollbackModules -join ', ')" +} +$reportContent += "" +$errors = Get-Content -Path $errorLogFile -ErrorAction SilentlyContinue +if ($errors -and $errors.Count -gt 0) { + $reportContent += "## Errors" + foreach ($err in $errors) { $reportContent += "- $err" } +} + +$reportContent | Set-Content -Path $reportFile -Encoding utf8 + +Write-Host "Report written to: $reportFile" -ForegroundColor Green Write-Host "Press any key to exit..." $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") \ No newline at end of file