This is the core integration that makes the EoP protection actually take effect:
the Editor now persists settings through PTSettingsSvc instead of writing
workspaces.json directly into the protected folder (which would be denied for a
non-elevated user).
WorkspacesEditorIO:
- ParseWorkspaces (read): GetBlob -> WorkspacesData.Deserialize, using the SAME
serializer/format as before (native Launcher stays compatible). NotFound ->
empty; Unavailable -> legacy %LocalAppData% file fallback (no-admin only, §10).
- SerializeWorkspaces (write): WorkspacesData.Serialize -> PutBlob; Unavailable ->
legacy file fallback. The transient snapshot->editor temp file stays direct IO.
- Fires SettingsBootstrapper.EnsureInitialized at editor open (the per-user
deferred-init / migration trigger the design called for).
FolderUtils.DataFolder() reverted to the user-writable %LocalAppData% working
folder. v6 had repointed it at the protected %ProgramData% store, which also
broke the Editor's icons and temp-project handoff (those need to stay
user-writable). The protected store is reached only through the service now.
Verified locally (harness compiling the real source files, run as an
allow-listed PowerToys.WorkspacesEditor.exe against the dev service) — 8/8:
Serialize->PutBlob Ok, GetBlob->Deserialize round-trips, dash-case JSON lands in
the protected %ProgramData%\\...\\blob.bin (owner NT SERVICE\\PTSettingsSvc,
user RX), non-elevated write+delete rejected.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drops every v5 launcher-gating element (Authenticode / MS publisher /
ProgramFiles path / empty-args triple-AND) and tackles the EoP from the
other end: same-user malware can no longer modify workspaces.json
because the file lives under a DACL that only the new PTWorkspacesSvc
service can write.
Components:
- WorkspacesSettingsService/ native C++ service exe (SCM + --console)
using NT SERVICE\PTWorkspacesSvc virtual
account; protocol/Protocol.h holds the
tiny wire format (5 opcodes).
- WorkspacesSettingsClient/ native static lib used by Editor /
SnapshotTool / smoke test.
- WorkspacesCsharpLibrary/
SettingsService/ managed mirror: client, paths, one-shot
legacy migration helper.
- WorkspacesSettingsService.wxs ServiceInstall (virtual account, demand
start, restart-on-failure) +
ServiceControl + CreateFolder with
protected DACL on the data root.
Caller authentication (no signatures, by design):
1. ImpersonateNamedPipeClient -> token must be a real interactive user
(rejects SYSTEM / SERVICE / Anonymous).
2. GetNamedPipeClientProcessId -> caller image path must be under the
MSI-recorded PT install folder.
3. Image filename must match a tiny allow-list (Editor / SnapshotTool /
runner). The launcher is *not* on the list; it only reads, and
reads via the user's R+X grant on the file's DACL.
End-to-end verified on dev box:
- Smoke test from arbitrary folder -> AuthRejected (allow-list works).
- Smoke test renamed + located under a PT_DEV_INSTALL_FOLDER override ->
Ping=Ok, GetSettings=Ok (empty for first-time user).
- PutSettings completes against the service exe but errors on the DACL
apply because the NT SERVICE\PTWorkspacesSvc account only exists after
CreateService runs -- confirms the DACL is in fact being applied
against the right principal.
See Workspaces-EoP-Fix/Design-v6-Prototype-Notes.md for the threat model,
wire protocol, reproduction steps, and the list of known gaps before
this can graduate from prototype.
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx
<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments
<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
Installer built, and every command works as expected,
Now use sparse app deployment, so we don't need an extra msix
---------
Co-authored-by: kaitao-ms <kaitao1105@gmail.com>