2 Commits
Author SHA1 Message Date
0x000 4dfec83063 Fix GUI operation script scope
Windows tests / test-and-build (push) Canceled after 0s
2026-09-24 13:19:44 -04:00
0x000 cdfa0ecda8 Fix GUI apply status update
Windows tests / test-and-build (push) Canceled after 0s
2026-09-23 19:25:04 -04:00
15 changed files with 66 additions and 42 deletions

No files matched your search

+4 -4
View File
@@ -21,11 +21,11 @@ jobs:
dotnet-version: 10.0.x
- name: Build executable
shell: powershell
run: .\Build-Executable.ps1 -OutputDirectory .\dist\ci -Version 0.0.2
run: .\Build-Executable.ps1 -OutputDirectory .\dist\ci -Version 0.0.4
- uses: actions/upload-artifact@v4
with:
name: ms-private-0.0.2
name: ms-private-0.0.4
path: |
dist/ci/ms-private-0.0.2.exe
dist/ci/ms-private-0.0.2.exe.sha256
dist/ci/ms-private-0.0.4.exe
dist/ci/ms-private-0.0.4.exe.sha256
dist/ci/SHA256SUMS.txt
+1 -1
View File
@@ -1,5 +1,5 @@
#Requires -Version 5.1
[CmdletBinding()]param([string]$OutputDirectory,[ValidatePattern('^\d+\.\d+\.\d+$')][string]$Version='0.0.2')
[CmdletBinding()]param([string]$OutputDirectory,[ValidatePattern('^\d+\.\d+\.\d+$')][string]$Version='0.0.4')
Set-StrictMode -Version Latest;$ErrorActionPreference='Stop'
if(-not$OutputDirectory){$OutputDirectory=Join-Path $PSScriptRoot 'dist'};$out=[IO.Path]::GetFullPath($OutputDirectory);[IO.Directory]::CreateDirectory($out)|Out-Null
$seven=@((Get-Command 7z.exe -ErrorAction SilentlyContinue),(Get-Command (Join-Path $env:ProgramFiles '7-Zip\7z.exe') -ErrorAction SilentlyContinue))|Where-Object{$_}|Select-Object -First 1
+1 -1
View File
@@ -1,5 +1,5 @@
#Requires -Version 5.1
[CmdletBinding()]param([string]$OutputDirectory,[string]$Version='0.0.2')
[CmdletBinding()]param([string]$OutputDirectory,[string]$Version='0.0.4')
Set-StrictMode -Version Latest;$ErrorActionPreference='Stop'
if($Version-notmatch'^\d+\.\d+\.\d+$'){throw 'Version must use major.minor.patch.'}
$root=[IO.Path]::GetFullPath($PSScriptRoot);if(-not$OutputDirectory){$OutputDirectory=Join-Path $root ('dist\ms-private-'+$Version+'-final-'+(Get-Date -Format yyyyMMdd))};$output=[IO.Path]::GetFullPath($OutputDirectory);[IO.Directory]::CreateDirectory($output)|Out-Null
+1 -1
View File
@@ -1,6 +1,6 @@
#Requires -Version 5.1
[CmdletBinding()]
param([string]$OutputDirectory,[ValidatePattern('^\d+\.\d+\.\d+$')][string]$Version='0.0.2')
param([string]$OutputDirectory,[ValidatePattern('^\d+\.\d+\.\d+$')][string]$Version='0.0.4')
Set-StrictMode -Version Latest
$ErrorActionPreference='Stop'
if (-not $OutputDirectory) { $OutputDirectory=Join-Path $PSScriptRoot 'dist' }
+2 -2
View File
@@ -23,7 +23,7 @@
## Run
Download `ms-private-0.0.2.exe`, verify the adjacent SHA-256 file, then double-click the executable. Windows shows a UAC elevation prompt because the executable contains a `requireAdministrator` application manifest. Run it from the Windows account whose current-user apps and settings you intend to change; entering credentials for another administrator can target that account instead.
Download `ms-private-0.0.4.exe`, verify the adjacent SHA-256 file, then double-click the executable. Windows shows a UAC elevation prompt because the executable contains a `requireAdministrator` application manifest. Run it from the Windows account whose current-user apps and settings you intend to change; entering credentials for another administrator can target that account instead.
The executable contains the reviewed runtime and does not fetch code. It validates the embedded manifest and every SHA-256 before extracting to `%ProgramFiles%\ms-private\runtime\<version>-<payload hash>`, validates the installed copy on every launch, repairs modified or extra runtime files, removes older runtime versions, then opens the graphical wizard with Windows PowerShell 5.1. The executable is currently unsigned, so Windows may show an unknown-publisher warning.
@@ -185,6 +185,6 @@ Read the [complete tool breakdown](docs/TOOL-BREAKDOWN.md), [current roadmap](do
.\Build-Release.ps1
```
Tests use synthetic registry/app/network/service/account backends, read-only security inventory and temporary workspace files. CI runs 115 PowerShell checks on Windows PowerShell 5.1 and PowerShell 7, plus ten native launcher checks, PE inspection and the one-file publish. The source is ready for review as a **release candidate**; full live uninstall/sign-out/reboot/update/network/device validation on the supported OS matrix remains required before advertising a production-validated release. See [validation](docs/VALIDATION.md) and [contributing](CONTRIBUTING.md).
Tests use synthetic registry/app/network/service/account backends, read-only security inventory and temporary workspace files. CI runs 117 PowerShell checks on Windows PowerShell 5.1 and PowerShell 7, plus ten native launcher checks, PE inspection and the one-file publish. The source is ready for review as a **release candidate**; full live uninstall/sign-out/reboot/update/network/device validation on the supported OS matrix remains required before advertising a production-validated release. See [validation](docs/VALIDATION.md) and [contributing](CONTRIBUTING.md).
`Build-Executable.ps1` creates the self-contained x64 executable and checksum. `Build-Release.ps1` writes a source ZIP and checksum, and `Build-AmePlaybook.ps1` creates the separate AME wrapper when 7-Zip is installed. Builders exclude local reports, refuse to overwrite an existing artifact and do not sign or publish it.
+1 -1
View File
@@ -6,7 +6,7 @@
<Title>ms-private</Title>
<Description>Launches the ms-private graphical wizard. ms-private preserves Defender, Windows Firewall, Windows Update, security logs, WebView2, drivers and runtimes.</Description>
<Details>Every selected control is previewed and applied by the same PowerShell engine used by the standalone package.</Details>
<Version>0.0.2</Version>
<Version>0.0.4</Version>
<UniqueId>64d76b08-f58d-499c-aadc-1d72d620e704</UniqueId>
<Overhaul>false</Overhaul>
<SupportedBuilds>
+1 -1
View File
@@ -4,7 +4,7 @@
Build a Windows privacy wizard with three browser choices: Tor Browser, Zen and Helium. Its job is to reduce collection and retention, expose tradeoffs, and verify specific settings. It must never present a percentage-anonymous score or claim that a host is untraceable.
The current release-candidate version is 0.0.2. See [ROADMAP.md](ROADMAP.md) for the reconciled open and validation work and [NATIVE-API.md](NATIVE-API.md) for direct API and security-product behavior.
The current release-candidate version is 0.0.4. See [ROADMAP.md](ROADMAP.md) for the reconciled open and validation work and [NATIVE-API.md](NATIVE-API.md) for direct API and security-product behavior.
The running Windows installation is the development host. Development and read-only inspection are authorized; this iteration creates the tool and planning artifacts without destructive OS changes or installation of applications on that host.
+2 -2
View File
@@ -1,6 +1,6 @@
# ms-private 0.0.2 roadmap
# ms-private 0.0.4 roadmap
Version 0.0.2 is a published prerelease. Implementation exists for the graphical and terminal interfaces, presets, policy engine, debloat, account purge, service control, network modes, Recall, sensors, cleanup, lock handling, security audit, source/AME packaging and the self-contained executable. The remaining work is mostly live acceptance, signing provenance and the separately designed offline-maintenance system.
Version 0.0.4 is a published prerelease. Implementation exists for the graphical and terminal interfaces, presets, policy engine, debloat, account purge, service control, network modes, Recall, sensors, cleanup, lock handling, security audit, source/AME packaging and the self-contained executable. The remaining work is mostly live acceptance, signing provenance and the separately designed offline-maintenance system.
## Open work
+3 -3
View File
@@ -1,4 +1,4 @@
# Delivery status — 2026-09-23
# Delivery status — 2026-09-24
The update architecture and implementation pass was completed on 2026-09-20. The product is named ms-private. Local OS inventory, offline bundle verification, the graphical and terminal wizards, app/debloat flows, Microsoft app-account purge, network enforcement and capture, reversible privacy-service shutdown, Recall, exact sensors, privacy-artifact cleanup, lock handling, security status and AME packaging are implemented or prepared as described below. No real package download, app removal, account purge, policy write, service mutation, firewall change, device disable, cleanup or Recall deletion was performed.
@@ -23,11 +23,11 @@ The supplied script was parsed without execution. Three isolated in-memory probe
- Exact selected PnP sensor disable/restore; allowlisted Temp, crash-report, Recent/Jump List, thumbnail, PowerShell-history and expired-report cleanup; scheduling; and SessionLock clipboard/VeraCrypt handling.
- Read-only BitLocker/device-encryption, Defender/cloud posture, firewall, Secure Boot, TPM, VBS/Credential Guard, LSA, UAC, SMB/SMB1, remote-access and protected-service status without recovery keys, account names, Defender exclusions or secrets.
- AME wrapper source and builder that launch the same graphical wizard without disabling Defender or Windows Update.
- Self-contained x64 `ms-private-0.0.2.exe` launcher with a `requireAdministrator` manifest, embedded hash-manifested runtime, exact-file verification, tamper repair and protected Program Files installation.
- Self-contained x64 `ms-private-0.0.4.exe` launcher with a `requireAdministrator` manifest, embedded hash-manifested runtime, exact-file verification, tamper repair and protected Program Files installation.
## Verification performed
- 115 tests pass in PowerShell 7 and Windows PowerShell 5.1: 25 policy/transaction, 29 app/menu, 19 offline-bundle and 42 feature/package/security/service/account/preset/operation-runner checks. The runner now writes a real report path during regression testing.
- 117 tests pass in PowerShell 7 and Windows PowerShell 5.1: 25 policy/transaction, 29 app/menu, 19 offline-bundle and 44 feature/package/security/service/account/preset/operation-runner checks. The runner writes a real report path during regression testing, and the GUI child-script invoker is exercised across the same nested module boundary used by Apply.
- Ten native launcher tests pass for archive and installed-manifest integrity, installation validation, modified/extra-file repair, path traversal, bad hashes and undeclared entries. Managed PE inspection verifies AMD64 GUI format, the UAC manifest, version, checksum and signature state.
- Shared journal/report create, flush, atomic-replace and parse tests passed using temporary workspace files.
- All PowerShell source files parsed successfully during verification.
+2 -2
View File
@@ -6,7 +6,7 @@ ms-private is a local Windows privacy and debloat toolkit. It builds an exact pl
| Interface | Entry point | Use |
| --- | --- | --- |
| Single-file Windows release | `ms-private-0.0.2.exe` | Recommended launch path. Requests UAC, verifies and installs the embedded runtime, then opens the graphical wizard. |
| Single-file Windows release | `ms-private-0.0.4.exe` | Recommended launch path. Requests UAC, verifies and installs the embedded runtime, then opens the graphical wizard. |
| Graphical wizard | `ms-private-GUI.ps1` | Tabbed Windows Forms interface with one exact-plan review and final approval. |
| Terminal wizard | `ms-private.ps1` | Arrow, Space, Enter and Esc navigation with searchable app selection. |
| Complete preset CLI | `Preset.ps1` | Auto-detect or plan/apply EstablishedSafe, FreshInstall, Gentle, Medium, Strong or Maximum without navigating the UI. |
@@ -107,7 +107,7 @@ Reports are local and have no automatic upload. Installed-app inventories and ne
## Running a reviewed download
For a normal release, verify `ms-private-0.0.2.exe.sha256` and launch `ms-private-0.0.2.exe`. The executable is self-contained for Windows x64 and requests administrator rights through its application manifest. It embeds the source runtime, verifies the archive manifest and every file hash, extracts beneath `%ProgramFiles%\ms-private\runtime`, revalidates that copy at every launch, and repairs modifications or undeclared files. It is not code-signed yet, so the publisher warning is expected until a signing and provenance pipeline is added.
For a normal release, verify `ms-private-0.0.4.exe.sha256` and launch `ms-private-0.0.4.exe`. The executable is self-contained for Windows x64 and requests administrator rights through its application manifest. It embeds the source runtime, verifies the archive manifest and every file hash, extracts beneath `%ProgramFiles%\ms-private\runtime`, revalidates that copy at every launch, and repairs modifications or undeclared files. It is not code-signed yet, so the publisher warning is expected until a signing and provenance pipeline is added.
The PowerShell implementation remains visible in the source release and inside the hash-manifested payload. Moving the policy, recovery, Appx, WinGet and firewall logic into another language would not itself improve privacy; the native launcher supplies one-file delivery, elevation and runtime integrity while retaining the tested engine.
+1 -1
View File
@@ -2,7 +2,7 @@
## Completed checks
Release-candidate update, 2026-09-23: 115 tests pass in each of Windows PowerShell 5.1 and PowerShell 7 (25 policy/transaction, 29 app/menu, 19 offline-bundle, 42 feature/package/security/service/account/preset/operation-runner). The policy tests include current-user registry scope, Home direct-control selection and managed-edition exclusion, and Enterprise-only selection. The app tests include aggressive all-removable-app and explicit Store selection, safe skipping of ambiguous desktop registrations, protection of security/dependency applications, completeness of FreshInstall/Medium/Strong/Maximum and the non-destructive EstablishedSafe preset, the reported JSON catalog failure and journal callback scope failure, browser-before-removal sequencing, installer errors, stale inventory, partial failure, bundle deduplication, group selection, keyboard navigation, filtering and terminal layout limits. Feature tests cover real operation-runner report creation/finalization, fresh/established/unknown install-age decisions, continued execution of independent groups after a failure, dependency skipping, preset schema refusal, shared Recall apply/skip/conflict decisions, atomic JSON replacement, malformed or duplicate policy/service/account/network catalogs, protected update-endpoint refusal, untrusted registry-hive refusal, network journal recovery, cleanup-root and protected-log refusal, recovery-journal exclusion, browser-cache scope, protected-service exclusion, service disable/restore, forged service-plan rejection, exact account-package reset, credential allowlisting, report redaction, VeraCrypt volume validation, versioned secret-free security audit behavior and AME manifest structure. Native arrow/Space/Enter/Esc browser selection was also exercised in a real Windows PowerShell console. Live host inventory, service planning and account planning were read-only. No real uninstall, account purge, policy or service write, reboot, firewall change, capture, device disable, Recall deletion or update installation was performed.
Release-candidate update, 2026-09-24: 117 tests pass in each of Windows PowerShell 5.1 and PowerShell 7 (25 policy/transaction, 29 app/menu, 19 offline-bundle, 44 feature/package/security/service/account/preset/operation-runner). The policy tests include current-user registry scope, Home direct-control selection and managed-edition exclusion, and Enterprise-only selection. The app tests include aggressive all-removable-app and explicit Store selection, safe skipping of ambiguous desktop registrations, protection of security/dependency applications, completeness of FreshInstall/Medium/Strong/Maximum and the non-destructive EstablishedSafe preset, the reported JSON catalog failure and journal callback scope failure, browser-before-removal sequencing, installer errors, stale inventory, partial failure, bundle deduplication, group selection, keyboard navigation, filtering and terminal layout limits. Feature tests cover real operation-runner report creation/finalization, captured GUI child-script invocation across the nested execution-module boundary, invoker path-traversal refusal, fresh/established/unknown install-age decisions, continued execution of independent groups after a failure, dependency skipping, preset schema refusal, shared Recall apply/skip/conflict decisions, atomic JSON replacement, malformed or duplicate policy/service/account/network catalogs, protected update-endpoint refusal, untrusted registry-hive refusal, network journal recovery, cleanup-root and protected-log refusal, recovery-journal exclusion, browser-cache scope, protected-service exclusion, service disable/restore, forged service-plan rejection, exact account-package reset, credential allowlisting, report redaction, VeraCrypt volume validation, versioned secret-free security audit behavior and AME manifest structure. Native arrow/Space/Enter/Esc browser selection was also exercised in a real Windows PowerShell console. Live host inventory, service planning and account planning were read-only. No real uninstall, account purge, policy or service write, reboot, firewall change, capture, device disable, Recall deletion or update installation was performed.
The .NET launcher has ten passing tests for valid archive acceptance, installed payload and manifest verification, repair after modified files, rejection and repair of undeclared installed files, manifest path traversal refusal, bad-hash refusal and undeclared archive-entry refusal. `Build-Executable.ps1` must also produce exactly one x64 self-contained executable plus its checksum. Its managed PE inspection verifies AMD64 and GUI headers, the embedded `requireAdministrator` manifest, version, checksum and signature state. Before publishing, launch the GUI in a clean VM, alter and add runtime files to confirm repair, and test a Program Files access failure.
+23 -19
View File
@@ -8,7 +8,7 @@ $windowsContext=Get-WindContext
function Add-WindCheckBox {param($Parent,[string]$Text,[int]$X,[int]$Y,[bool]$Checked=$false,[int]$Width=520);$c=[Windows.Forms.CheckBox]::new();$c.Text=$Text;$c.Location=[Drawing.Point]::new($X,$Y);$c.Size=[Drawing.Size]::new($Width,26);$c.Checked=$Checked;$Parent.Controls.Add($c);$c}
function Add-WindLabel {param($Parent,[string]$Text,[int]$X,[int]$Y,[int]$Width=600,[int]$Height=40);$l=[Windows.Forms.Label]::new();$l.Text=$Text;$l.Location=[Drawing.Point]::new($X,$Y);$l.Size=[Drawing.Size]::new($Width,$Height);$Parent.Controls.Add($l);$l}
function Invoke-WindChild {param([string]$Script,[hashtable]$Parameters=@{});& (Join-Path $PSScriptRoot $Script) @Parameters|Out-Host}
$invokeWindChild=New-MsPrivateScriptInvoker -Root $PSScriptRoot
$form=[Windows.Forms.Form]::new();$form.Text='ms-private';$form.Size=[Drawing.Size]::new(860,690);$form.MinimumSize=[Drawing.Size]::new(760,600);$form.StartPosition='CenterScreen';$form.Font=[Drawing.Font]::new('Segoe UI',10);$form.BackColor=[Drawing.Color]::FromArgb(245,247,250)
$title=Add-WindLabel $form 'ms-private - Windows privacy control center' 22 15 790 35;$title.Font=[Drawing.Font]::new('Segoe UI Semibold',16);$title.ForeColor=[Drawing.Color]::FromArgb(20,65,110)
@@ -40,7 +40,7 @@ Add-WindLabel $network 'Observe changes nothing. Optional telemetry blocks only
$netProfile=[Windows.Forms.ComboBox]::new();$netProfile.DropDownStyle='DropDownList';$netProfile.Items.AddRange(@('Observe','BlockOptionalTelemetry','Isolate'));$netProfile.SelectedIndex=0;$netProfile.Location=[Drawing.Point]::new(24,85);$netProfile.Width=280;$network.Controls.Add($netProfile)
$rollback=[Windows.Forms.NumericUpDown]::new();$rollback.Minimum=2;$rollback.Maximum=60;$rollback.Value=10;$rollback.Location=[Drawing.Point]::new(330,85);$network.Controls.Add($rollback);Add-WindLabel $network 'minutes to automatic rollback' 460 87 240 28|Out-Null
$capture=[Windows.Forms.Button]::new();$capture.Text='Start local packet capture';$capture.Location=[Drawing.Point]::new(24,140);$capture.Size=[Drawing.Size]::new(250,38);$network.Controls.Add($capture)
$capture.Add_Click({try{Invoke-WindChild 'Network.ps1' @{Mode='CaptureStart'};[Windows.Forms.MessageBox]::Show('Capture started. Use Network.ps1 -Mode CaptureStop -CaptureDirectory <shown path> to stop and convert it.','ms-private')|Out-Null}catch{[Windows.Forms.MessageBox]::Show($_.Exception.Message,'ms-private error')|Out-Null}})
$capture.Add_Click({try{& $invokeWindChild 'Network.ps1' @{Mode='CaptureStart'};[Windows.Forms.MessageBox]::Show('Capture started. Use Network.ps1 -Mode CaptureStop -CaptureDirectory <shown path> to stop and convert it.','ms-private')|Out-Null}catch{[Windows.Forms.MessageBox]::Show($_.Exception.Message,'ms-private error')|Out-Null}})
Add-WindLabel $devices 'Select exact hardware devices to disable. Nothing is selected by default.' 20 18 700 28|Out-Null
$deviceList=[Windows.Forms.CheckedListBox]::new();$deviceList.Location=[Drawing.Point]::new(24,58);$deviceList.Size=[Drawing.Size]::new(720,340);$deviceList.CheckOnClick=$true;$devices.Controls.Add($deviceList)
@@ -52,7 +52,7 @@ $cleanupSchedule=Add-WindCheckBox $maintenance 'Install daily Temp and expired m
$lock=Add-WindCheckBox $maintenance 'Install session-lock clipboard clearing' 24 148 $false 650
$vc=[Windows.Forms.TextBox]::new();$vc.Location=[Drawing.Point]::new(48,190);$vc.Width=210;$maintenance.Controls.Add($vc);Add-WindLabel $maintenance 'VeraCrypt drive letters, e.g. X:,Y:' 275 192 330 28|Out-Null
Add-WindLabel $maintenance 'Optional selected VeraCrypt volumes are unmounted gracefully on lock. Privacy cleanup is ordinary deletion, not guaranteed SSD erasure. Security, Defender, update, firewall and authentication event logs remain protected.' 24 236 700 72|Out-Null
$securityAudit=[Windows.Forms.Button]::new();$securityAudit.Text='Audit encryption and security';$securityAudit.Location=[Drawing.Point]::new(24,322);$securityAudit.Size=[Drawing.Size]::new(260,38);$maintenance.Controls.Add($securityAudit);$securityAudit.Add_Click({try{Invoke-WindChild 'Security.ps1' @{};[Windows.Forms.MessageBox]::Show('Security status was written to the PowerShell host. No recovery keys were read.','ms-private')|Out-Null}catch{[Windows.Forms.MessageBox]::Show($_.Exception.Message,'ms-private error')|Out-Null}})
$securityAudit=[Windows.Forms.Button]::new();$securityAudit.Text='Audit encryption and security';$securityAudit.Location=[Drawing.Point]::new(24,322);$securityAudit.Size=[Drawing.Size]::new(260,38);$maintenance.Controls.Add($securityAudit);$securityAudit.Add_Click({try{& $invokeWindChild 'Security.ps1' @{};[Windows.Forms.MessageBox]::Show('Security status was written to the PowerShell host. No recovery keys were read.','ms-private')|Out-Null}catch{[Windows.Forms.MessageBox]::Show($_.Exception.Message,'ms-private error')|Out-Null}})
Add-WindLabel $homeTab 'Windows Home does not enforce the same policy CSPs as Pro/Enterprise. These buttons open Microsoft-supported Settings pages; verify each visible toggle before treating it as applied.' 20 18 720 70|Out-Null
$pages=@('Location','Camera','Microphone','Diagnostics','Recall','Updates','Apps','DefaultApps');for($i=0;$i-lt$pages.Count;$i++){$p=$pages[$i];$b=[Windows.Forms.Button]::new();$b.Text=$p;$b.Size=[Drawing.Size]::new(165,38);$b.Location=[Drawing.Point]::new(24+(($i%4)*182),105+([Math]::Floor($i/4)*55));$b.Tag=$p;$b.Add_Click({param($s,$e)Start-Process (Join-Path $PSScriptRoot 'Home.ps1') -ArgumentList @('-Page',[string]$s.Tag)});$homeTab.Controls.Add($b)}
@@ -100,7 +100,7 @@ $rollback.Add_ValueChanged($markCustom);$deviceList.Add_ItemCheck($markCustom)
$preset.Add_SelectedIndexChanged({$selected=@($presetCatalog.Presets|Where-Object Label -CEQ ([string]$preset.SelectedItem));if($selected.Count-eq1){Set-WindGuiPreset $selected[0]}})
$preset.SelectedItem=(@($presetCatalog.Presets|Where-Object {$_.Id -eq $installContext.RecommendedPreset})[0].Label)
$status=Add-WindLabel $form ("Ready on $($windowsContext.Edition) build $($windowsContext.Build). $($installContext.RecommendedPreset) was selected from installation age; review before applying.") 24 580 610 55;$status.ForeColor=[Drawing.Color]::FromArgb(60,70,80)
$statusLabel=Add-WindLabel $form ("Ready on $($windowsContext.Edition) build $($windowsContext.Build). $($installContext.RecommendedPreset) was selected from installation age; review before applying.") 24 580 610 55;$statusLabel.ForeColor=[Drawing.Color]::FromArgb(60,70,80)
$review=[Windows.Forms.Button]::new();$review.Text='Review plan';$review.Location=[Drawing.Point]::new(620,575);$review.Size=[Drawing.Size]::new(95,42);$form.Controls.Add($review)
$apply=[Windows.Forms.Button]::new();$apply.Text='Apply';$apply.Location=[Drawing.Point]::new(722,575);$apply.Size=[Drawing.Size]::new(95,42);$apply.BackColor=[Drawing.Color]::FromArgb(35,105,165);$apply.ForeColor=[Drawing.Color]::White;$form.Controls.Add($apply)
@@ -119,20 +119,24 @@ $apply.Add_Click({
$answer=[Windows.Forms.MessageBox]::Show("Apply this complete plan now?`r`n`r`n$removeCount applications, $($deviceList.CheckedIndices.Count) devices, account purge=$($accounts.Checked), Recall deletion=$($recall.Checked), network=$($netProfile.SelectedItem).`r`n`r`nApp/account/Recall/cache deletion is not restored by policy rollback. Maximum isolation keeps a timed recovery until separately confirmed.",'Final ms-private approval',[Windows.Forms.MessageBoxButtons]::YesNo,[Windows.Forms.MessageBoxIcon]::Warning);if($answer-ne[Windows.Forms.DialogResult]::Yes){throw 'The complete plan was not approved.'}
$tools=@();if($mullvad.Checked){$tools+='Mullvad'};if($veracrypt.Checked){$tools+='VeraCrypt'};$b=@();if($zen.Checked){$b+='Zen'};if($helium.Checked){$b+='Helium'};if($tor.Checked){$b+='Tor'};$selected=@();foreach($i in $deviceList.CheckedIndices){$selected+=@($sensorItems[[int]$i].InstanceId)}
$runState=[pscustomobject]@{NetworkJournal=$null};$groups=[Collections.Generic.List[object]]::new()
if($policyIds.Count-or$policyIssues.Count){$groups.Add([pscustomobject]@{Id='Policies';Label='Registry policy writes';DependsOn=@();Action=({if($policyIds.Count){Invoke-WindChild 'ms-private.ps1' @{Mode='Apply';Profile=[string]$profile.SelectedItem;Updates=[string]$updates.SelectedItem;Id=$policyIds;Confirm=$false}};if($policyIssues.Count){throw "$($policyIssues.Count) requested policies had a conflict or read error; eligible policies were attempted."}}.GetNewClosure())})}
if($services.Checked){$groups.Add([pscustomobject]@{Id='Services';Label='Service configuration';DependsOn=@();Action=({Invoke-WindChild 'Services.ps1' @{Mode='Apply';Profile='Strict';Confirm=$false}}.GetNewClosure())})}
if($accounts.Checked){$groups.Add([pscustomobject]@{Id='Accounts';Label='Credential and application-account purge';DependsOn=@();Action=({Invoke-WindChild 'Accounts.ps1' @{Mode='Apply';Scope='AllAppAccounts';AcknowledgeAccountDataDeletion=$true;Confirm=$false}}.GetNewClosure())})}
if($tools.Count){$groups.Add([pscustomobject]@{Id='PrivacyTools';Label='Privacy-tool installation';DependsOn=@();Action=({Invoke-WindChild 'Install-Apps.ps1' @{App=$tools;Confirm=$false}}.GetNewClosure())})}
if($b.Count-or@($appPlan | Where-Object { $_.Status -eq 'Remove' }).Count-or$appIssues.Count){$groups.Add([pscustomobject]@{Id='Applications';Label='Browser installation and exact app removal';DependsOn=@();Action=({if($appIssues.Count){throw "$($appIssues.Count) application targets were blocked or ambiguous; no app removal was attempted."};$a=@{Mode='Apply';Browser=$b;SkipBundled=(-not$bundled.Checked);KeepOneDrive=(-not$oneDrive.Checked);KeepEdge=(-not$edge.Checked);RemovePersonal=$personal.Checked;RemoveStore=$store.Checked;ReviewedPlan=$appPlan;Confirm=$false};Invoke-WindChild 'Manage-Apps.ps1' $a}.GetNewClosure())})}
if($netProfile.SelectedItem-ne'Observe'){$groups.Add([pscustomobject]@{Id='Network';Label='Firewall changes';DependsOn=@();Action=({$networkRoot=Join-Path ([Environment]::GetFolderPath('LocalApplicationData')) 'MsPrivate\network';[IO.Directory]::CreateDirectory($networkRoot)|Out-Null;$runState.NetworkJournal=Join-Path $networkRoot ((Get-Date -Format 'yyyyMMdd-HHmmss')+'-'+[guid]::NewGuid().ToString('N')+'.json');Invoke-WindChild 'Network.ps1' @{Mode='Apply';Profile=[string]$netProfile.SelectedItem;RollbackMinutes=[int]$rollback.Value;Journal=$runState.NetworkJournal;Confirm=$false}}.GetNewClosure())})}
if($selected.Count){$groups.Add([pscustomobject]@{Id='Sensors';Label='Exact device disablement';DependsOn=@();Action=({Invoke-WindChild 'Hardware.ps1' @{Mode='Apply';InstanceId=$selected;Confirm=$false}}.GetNewClosure())})}
if($recall.Checked-and($recallDisposition.Applicable-or$recallDisposition.Conflict)){$groups.Add([pscustomobject]@{Id='Recall';Label='Recall policy, snapshot and feature removal';DependsOn=@();Action=({if($recallDisposition.Conflict){throw 'The Recall plan contains a conflicting value.'};Invoke-WindChild 'Recall.ps1' @{Mode='Apply';AcknowledgeSnapshotDeletion=$true;RemoveFeature=[bool]$removeRecall.Checked;Confirm=$false}}.GetNewClosure())})}
if($cleanup.Checked-or$privacyCleanup.Checked){$groups.Add([pscustomobject]@{Id='Cleanup';Label='Allowlisted file cleanup';DependsOn=@();Action=({if($cleanup.Checked){Invoke-WindChild 'Cleanup.ps1' @{Mode='Run';AgeDays=7;Category=@('Temp');Confirm=$false}};if($privacyCleanup.Checked){Invoke-WindChild 'Cleanup.ps1' @{Mode='Run';AgeDays=0;Category=@('CrashReports','RecentItems','ThumbnailCache','BrowserCache','PowerShellHistory');ClearClipboard=$true;Confirm=$false}}}.GetNewClosure())})}
if($cleanupSchedule.Checked){$groups.Add([pscustomobject]@{Id='CleanupSchedule';Label='Cleanup schedule';DependsOn=@();Action=({Invoke-WindChild 'Cleanup.ps1' @{Mode='InstallSchedule';AgeDays=7;Confirm=$false}}.GetNewClosure())})}
if($lock.Checked){$vols=@($vc.Text.Split(',')|ForEach-Object{$_.Trim().ToUpperInvariant()}|Where-Object{$_});$groups.Add([pscustomobject]@{Id='LockHandler';Label='Session-lock handler';DependsOn=@();Action=({Invoke-WindChild 'Lock.ps1' @{Mode='Install';VeraCryptVolume=$vols;Confirm=$false}}.GetNewClosure())})}
$runRoot=Join-Path ([Environment]::GetFolderPath('LocalApplicationData')) 'MsPrivate\runs';$runPath=Join-Path $runRoot ((Get-Date -Format 'yyyyMMdd-HHmmss')+'-'+[guid]::NewGuid().ToString('N')+'.json');$progress={param($entry)$status.Text='Running: '+$entry.Label;$form.Refresh()}.GetNewClosure();$runResult=Invoke-MsPrivateGroupPlan -Groups @($groups) -OnProgress $progress -ReportPath $runPath
$networkEntry=@($runResult.Entries | Where-Object { $_.Id -eq 'Network' });if($netProfile.SelectedItem-eq'Isolate'-and$networkEntry.Count-and$networkEntry[0].Status-eq'Succeeded'){$answer=[Windows.Forms.MessageBox]::Show("Isolation is active. Verify required connectivity and local recovery now.`r`n`r`nYes keeps the block profile and cancels timed rollback.`r`nNo restores immediately. Closing this prompt leaves the timed rollback active.`r`n`r`nJournal: $($runState.NetworkJournal)",'Confirm network isolation',[Windows.Forms.MessageBoxButtons]::YesNoCancel,[Windows.Forms.MessageBoxIcon]::Warning);if($answer-eq[Windows.Forms.DialogResult]::Yes){Invoke-WindChild 'Network.ps1' @{Mode='Confirm';Journal=$runState.NetworkJournal}}elseif($answer-eq[Windows.Forms.DialogResult]::No){Invoke-WindChild 'Network.ps1' @{Mode='Restore';Journal=$runState.NetworkJournal}}}
$failed=@($runResult.Entries | Where-Object { $_.Status -in @('Failed','SkippedDependency') });$status.Text=if($failed.Count){"Finished with $($failed.Count) failed/skipped groups. Independent groups were attempted. Report: $runPath"}elseif($recallDisposition.Skipped.Count){'Completed with unsupported Recall actions skipped. Review each report and use the Home guide.'}else{'Completed. Review each generated journal/report and reboot before validation.'};[Windows.Forms.MessageBox]::Show($status.Text,'ms-private', [Windows.Forms.MessageBoxButtons]::OK,$(if($failed.Count){[Windows.Forms.MessageBoxIcon]::Warning}else{[Windows.Forms.MessageBoxIcon]::Information}))|Out-Null
}catch{$status.Text='Stopped: '+$_.Exception.Message;[Windows.Forms.MessageBox]::Show($_.Exception.Message,'ms-private error',[Windows.Forms.MessageBoxButtons]::OK,[Windows.Forms.MessageBoxIcon]::Error)|Out-Null}
if($policyIds.Count-or$policyIssues.Count){$groups.Add([pscustomobject]@{Id='Policies';Label='Registry policy writes';DependsOn=@();Action=({if($policyIds.Count){& $invokeWindChild 'ms-private.ps1' @{Mode='Apply';Profile=[string]$profile.SelectedItem;Updates=[string]$updates.SelectedItem;Id=$policyIds;Confirm=$false}};if($policyIssues.Count){throw "$($policyIssues.Count) requested policies had a conflict or read error; eligible policies were attempted."}}.GetNewClosure())})}
if($services.Checked){$groups.Add([pscustomobject]@{Id='Services';Label='Service configuration';DependsOn=@();Action=({& $invokeWindChild 'Services.ps1' @{Mode='Apply';Profile='Strict';Confirm=$false}}.GetNewClosure())})}
if($accounts.Checked){$groups.Add([pscustomobject]@{Id='Accounts';Label='Credential and application-account purge';DependsOn=@();Action=({& $invokeWindChild 'Accounts.ps1' @{Mode='Apply';Scope='AllAppAccounts';AcknowledgeAccountDataDeletion=$true;Confirm=$false}}.GetNewClosure())})}
if($tools.Count){$groups.Add([pscustomobject]@{Id='PrivacyTools';Label='Privacy-tool installation';DependsOn=@();Action=({& $invokeWindChild 'Install-Apps.ps1' @{App=$tools;Confirm=$false}}.GetNewClosure())})}
if($b.Count-or@($appPlan | Where-Object { $_.Status -eq 'Remove' }).Count-or$appIssues.Count){$groups.Add([pscustomobject]@{Id='Applications';Label='Browser installation and exact app removal';DependsOn=@();Action=({if($appIssues.Count){throw "$($appIssues.Count) application targets were blocked or ambiguous; no app removal was attempted."};$a=@{Mode='Apply';Browser=$b;SkipBundled=(-not$bundled.Checked);KeepOneDrive=(-not$oneDrive.Checked);KeepEdge=(-not$edge.Checked);RemovePersonal=$personal.Checked;RemoveStore=$store.Checked;ReviewedPlan=$appPlan;Confirm=$false};& $invokeWindChild 'Manage-Apps.ps1' $a}.GetNewClosure())})}
if($netProfile.SelectedItem-ne'Observe'){$groups.Add([pscustomobject]@{Id='Network';Label='Firewall changes';DependsOn=@();Action=({$networkRoot=Join-Path ([Environment]::GetFolderPath('LocalApplicationData')) 'MsPrivate\network';[IO.Directory]::CreateDirectory($networkRoot)|Out-Null;$runState.NetworkJournal=Join-Path $networkRoot ((Get-Date -Format 'yyyyMMdd-HHmmss')+'-'+[guid]::NewGuid().ToString('N')+'.json');& $invokeWindChild 'Network.ps1' @{Mode='Apply';Profile=[string]$netProfile.SelectedItem;RollbackMinutes=[int]$rollback.Value;Journal=$runState.NetworkJournal;Confirm=$false}}.GetNewClosure())})}
if($selected.Count){$groups.Add([pscustomobject]@{Id='Sensors';Label='Exact device disablement';DependsOn=@();Action=({& $invokeWindChild 'Hardware.ps1' @{Mode='Apply';InstanceId=$selected;Confirm=$false}}.GetNewClosure())})}
if($recall.Checked-and($recallDisposition.Applicable-or$recallDisposition.Conflict)){$groups.Add([pscustomobject]@{Id='Recall';Label='Recall policy, snapshot and feature removal';DependsOn=@();Action=({if($recallDisposition.Conflict){throw 'The Recall plan contains a conflicting value.'};& $invokeWindChild 'Recall.ps1' @{Mode='Apply';AcknowledgeSnapshotDeletion=$true;RemoveFeature=[bool]$removeRecall.Checked;Confirm=$false}}.GetNewClosure())})}
if($cleanup.Checked-or$privacyCleanup.Checked){$groups.Add([pscustomobject]@{Id='Cleanup';Label='Allowlisted file cleanup';DependsOn=@();Action=({if($cleanup.Checked){& $invokeWindChild 'Cleanup.ps1' @{Mode='Run';AgeDays=7;Category=@('Temp');Confirm=$false}};if($privacyCleanup.Checked){& $invokeWindChild 'Cleanup.ps1' @{Mode='Run';AgeDays=0;Category=@('CrashReports','RecentItems','ThumbnailCache','BrowserCache','PowerShellHistory');ClearClipboard=$true;Confirm=$false}}}.GetNewClosure())})}
if($cleanupSchedule.Checked){$groups.Add([pscustomobject]@{Id='CleanupSchedule';Label='Cleanup schedule';DependsOn=@();Action=({& $invokeWindChild 'Cleanup.ps1' @{Mode='InstallSchedule';AgeDays=7;Confirm=$false}}.GetNewClosure())})}
if($lock.Checked){$vols=@($vc.Text.Split(',')|ForEach-Object{$_.Trim().ToUpperInvariant()}|Where-Object{$_});$groups.Add([pscustomobject]@{Id='LockHandler';Label='Session-lock handler';DependsOn=@();Action=({& $invokeWindChild 'Lock.ps1' @{Mode='Install';VeraCryptVolume=$vols;Confirm=$false}}.GetNewClosure())})}
$runRoot=Join-Path ([Environment]::GetFolderPath('LocalApplicationData')) 'MsPrivate\runs';$runPath=Join-Path $runRoot ((Get-Date -Format 'yyyyMMdd-HHmmss')+'-'+[guid]::NewGuid().ToString('N')+'.json');$statusLabel.Text='Running the approved plan...';$form.Refresh();$runResult=Invoke-MsPrivateGroupPlan -Groups @($groups) -ReportPath $runPath
$networkEntry=@($runResult.Entries | Where-Object { $_.Id -eq 'Network' });if($netProfile.SelectedItem-eq'Isolate'-and$networkEntry.Count-and$networkEntry[0].Status-eq'Succeeded'){$answer=[Windows.Forms.MessageBox]::Show("Isolation is active. Verify required connectivity and local recovery now.`r`n`r`nYes keeps the block profile and cancels timed rollback.`r`nNo restores immediately. Closing this prompt leaves the timed rollback active.`r`n`r`nJournal: $($runState.NetworkJournal)",'Confirm network isolation',[Windows.Forms.MessageBoxButtons]::YesNoCancel,[Windows.Forms.MessageBoxIcon]::Warning);if($answer-eq[Windows.Forms.DialogResult]::Yes){& $invokeWindChild 'Network.ps1' @{Mode='Confirm';Journal=$runState.NetworkJournal}}elseif($answer-eq[Windows.Forms.DialogResult]::No){& $invokeWindChild 'Network.ps1' @{Mode='Restore';Journal=$runState.NetworkJournal}}}
$failed=@($runResult.Entries | Where-Object { $_.Status -in @('Failed','SkippedDependency') })
$statusLabel.Text=if($failed.Count){"Finished with $($failed.Count) failed/skipped groups. Report: $runPath"}elseif($recallDisposition.Skipped.Count){'Completed with unsupported Recall actions skipped. Review each report and use the Home guide.'}else{'Completed. Review each generated journal/report and reboot before validation.'}
$message=$statusLabel.Text
if($failed.Count){$details=@($failed|ForEach-Object{"$($_.Label): $($_.Error)"});$message+="`r`n`r`n"+($details-join"`r`n")}
[Windows.Forms.MessageBox]::Show($message,'ms-private',[Windows.Forms.MessageBoxButtons]::OK,$(if($failed.Count){[Windows.Forms.MessageBoxIcon]::Warning}else{[Windows.Forms.MessageBoxIcon]::Information}))|Out-Null
}catch{$statusLabel.Text='Stopped: '+$_.Exception.Message;[Windows.Forms.MessageBox]::Show($_.Exception.Message,'ms-private error',[Windows.Forms.MessageBoxButtons]::OK,[Windows.Forms.MessageBoxIcon]::Error)|Out-Null}
})
[void]$form.ShowDialog()
@@ -19,9 +19,9 @@
<ApplicationManifest>app.manifest</ApplicationManifest>
<AssemblyName>ms-private</AssemblyName>
<RootNamespace>MsPrivate.Launcher</RootNamespace>
<Version>0.0.2</Version>
<AssemblyVersion>0.0.2.0</AssemblyVersion>
<FileVersion>0.0.2.0</FileVersion>
<Version>0.0.4</Version>
<AssemblyVersion>0.0.4.0</AssemblyVersion>
<FileVersion>0.0.4.0</FileVersion>
<Product>ms-private</Product>
<Description>Windows privacy control center</Description>
</PropertyGroup>
+14 -1
View File
@@ -2,6 +2,19 @@ Set-StrictMode -Version Latest
$ErrorActionPreference='Stop'
Import-Module (Join-Path $PSScriptRoot 'MsPrivate.IO.psm1') -ErrorAction Stop
function New-MsPrivateScriptInvoker {
param([Parameter(Mandatory)][string]$Root)
$runtimeRoot=[IO.Path]::GetFullPath($Root).TrimEnd([IO.Path]::DirectorySeparatorChar)+[IO.Path]::DirectorySeparatorChar
if(-not[IO.Directory]::Exists($runtimeRoot)){throw "Runtime root does not exist: $runtimeRoot"}
{
param([Parameter(Mandatory)][string]$Script,[hashtable]$Parameters=@{})
if($Script-notmatch'^[A-Za-z0-9][A-Za-z0-9.-]*\.ps1$'){throw "Invalid runtime script name: $Script"}
$path=[IO.Path]::GetFullPath((Join-Path $runtimeRoot $Script))
if(-not$path.StartsWith($runtimeRoot,[StringComparison]::OrdinalIgnoreCase)-or-not[IO.File]::Exists($path)){throw "Runtime script was not found: $Script"}
& $path @Parameters|Out-Host
}.GetNewClosure()
}
function Invoke-MsPrivateGroupPlan {
param(
[Parameter(Mandatory)][object[]]$Groups,
@@ -36,4 +49,4 @@ function Invoke-MsPrivateGroupPlan {
$report
}
Export-ModuleMember -Function Invoke-MsPrivateGroupPlan
Export-ModuleMember -Function Invoke-MsPrivateGroupPlan,New-MsPrivateScriptInvoker
+7
View File
@@ -22,6 +22,13 @@ try{
$saved=Get-Content -LiteralPath $jsonPath -Raw|ConvertFrom-Json
Assert-True ($saved.Value-eq2-and-not@(Get-ChildItem -LiteralPath $ioRoot -Filter '*.tmp' -Force).Count-and-not@(Get-ChildItem -LiteralPath $ioRoot -Filter '*.previous' -Force).Count) 'Atomic JSON replacement left stale state or temporary files.'
$probePath=Join-Path $ioRoot 'Probe.ps1';[IO.File]::WriteAllText($probePath,"param([string]`$Value) [IO.File]::WriteAllText((Join-Path `$PSScriptRoot 'probe.txt'),`$Value)",[Text.UTF8Encoding]::new($false))
$invoker=New-MsPrivateScriptInvoker -Root $ioRoot
$action=({& $invoker 'Probe.ps1' @{Value='nested-module'}}.GetNewClosure())
$invokerRun=Invoke-MsPrivateGroupPlan -Groups @([pscustomobject]@{Id='Invoker';Label='Captured script invoker';DependsOn=@();Action=$action})
Assert-True ($invokerRun.Status-eq'Completed'-and[IO.File]::ReadAllText((Join-Path $ioRoot 'probe.txt'))-eq'nested-module') 'Captured child-script invoker failed across execution-module scope.'
$threw=$false;try{& $invoker '..\outside.ps1' @{}|Out-Null}catch{$threw=$true};Assert-True $threw 'Child-script invoker accepted path traversal.'
$network=Get-Content -LiteralPath (Join-Path $root 'config/network.json') -Raw|ConvertFrom-Json;$network.OptionalTelemetry[1].Fqdn=$network.OptionalTelemetry[0].Fqdn;$path=Join-Path $ioRoot 'network.json';$network|ConvertTo-Json -Depth 8|Set-Content -LiteralPath $path
$threw=$false;try{Get-WindNetworkCatalog -Path $path|Out-Null}catch{$threw=$true};Assert-True $threw 'Network catalog accepted a duplicate FQDN.'
$network=Get-Content -LiteralPath (Join-Path $root 'config/network.json') -Raw|ConvertFrom-Json;$network.OptionalTelemetry[0].Fqdn='ctldl.windowsupdate.com';$path=Join-Path $ioRoot 'network-protected.json';$network|ConvertTo-Json -Depth 8|Set-Content -LiteralPath $path