2 Commits
Author SHA1 Message Date
0x000 cdfa0ecda8 Fix GUI apply status update
Windows tests / test-and-build (push) Canceled after 0s
2026-09-23 19:25:04 -04:00
0x000 22d06ac7fd Fix run reports and Windows Home policy planning
Windows tests / test-and-build (push) Canceled after 0s
2026-09-23 18:49:13 -04:00
19 changed files with 75 additions and 46 deletions

No files matched your search

+4 -4
View File
@@ -21,11 +21,11 @@ jobs:
dotnet-version: 10.0.x dotnet-version: 10.0.x
- name: Build executable - name: Build executable
shell: powershell shell: powershell
run: .\Build-Executable.ps1 -OutputDirectory .\dist\ci -Version 0.0.1 run: .\Build-Executable.ps1 -OutputDirectory .\dist\ci -Version 0.0.3
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
name: ms-private-0.0.1 name: ms-private-0.0.3
path: | path: |
dist/ci/ms-private-0.0.1.exe dist/ci/ms-private-0.0.3.exe
dist/ci/ms-private-0.0.1.exe.sha256 dist/ci/ms-private-0.0.3.exe.sha256
dist/ci/SHA256SUMS.txt dist/ci/SHA256SUMS.txt
+1 -1
View File
@@ -1,5 +1,5 @@
#Requires -Version 5.1 #Requires -Version 5.1
[CmdletBinding()]param([string]$OutputDirectory,[ValidatePattern('^\d+\.\d+\.\d+$')][string]$Version='0.0.1') [CmdletBinding()]param([string]$OutputDirectory,[ValidatePattern('^\d+\.\d+\.\d+$')][string]$Version='0.0.3')
Set-StrictMode -Version Latest;$ErrorActionPreference='Stop' 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 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 $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 #Requires -Version 5.1
[CmdletBinding()]param([string]$OutputDirectory,[string]$Version='0.0.1') [CmdletBinding()]param([string]$OutputDirectory,[string]$Version='0.0.3')
Set-StrictMode -Version Latest;$ErrorActionPreference='Stop' Set-StrictMode -Version Latest;$ErrorActionPreference='Stop'
if($Version-notmatch'^\d+\.\d+\.\d+$'){throw 'Version must use major.minor.patch.'} 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 $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 #Requires -Version 5.1
[CmdletBinding()] [CmdletBinding()]
param([string]$OutputDirectory,[ValidatePattern('^\d+\.\d+\.\d+$')][string]$Version='0.0.1') param([string]$OutputDirectory,[ValidatePattern('^\d+\.\d+\.\d+$')][string]$Version='0.0.3')
Set-StrictMode -Version Latest Set-StrictMode -Version Latest
$ErrorActionPreference='Stop' $ErrorActionPreference='Stop'
if (-not $OutputDirectory) { $OutputDirectory=Join-Path $PSScriptRoot 'dist' } if (-not $OutputDirectory) { $OutputDirectory=Join-Path $PSScriptRoot 'dist' }
+3 -3
View File
@@ -23,7 +23,7 @@
## Run ## Run
Download `ms-private-0.0.1.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.3.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. 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.
@@ -146,7 +146,7 @@ WinGet must be installed and its `winget` source usable. ms-private retains norm
## Support and recovery ## Support and recovery
- Policy apply/restore: Windows 11 Pro/Enterprise/Education families, builds 26100/26200. Home and unreviewed builds use the manual guide; the menu explicitly reports skipped policies. - Policy apply/restore: Windows 11 Pro/Enterprise/Education families, builds 26100/26200. On Home, seven Microsoft-documented direct registry controls remain available; managed-edition CSP policies are explicitly skipped and linked to the manual guide. Unreviewed builds remain audit-only.
- App operations: build gate 19045/22631/26100/26200. Windows 10 requires an appropriately supported servicing arrangement. Build gating is not proof of successful live testing. - App operations: build gate 19045/22631/26100/26200. Windows 10 requires an appropriately supported servicing arrangement. Build gating is not proof of successful live testing.
- Domain/MDM-managed devices and non-elevated changes are refused. Use an administrator-managed deployment for those devices. - Domain/MDM-managed devices and non-elevated changes are refused. Use an administrator-managed deployment for those devices.
- Policy changes save original values before writing and roll back failures. Restoration preserves later conflicting edits. Restore newer journals before older ones. - Policy changes save original values before writing and roll back failures. Restoration preserves later conflicting edits. Restore newer journals before older ones.
@@ -185,6 +185,6 @@ Read the [complete tool breakdown](docs/TOOL-BREAKDOWN.md), [current roadmap](do
.\Build-Release.ps1 .\Build-Release.ps1
``` ```
Tests use synthetic registry/app/network/service/account backends, read-only security inventory and temporary workspace files. CI runs 114 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 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).
`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. `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> <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> <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> <Details>Every selected control is previewed and applied by the same PowerShell engine used by the standalone package.</Details>
<Version>0.0.1</Version> <Version>0.0.3</Version>
<UniqueId>64d76b08-f58d-499c-aadc-1d72d620e704</UniqueId> <UniqueId>64d76b08-f58d-499c-aadc-1d72d620e704</UniqueId>
<Overhaul>false</Overhaul> <Overhaul>false</Overhaul>
<SupportedBuilds> <SupportedBuilds>
+7
View File
@@ -262,6 +262,7 @@
"Standard": 1, "Standard": 1,
"Strict": 1, "Strict": 1,
"EnterpriseValue": null, "EnterpriseValue": null,
"HomeSupported": true,
"Source": "https://learn.microsoft.com/en-us/windows/win32/wer/wer-settings", "Source": "https://learn.microsoft.com/en-us/windows/win32/wer/wer-settings",
"Impact": "Stops Windows Error Reporting. Automatic crash analysis and solution lookup are unavailable; local security event channels are unaffected.", "Impact": "Stops Windows Error Reporting. Automatic crash analysis and solution lookup are unavailable; local security event channels are unaffected.",
"Exceptions": [] "Exceptions": []
@@ -274,6 +275,7 @@
"Standard": 1, "Standard": 1,
"Strict": 1, "Strict": 1,
"EnterpriseValue": null, "EnterpriseValue": null,
"HomeSupported": true,
"Source": "https://learn.microsoft.com/en-us/windows/win32/wer/wer-settings", "Source": "https://learn.microsoft.com/en-us/windows/win32/wer/wer-settings",
"Impact": "Adds a defense-in-depth restriction on second-level WER data if another component re-enables reporting.", "Impact": "Adds a defense-in-depth restriction on second-level WER data if another component re-enables reporting.",
"Exceptions": [] "Exceptions": []
@@ -348,6 +350,7 @@
"Standard": 1, "Standard": 1,
"Strict": 1, "Strict": 1,
"EnterpriseValue": null, "EnterpriseValue": null,
"HomeSupported": true,
"Source": "https://learn.microsoft.com/en-gb/windows/privacy/manage-connections-from-windows-operating-system-components-to-microsoft-services", "Source": "https://learn.microsoft.com/en-gb/windows/privacy/manage-connections-from-windows-operating-system-components-to-microsoft-services",
"Impact": "Restricts typing personalization collection for the current user. It does not block input observed by applications receiving keystrokes.", "Impact": "Restricts typing personalization collection for the current user. It does not block input observed by applications receiving keystrokes.",
"Exceptions": [] "Exceptions": []
@@ -361,6 +364,7 @@
"Standard": 1, "Standard": 1,
"Strict": 1, "Strict": 1,
"EnterpriseValue": null, "EnterpriseValue": null,
"HomeSupported": true,
"Source": "https://learn.microsoft.com/en-gb/windows/privacy/manage-connections-from-windows-operating-system-components-to-microsoft-services", "Source": "https://learn.microsoft.com/en-gb/windows/privacy/manage-connections-from-windows-operating-system-components-to-microsoft-services",
"Impact": "Restricts handwriting personalization collection for the current user; recognition and personalization quality can be reduced.", "Impact": "Restricts handwriting personalization collection for the current user; recognition and personalization quality can be reduced.",
"Exceptions": [] "Exceptions": []
@@ -409,6 +413,7 @@
"Standard": 0, "Standard": 0,
"Strict": 1, "Strict": 1,
"EnterpriseValue": null, "EnterpriseValue": null,
"HomeSupported": true,
"Source": "https://learn.microsoft.com/en-gb/windows/privacy/manage-connections-from-windows-operating-system-components-to-microsoft-services", "Source": "https://learn.microsoft.com/en-gb/windows/privacy/manage-connections-from-windows-operating-system-components-to-microsoft-services",
"Impact": "Strict stops device metadata downloads; device icons, names or companion information can be less complete.", "Impact": "Strict stops device metadata downloads; device icons, names or companion information can be less complete.",
"Exceptions": [] "Exceptions": []
@@ -421,6 +426,7 @@
"Standard": 1, "Standard": 1,
"Strict": 0, "Strict": 0,
"EnterpriseValue": null, "EnterpriseValue": null,
"HomeSupported": true,
"Source": "https://learn.microsoft.com/en-gb/windows/privacy/manage-connections-from-windows-operating-system-components-to-microsoft-services", "Source": "https://learn.microsoft.com/en-gb/windows/privacy/manage-connections-from-windows-operating-system-components-to-microsoft-services",
"Impact": "Strict prevents Windows from downloading missing fonts; documents or sites can render with fallback fonts.", "Impact": "Strict prevents Windows from downloading missing fonts; documents or sites can render with fallback fonts.",
"Exceptions": [] "Exceptions": []
@@ -433,6 +439,7 @@
"Standard": 1, "Standard": 1,
"Strict": 1, "Strict": 1,
"EnterpriseValue": null, "EnterpriseValue": null,
"HomeSupported": true,
"Source": "https://learn.microsoft.com/en-gb/windows/privacy/manage-connections-from-windows-operating-system-components-to-microsoft-services", "Source": "https://learn.microsoft.com/en-gb/windows/privacy/manage-connections-from-windows-operating-system-components-to-microsoft-services",
"Impact": "Stops MSRT infection reports while preserving the tool and its delivery through Windows Update.", "Impact": "Stops MSRT infection reports while preserving the tool and its delivery through Windows Update.",
"Exceptions": [] "Exceptions": []
+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. 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.1. 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.3. 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. 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.1 roadmap # ms-private 0.0.3 roadmap
Version 0.0.1 is an unpublished release candidate. 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, release provenance and the separately designed offline-maintenance system. Version 0.0.3 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 ## Open work
+4 -4
View File
@@ -1,4 +1,4 @@
# Delivery status — 2026-09-19 # Delivery status — 2026-09-23
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. 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,15 +23,15 @@ 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. - 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. - 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. - AME wrapper source and builder that launch the same graphical wizard without disabling Defender or Windows Update.
- Self-contained x64 `ms-private-0.0.1.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.3.exe` launcher with a `requireAdministrator` manifest, embedded hash-manifested runtime, exact-file verification, tamper repair and protected Program Files installation.
## Verification performed ## Verification performed
- 114 tests pass in PowerShell 7 and Windows PowerShell 5.1: 25 policy/transaction, 29 app/menu, 19 offline-bundle and 41 feature/package/security/service/account/preset/operation-runner checks. - 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.
- 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. - 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. - Shared journal/report create, flush, atomic-replace and parse tests passed using temporary workspace files.
- All PowerShell source files parsed successfully during verification. - All PowerShell source files parsed successfully during verification.
- Real read-only service planning completed in both PowerShell engines. Policy enforcement remains audit-only on this Windows Home (`Core`) build 26200. - Real read-only service and policy planning completed in both PowerShell engines. On this Windows Home (`Core`) build 26200, seven documented direct registry controls are eligible and 45 managed-edition policies remain unsupported.
- Installer previews used `-WhatIf`; no package installation occurred. - Installer previews used `-WhatIf`; no package installation occurred.
- Live operating-system policy mutation, app uninstallation, firewall changes and telemetry suppression were not performed or claimed. - Live operating-system policy mutation, app uninstallation, firewall changes and telemetry suppression were not performed or claimed.
+3 -3
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 | | Interface | Entry point | Use |
| --- | --- | --- | | --- | --- | --- |
| Single-file Windows release | `ms-private-0.0.1.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.3.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. | | 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. | | 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. | | Complete preset CLI | `Preset.ps1` | Auto-detect or plan/apply EstablishedSafe, FreshInstall, Gentle, Medium, Strong or Maximum without navigating the UI. |
@@ -31,7 +31,7 @@ The policy catalog contains 56 reviewed controls. Standard and Strict cover diag
Update choices are separate: Keep, Automatic, Notify and Manual. The update engine, Update Orchestrator, BITS, Cryptographic Services and servicing infrastructure remain installed. Update choices are separate: Keep, Automatic, Notify and Manual. The update engine, Update Orchestrator, BITS, Cryptographic Services and servicing infrastructure remain installed.
The engine checks Windows edition and build, refuses domain or MDM-managed devices, journals original values before writing, reads back changes, rolls back partial failures and preserves later conflicting edits during restoration. Home and unreviewed builds receive an explicit unsupported result and the Settings guide. The engine checks Windows edition and build, refuses domain or MDM-managed devices, journals original values before writing, reads back changes, rolls back partial failures and preserves later conflicting edits during restoration. Home can apply seven direct registry controls documented independently of managed-edition CSP support; its remaining CSP policies are explicitly unsupported and linked to the Settings guide. Unreviewed builds remain audit-only.
## Services and GDID-related reduction ## Services and GDID-related reduction
@@ -107,7 +107,7 @@ Reports are local and have no automatic upload. Installed-app inventories and ne
## Running a reviewed download ## Running a reviewed download
For a normal release, verify `ms-private-0.0.1.exe.sha256` and launch `ms-private-0.0.1.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.3.exe.sha256` and launch `ms-private-0.0.3.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. 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.
+2 -2
View File
@@ -2,7 +2,7 @@
## Completed checks ## Completed checks
Release-candidate update, 2026-09-22: 114 tests pass in each of Windows PowerShell 5.1 and PowerShell 7 (25 policy/transaction, 29 app/menu, 19 offline-bundle, 41 feature/package/security/service/account/preset/operation-runner). The policy tests include current-user registry scope 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 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-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.
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. 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.
@@ -18,7 +18,7 @@ Run `./tests/Run-All.ps1` with both PowerShell 7 and Windows PowerShell 5.1. Thi
| OS | Test | | OS | Test |
| --- | --- | | --- | --- |
| Windows 11 Home 24H2/25H2 | Audit reports unsupported machine policies; Apply makes no changes; manual guide matches current Settings | | Windows 11 Home 24H2/25H2 | Seven documented direct registry controls apply transactionally; managed-edition policies remain unsupported; manual guide matches current Settings |
| Windows 11 Pro 24H2/25H2 | Standard/Strict policy and service apply, readback, reboot, behavioral checks and restore | | Windows 11 Pro 24H2/25H2 | Standard/Strict policy and service apply, readback, reboot, behavioral checks and restore |
| Windows 11 Enterprise/Education 24H2/25H2 | Same, including actual diagnostic policy effect | | Windows 11 Enterprise/Education 24H2/25H2 | Same, including actual diagnostic policy effect |
| Unknown/future build | No automatic mutation until separately reviewed | | Unknown/future build | No automatic mutation until separately reviewed |
+9 -6
View File
@@ -4,13 +4,14 @@ Set-StrictMode -Version Latest;$ErrorActionPreference='Stop'
if([Environment]::OSVersion.Platform-ne[PlatformID]::Win32NT){throw 'Windows is required.'} if([Environment]::OSVersion.Platform-ne[PlatformID]::Win32NT){throw 'Windows is required.'}
Add-Type -AssemblyName System.Windows.Forms;Add-Type -AssemblyName System.Drawing Add-Type -AssemblyName System.Windows.Forms;Add-Type -AssemblyName System.Drawing
Import-Module (Join-Path $PSScriptRoot 'src/Wind.Core.psm1');Import-Module (Join-Path $PSScriptRoot 'src/Wind.Hardware.psm1');Import-Module (Join-Path $PSScriptRoot 'src/Wind.Apps.psm1');Import-Module (Join-Path $PSScriptRoot 'src/Wind.Recall.psm1');Import-Module (Join-Path $PSScriptRoot 'src/MsPrivate.Presets.psm1');Import-Module (Join-Path $PSScriptRoot 'src/MsPrivate.Execution.psm1') Import-Module (Join-Path $PSScriptRoot 'src/Wind.Core.psm1');Import-Module (Join-Path $PSScriptRoot 'src/Wind.Hardware.psm1');Import-Module (Join-Path $PSScriptRoot 'src/Wind.Apps.psm1');Import-Module (Join-Path $PSScriptRoot 'src/Wind.Recall.psm1');Import-Module (Join-Path $PSScriptRoot 'src/MsPrivate.Presets.psm1');Import-Module (Join-Path $PSScriptRoot 'src/MsPrivate.Execution.psm1')
$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-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 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} function Invoke-WindChild {param([string]$Script,[hashtable]$Parameters=@{});& (Join-Path $PSScriptRoot $Script) @Parameters|Out-Host}
$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) $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) $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)
$tabs=[Windows.Forms.TabControl]::new();$tabs.Location=[Drawing.Point]::new(20,58);$tabs.Size=[Drawing.Size]::new(804,510);$form.Controls.Add($tabs) $tabs=[Windows.Forms.TabControl]::new();$tabs.Location=[Drawing.Point]::new(20,58);$tabs.Size=[Drawing.Size]::new(804,510);$form.Controls.Add($tabs)
$privacy=[Windows.Forms.TabPage]::new('Privacy');$apps=[Windows.Forms.TabPage]::new('Apps & browsers');$network=[Windows.Forms.TabPage]::new('Network');$devices=[Windows.Forms.TabPage]::new('Devices');$maintenance=[Windows.Forms.TabPage]::new('Maintenance');$homeTab=[Windows.Forms.TabPage]::new('Home guide');$tabs.TabPages.AddRange(@($privacy,$apps,$network,$devices,$maintenance,$homeTab)) $privacy=[Windows.Forms.TabPage]::new('Privacy');$apps=[Windows.Forms.TabPage]::new('Apps & browsers');$network=[Windows.Forms.TabPage]::new('Network');$devices=[Windows.Forms.TabPage]::new('Devices');$maintenance=[Windows.Forms.TabPage]::new('Maintenance');$homeTab=[Windows.Forms.TabPage]::new('Home guide');$tabs.TabPages.AddRange(@($privacy,$apps,$network,$devices,$maintenance,$homeTab))
@@ -75,7 +76,9 @@ function Set-WindGuiPreset {
function Get-WindGuiReviewText { function Get-WindGuiReviewText {
param([object[]]$AppPlan,[object[]]$PolicyPlan=@()) param([object[]]$AppPlan,[object[]]$PolicyPlan=@())
$remove=@($AppPlan|Where-Object {$_.Status -eq 'Remove'}|Sort-Object Name) $remove=@($AppPlan|Where-Object {$_.Status -eq 'Remove'}|Sort-Object Name)
$lines=@("Preset: $($preset.SelectedItem)","Privacy: $($profile.SelectedItem); updates: $($updates.SelectedItem)","Privacy services: $($services.Checked); account purge: $($accounts.Checked)","Network: $($netProfile.SelectedItem); Recall deletion/removal: $($recall.Checked)/$($removeRecall.Checked)","Cleanup now/privacy/schedule: $($cleanup.Checked)/$($privacyCleanup.Checked)/$($cleanupSchedule.Checked)","Sensors selected: $($deviceList.CheckedIndices.Count)","Applications selected: $($remove.Count)",'') $supportedCount=@($PolicyPlan|Where-Object {$_.Status-in@('WouldChange','Configured')}).Count;$unsupportedCount=@($PolicyPlan|Where-Object {$_.Status-eq'Unsupported'}).Count
$lines=@("Windows: $($windowsContext.Edition), build $($windowsContext.Build).$($windowsContext.Revision)","Policy controls available: $supportedCount; unsupported on this edition/build: $unsupportedCount","Preset: $($preset.SelectedItem)","Privacy: $($profile.SelectedItem); updates: $($updates.SelectedItem)","Privacy services: $($services.Checked); account purge: $($accounts.Checked)","Network: $($netProfile.SelectedItem); Recall deletion/removal: $($recall.Checked)/$($removeRecall.Checked)","Cleanup now/privacy/schedule: $($cleanup.Checked)/$($privacyCleanup.Checked)/$($cleanupSchedule.Checked)","Sensors selected: $($deviceList.CheckedIndices.Count)","Applications selected: $($remove.Count)",'')
if($windowsContext.Edition-match'^Core' -and$unsupportedCount){$lines+='Windows Home does not support the managed-edition CSP policies below. Direct registry controls explicitly documented for Windows remain available; use the Home guide for the rest.';$lines+=''}
foreach($p in $PolicyPlan){$lines+="POLICY [$($p.Status)] $($p.Id)=$($p.Desired)"} foreach($p in $PolicyPlan){$lines+="POLICY [$($p.Status)] $($p.Id)=$($p.Desired)"}
if($recall.Checked){$rp=Get-WindRecallPlan -RemoveFeature:$removeRecall.Checked;foreach($item in $rp.Items){$lines+="RECALL [$($item.Status)] $($item.Action)"}} if($recall.Checked){$rp=Get-WindRecallPlan -RemoveFeature:$removeRecall.Checked;foreach($item in $rp.Items){$lines+="RECALL [$($item.Status)] $($item.Action)"}}
if($PolicyPlan.Count){$lines+=''} if($PolicyPlan.Count){$lines+=''}
@@ -97,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.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) $preset.SelectedItem=(@($presetCatalog.Presets|Where-Object {$_.Id -eq $installContext.RecommendedPreset})[0].Label)
$status=Add-WindLabel $form ("Ready. $($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) $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) $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)
@@ -127,9 +130,9 @@ if($recall.Checked-and($recallDisposition.Applicable-or$recallDisposition.Confli
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($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($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())})} 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 $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){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}}} $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 $failed=@($runResult.Entries | Where-Object { $_.Status -in @('Failed','SkippedDependency') });$statusLabel.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($statusLabel.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} }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() [void]$form.ShowDialog()
@@ -19,9 +19,9 @@
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>
<AssemblyName>ms-private</AssemblyName> <AssemblyName>ms-private</AssemblyName>
<RootNamespace>MsPrivate.Launcher</RootNamespace> <RootNamespace>MsPrivate.Launcher</RootNamespace>
<Version>0.0.1</Version> <Version>0.0.3</Version>
<AssemblyVersion>0.0.1.0</AssemblyVersion> <AssemblyVersion>0.0.3.0</AssemblyVersion>
<FileVersion>0.0.1.0</FileVersion> <FileVersion>0.0.3.0</FileVersion>
<Product>ms-private</Product> <Product>ms-private</Product>
<Description>Windows privacy control center</Description> <Description>Windows privacy control center</Description>
</PropertyGroup> </PropertyGroup>
+5 -5
View File
@@ -17,7 +17,7 @@ function Invoke-MsPrivateGroupPlan {
} }
$entries=@($Groups|ForEach-Object{[pscustomobject]@{Id=[string]$_.Id;Label=[string]$_.Label;DependsOn=@($_.DependsOn);Status='Pending';Error=$null}}) $entries=@($Groups|ForEach-Object{[pscustomobject]@{Id=[string]$_.Id;Label=[string]$_.Label;DependsOn=@($_.DependsOn);Status='Pending';Error=$null}})
$report=[pscustomobject]@{Schema=1;Kind='MsPrivateRun';CreatedUtc=[DateTime]::UtcNow.ToString('o');CompletedUtc=$null;Status='Running';Entries=$entries} $report=[pscustomobject]@{Schema=1;Kind='MsPrivateRun';CreatedUtc=[DateTime]::UtcNow.ToString('o');CompletedUtc=$null;Status='Running';Entries=$entries}
$save={if($ReportPath){Write-MsPrivateJsonAtomic -InputObject $report -Path $ReportPath -Depth 8}}.GetNewClosure();&$save if($ReportPath){Write-MsPrivateJsonAtomic -InputObject $report -Path $ReportPath -Depth 8}
foreach($group in $Groups){ foreach($group in $Groups){
$entry=@($entries | Where-Object { $_.Id -ceq [string]$group.Id })[0] $entry=@($entries | Where-Object { $_.Id -ceq [string]$group.Id })[0]
$blocked=@($entry.DependsOn | Where-Object { $blocked=@($entry.DependsOn | Where-Object {
@@ -25,14 +25,14 @@ function Invoke-MsPrivateGroupPlan {
$dependencyEntry=@($entries | Where-Object { $_.Id -ceq $dependency }) $dependencyEntry=@($entries | Where-Object { $_.Id -ceq $dependency })
$dependencyEntry.Count -ne 1 -or $dependencyEntry[0].Status -ne 'Succeeded' $dependencyEntry.Count -ne 1 -or $dependencyEntry[0].Status -ne 'Succeeded'
}) })
if($blocked.Count){$entry.Status='SkippedDependency';$entry.Error='Required group did not succeed: '+($blocked-join', ');&$save;continue} if($blocked.Count){$entry.Status='SkippedDependency';$entry.Error='Required group did not succeed: '+($blocked-join', ');if($ReportPath){Write-MsPrivateJsonAtomic -InputObject $report -Path $ReportPath -Depth 8};continue}
$entry.Status='Running';&$save;if($OnProgress){&$OnProgress $entry} $entry.Status='Running';if($ReportPath){Write-MsPrivateJsonAtomic -InputObject $report -Path $ReportPath -Depth 8};if($OnProgress){&$OnProgress $entry}
try{& $group.Action|Out-Host;$entry.Status='Succeeded'}catch{$entry.Status='Failed';$entry.Error=$_.Exception.Message} try{& $group.Action|Out-Host;$entry.Status='Succeeded'}catch{$entry.Status='Failed';$entry.Error=$_.Exception.Message}
&$save if($ReportPath){Write-MsPrivateJsonAtomic -InputObject $report -Path $ReportPath -Depth 8}
} }
$failed=@($entries | Where-Object { $_.Status -eq 'Failed' }).Count $failed=@($entries | Where-Object { $_.Status -eq 'Failed' }).Count
$skipped=@($entries | Where-Object { $_.Status -eq 'SkippedDependency' }).Count $skipped=@($entries | Where-Object { $_.Status -eq 'SkippedDependency' }).Count
$report.Status=if($failed-or$skipped){'PartialFailure'}else{'Completed'};$report.CompletedUtc=[DateTime]::UtcNow.ToString('o');&$save $report.Status=if($failed-or$skipped){'PartialFailure'}else{'Completed'};$report.CompletedUtc=[DateTime]::UtcNow.ToString('o');if($ReportPath){Write-MsPrivateJsonAtomic -InputObject $report -Path $ReportPath -Depth 8}
$report $report
} }
+10 -2
View File
@@ -30,6 +30,7 @@ function Get-WindCatalog {
if($registryPath-notmatch'^(SOFTWARE|SYSTEM)\\[A-Za-z0-9 _.-]+(?:\\[A-Za-z0-9 _.-]+)*$'-or$registryPath-match'(^|\\)\.\.?($|\\)'){throw "Invalid policy registry path: $id"} if($registryPath-notmatch'^(SOFTWARE|SYSTEM)\\[A-Za-z0-9 _.-]+(?:\\[A-Za-z0-9 _.-]+)*$'-or$registryPath-match'(^|\\)\.\.?($|\\)'){throw "Invalid policy registry path: $id"}
if($name-notmatch'^[A-Za-z0-9_]+$'-or$targets.ContainsKey($target)){throw "Invalid or duplicate policy registry target: $id"} if($name-notmatch'^[A-Za-z0-9_]+$'-or$targets.ContainsKey($target)){throw "Invalid or duplicate policy registry target: $id"}
foreach($profile in @('Standard','Strict')){$value=$item.$profile;if($null-eq$value){continue};if($value-isnot[ValueType]-or[double]$value-ne[int]$value-or[int]$value-lt0){throw "Invalid $profile policy value: $id"}} foreach($profile in @('Standard','Strict')){$value=$item.$profile;if($null-eq$value){continue};if($value-isnot[ValueType]-or[double]$value-ne[int]$value-or[int]$value-lt0){throw "Invalid $profile policy value: $id"}}
if($item.PSObject.Properties['HomeSupported']-and$item.HomeSupported-isnot[bool]){throw "HomeSupported must be a Boolean: $id"}
if($null-ne$item.EnterpriseValue-and($item.EnterpriseValue-isnot[ValueType]-or[double]$item.EnterpriseValue-ne[int]$item.EnterpriseValue-or[int]$item.EnterpriseValue-lt0)){throw "Invalid Enterprise policy value: $id"} if($null-ne$item.EnterpriseValue-and($item.EnterpriseValue-isnot[ValueType]-or[double]$item.EnterpriseValue-ne[int]$item.EnterpriseValue-or[int]$item.EnterpriseValue-lt0)){throw "Invalid Enterprise policy value: $id"}
if($null-eq$item.Standard-and$null-eq$item.Strict-and$null-eq$item.EnterpriseValue-and$id-notin@('updates-auto','updates-options')){throw "Policy has no selectable value: $id"} if($null-eq$item.Standard-and$null-eq$item.Strict-and$null-eq$item.EnterpriseValue-and$id-notin@('updates-auto','updates-options')){throw "Policy has no selectable value: $id"}
$source=$null;if(-not[uri]::TryCreate([string]$item.Source,[UriKind]::Absolute,[ref]$source)-or$source.Scheme-ne'https'){throw "Policy requires an HTTPS source: $id"} $source=$null;if(-not[uri]::TryCreate([string]$item.Source,[UriKind]::Absolute,[ref]$source)-or$source.Scheme-ne'https'){throw "Policy requires an HTTPS source: $id"}
@@ -116,6 +117,7 @@ function Get-WindPlan {
if ($catalog.Id -notcontains $item) { throw "Unknown policy ID: $item" } if ($catalog.Id -notcontains $item) { throw "Unknown policy ID: $item" }
} }
$supportedEdition = $Context.Edition -in @('Professional','ProfessionalN','ProfessionalEducation','ProfessionalEducationN','ProfessionalWorkstation','ProfessionalWorkstationN','Enterprise','EnterpriseN','Education','EducationN','EnterpriseS','EnterpriseSN') $supportedEdition = $Context.Edition -in @('Professional','ProfessionalN','ProfessionalEducation','ProfessionalEducationN','ProfessionalWorkstation','ProfessionalWorkstationN','Enterprise','EnterpriseN','Education','EducationN','EnterpriseS','EnterpriseSN')
$homeEdition = $Context.Edition -in @('Core','CoreN','CoreSingleLanguage','CoreCountrySpecific')
$supportedBuild = $Context.Build -in @(26100,26200) $supportedBuild = $Context.Build -in @(26100,26200)
foreach ($policy in $catalog) { foreach ($policy in $catalog) {
if ($Id.Count -gt 0 -and $Id -notcontains $policy.Id) { continue } if ($Id.Count -gt 0 -and $Id -notcontains $policy.Id) { continue }
@@ -138,9 +140,15 @@ function Get-WindPlan {
$status = 'Conflict'; $reason = 'Existing per-app exceptions need review; default deny would not cover all apps.' $status = 'Conflict'; $reason = 'Existing per-app exceptions need review; default deny would not cover all apps.'
} }
} }
if (-not $supportedEdition -or -not $supportedBuild) { $homeSupported=$policy.PSObject.Properties['HomeSupported']-and[bool]$policy.HomeSupported
if (-not $supportedBuild) {
$status = 'Unsupported' $status = 'Unsupported'
$reason = 'This edition/build is audit-only. Registry presence does not establish policy enforcement; use Guide.' $reason = 'This Windows build is not reviewed for policy application. Registry presence does not establish enforcement.'
} elseif (-not $supportedEdition -and-not($homeEdition-and$homeSupported)) {
$status = 'Unsupported'
$reason = if($homeEdition){'Microsoft documents this policy for managed Pro/Enterprise/Education editions, not Windows Home. Use the Home guide.'}else{'This Windows edition is not reviewed for policy application.'}
} elseif($homeEdition-and$homeSupported) {
$reason = 'Microsoft documents this direct registry control independently of the managed-edition CSP. Behavioral verification remains required on Home.'
} }
} catch { } catch {
$status = 'ReadError'; $reason = 'Could not read policy state. No protection can be inferred.' $status = 'ReadError'; $reason = 'Could not read policy state. No protection can be inferred.'
+4 -2
View File
@@ -126,7 +126,8 @@ function Get-WindSetupSnapshot {
$options=$Settings.AppOptions $options=$Settings.AppOptions
$sensors=@();if($Settings.DisableSensors){$sensors=@(Get-WindSensorInventory|Where-Object Status -eq 'OK')} $sensors=@();if($Settings.DisableSensors){$sensors=@(Get-WindSensorInventory|Where-Object Status -eq 'OK')}
$recallPlan=if($Settings.Recall){Get-WindRecallPlan -RemoveFeature:$Settings.RemoveRecall}else{$null} $recallPlan=if($Settings.Recall){Get-WindRecallPlan -RemoveFeature:$Settings.RemoveRecall}else{$null}
[pscustomobject]@{Apps=@(Get-WindAppPlan -Inventory $inventory @options);Policies=@(Get-WindPlan -Context (Get-WindContext) -Store (New-WindRegistryStore) -Profile $Settings.Profile -Updates $Settings.Updates);Sensors=$sensors;Recall=$recallPlan} $context=Get-WindContext
[pscustomobject]@{Context=$context;Apps=@(Get-WindAppPlan -Inventory $inventory @options);Policies=@(Get-WindPlan -Context $context -Store (New-WindRegistryStore) -Profile $Settings.Profile -Updates $Settings.Updates);Sensors=$sensors;Recall=$recallPlan}
} }
function Show-WindSetupReview { function Show-WindSetupReview {
param($Snapshot,$Settings) param($Snapshot,$Settings)
@@ -135,7 +136,8 @@ function Show-WindSetupReview {
foreach ($a in @($Snapshot.Apps | Where-Object Status -ne 'Keep')) {$items+=New-WindMenuItem $a.Id ("[$($a.Status)] $($a.Name)") ("$($a.Scope) scope. $($a.Reason). App data may be removed by the uninstaller.") $true} foreach ($a in @($Snapshot.Apps | Where-Object Status -ne 'Keep')) {$items+=New-WindMenuItem $a.Id ("[$($a.Status)] $($a.Name)") ("$($a.Scope) scope. $($a.Reason). App data may be removed by the uninstaller.") $true}
foreach($s in @($Snapshot.Sensors)){$items+=New-WindMenuItem ('sensor:'+ $s.InstanceId) ("[WouldDisable] $($s.Kind): $($s.Name)") $s.Risk $true} foreach($s in @($Snapshot.Sensors)){$items+=New-WindMenuItem ('sensor:'+ $s.InstanceId) ("[WouldDisable] $($s.Kind): $($s.Name)") $s.Risk $true}
if($Snapshot.Recall){foreach($r in $Snapshot.Recall.Items){$items+=New-WindMenuItem ('recall:'+ $r.Id) ("[$($r.Status)] $($r.Action)") $r.Impact $true}} if($Snapshot.Recall){foreach($r in $Snapshot.Recall.Items){$items+=New-WindMenuItem ('recall:'+ $r.Id) ("[$($r.Status)] $($r.Action)") $r.Impact $true}}
Select-WindMenu -Title 'Review your plan' -Subtitle ("$($Settings.Preset) | $($Settings.Profile) | Apps: $(@($Snapshot.Apps|Where-Object Status -eq 'Remove').Count) | Sensors: $(@($Snapshot.Sensors).Count) | Network: $($Settings.Network)") -Items $items -Searchable | Out-Null $available=@($Snapshot.Policies|Where-Object {$_.Status-in@('WouldChange','Configured')}).Count;$unsupported=@($Snapshot.Policies|Where-Object {$_.Status-eq'Unsupported'}).Count
Select-WindMenu -Title 'Review your plan' -Subtitle ("$($Snapshot.Context.Edition) $($Snapshot.Context.Build) | $available policies available / $unsupported unsupported | Apps: $(@($Snapshot.Apps|Where-Object Status -eq 'Remove').Count) | Network: $($Settings.Network)") -Items $items -Searchable | Out-Null
} }
function Set-WindSetupPreset { function Set-WindSetupPreset {
param($Settings,$Preset) param($Settings,$Preset)
+7
View File
@@ -70,6 +70,13 @@ $run=Invoke-MsPrivateGroupPlan -Groups $groups
Assert-True ($run.Status-eq'PartialFailure'-and($run.Entries | Where-Object { $_.Id -eq 'First' }).Status-eq'Failed') 'Operation runner did not record a failed group.' Assert-True ($run.Status-eq'PartialFailure'-and($run.Entries | Where-Object { $_.Id -eq 'First' }).Status-eq'Failed') 'Operation runner did not record a failed group.'
Assert-True (($run.Entries | Where-Object { $_.Id -eq 'Dependent' }).Status-eq'SkippedDependency'-and$events-notcontains'dependent') 'Operation runner executed a blocked dependency.' Assert-True (($run.Entries | Where-Object { $_.Id -eq 'Dependent' }).Status-eq'SkippedDependency'-and$events-notcontains'dependent') 'Operation runner executed a blocked dependency.'
Assert-True (($run.Entries | Where-Object { $_.Id -eq 'Independent' }).Status-eq'Succeeded'-and$events-contains'independent') 'Operation runner did not continue with an independent group.' Assert-True (($run.Entries | Where-Object { $_.Id -eq 'Independent' }).Status-eq'Succeeded'-and$events-contains'independent') 'Operation runner did not continue with an independent group.'
$runReportRoot=Join-Path ([IO.Path]::GetTempPath()) ('ms-private-run-report-'+[guid]::NewGuid().ToString('N'))
try{
$runReportPath=Join-Path $runReportRoot 'run.json'
$savedRun=Invoke-MsPrivateGroupPlan -Groups @([pscustomobject]@{Id='WriteReport';Label='Write report';DependsOn=@();Action={}}) -ReportPath $runReportPath
$savedRunDisk=Get-Content -LiteralPath $runReportPath -Raw|ConvertFrom-Json
Assert-True ($savedRun.Status-eq'Completed'-and$savedRunDisk.Status-eq'Completed'-and$savedRunDisk.Entries[0].Status-eq'Succeeded') 'Operation runner could not write and finalize a run report.'
}finally{if(Test-Path -LiteralPath $runReportRoot){Remove-Item -LiteralPath $runReportRoot -Recurse -Force}}
$calls=[Collections.Generic.List[string]]::new() $calls=[Collections.Generic.List[string]]::new()
$backend=@{ $backend=@{
+7 -5
View File
@@ -45,12 +45,14 @@ function New-Plan { param($Harness,[string[]]$Ids=@('advertising-id','clipboard-
function Apply-Test { param($Harness,$Plan) Invoke-WindTransaction -Plan $Plan -Context $Harness.Context -Store $Harness.Store -Save $Harness.Save -InstallationId 'test-installation' } function Apply-Test { param($Harness,$Plan) Invoke-WindTransaction -Plan $Plan -Context $Harness.Context -Store $Harness.Store -Save $Harness.Save -InstallationId 'test-installation' }
function Restore-Test { param($Harness,$Journal) Restore-WindTransaction -Journal $Journal -Store $Harness.Store -Save $Harness.Save -InstallationId 'test-installation' } function Restore-Test { param($Harness,$Journal) Restore-WindTransaction -Journal $Journal -Store $Harness.Store -Save $Harness.Save -InstallationId 'test-installation' }
Run-Test 'Home is unsupported even if a matching policy value exists' { Run-Test 'Home applies only explicitly documented direct registry controls' {
$h=New-Harness; $h.Context.Edition='Core' $h=New-Harness; $h.Context.Edition='Core'
$plan=New-Plan $h $plan=@(Get-WindPlan -Context $h.Context -Store $h.Store -Id @('advertising-id','implicit-text-collection','windows-error-reporting'))
Assert-True (@($plan | Where-Object Status -NE 'Unsupported').Count -eq 0) 'Home policies must not be called enforced.' Assert-True (($plan|Where-Object Id -EQ 'advertising-id').Status-eq'Unsupported') 'Managed-edition policy was offered on Home.'
Assert-Throws { Apply-Test $h $plan } $eligible=@($plan|Where-Object Status -EQ 'WouldChange')
Assert-True ($h.State.Writes -eq 0 -and $h.State.Saves -eq 0) 'Unsupported apply touched state.' Assert-True ($eligible.Id-contains'implicit-text-collection'-and$eligible.Id-contains'windows-error-reporting') 'Documented direct registry controls were not offered on Home.'
$j=Apply-Test $h $eligible
Assert-True ($j.Status-eq'Applied'-and$h.State.Writes-eq2) 'Home direct registry controls were not applied transactionally.'
} }
Run-Test 'Unknown build is audit-only' { Run-Test 'Unknown build is audit-only' {
$h=New-Harness; $h.Context.Build=99999 $h=New-Harness; $h.Context.Build=99999