Skip to content

Stop leaving Appx sideloading policy permanently enabled - #5

Merged
TecharyJames merged 1 commit into
BETAfrom
fix/restore-sideload-policy
Sep 19, 2026
Merged

TecharyJames merged 1 commit into
BETAfrom
fix/restore-sideload-policy

Conversation

@TecharyJames

@TecharyJames TecharyJames commented Sep 19, 2026 •

Copy link
Copy Markdown
Member

Branches from BETA. Independent of #2, #3 and #4, and safe to merge in any order.

The problem

Installing any MSIX through the module wrote AllowAllTrustedApps=1 to both HKLM\SOFTWARE\Policies\Microsoft\Windows\Appx and AppModelUnlock, and never put them back.

Every machine that has installed an MSIX through TecharyGet is left with sideloading permanently enabled, including machines where that policy had been explicitly set to disabled, which the old code silently overwrote. It also did this unconditionally, before establishing whether it was needed — a correctly signed package usually provisions with no policy change at all.

What changed

  1. Provision first, as the machine is configured. Touch nothing.
  2. Only if that is refused, relax the policy.
  3. Restore in a finally, covering the success path, the per-user fallback and the throw.

Restoration is exact rather than "set it back to 0". The prior state of each value is recorded first, then put back:

Prior state After restore
Key absent Key removed entirely
Key present, value absent Only the value removed, key kept
AllowAllTrustedApps=0 Comes back as 0, not deleted

An unrelated value in the same key is left untouched.

Two design points

Push-SideloadPolicy never throws. If it did, the state describing what it had already changed would be lost with it, the caller's finally would have nothing to restore from, and the policy would stay relaxed. Failures are returned through the result object instead.

Pop-SideloadPolicy indexes its list rather than wrapping it in @(). On PowerShell 7.6 / .NET 10, @() over a List[object] throws Argument types do not match, which aborted the restore mid-way and left the policy relaxed. Caught by the round-trip test below. List[string] is unaffected; this is specific to List[object].

Verification

Round trip against a scratch HKCU key covering all three prior states:

WHILE RELAXED:
  NoKey          value=1
  KeyNoValue     value=1
  KeyWithValue   value=1

AFTER RESTORE:
  A no key         key absent                   PASS
  B key,no value   key present, value absent    PASS
  C key,value=0    value=0                      PASS
  B unrelated      Unrelated=42                 PASS untouched

ROUND TRIP: CLEAN

The registry paths are parameters so the round trip is testable without modifying live machine policy.

Coverage limitation: this fixes the leak going forward. Machines already left in the relaxed state by previous runs are not cleaned up by this change.

Installing any MSIX through the module wrote AllowAllTrustedApps=1 to both
HKLM\SOFTWARE\Policies\Microsoft\Windows\Appx and AppModelUnlock, and never
put them back. Every machine that ever installed an MSIX was left with
sideloading permanently enabled, including machines where the policy had
previously been explicitly disabled.

It also did this unconditionally, before finding out whether it was needed.
A correctly signed package usually provisions with no policy change at all.

Provisioning is now attempted as the machine is configured. Only if that is
refused is the policy relaxed, and it is restored in a finally block that
runs on the success path, the per-user fallback path, and the throw.

Restoration is exact rather than "set it back to 0": the prior state of each
value is recorded first, and the restore puts back the previous value if
there was one, removes just the value if the key existed without it, or
removes the key entirely if we created it. A pre-existing
AllowAllTrustedApps=0 therefore comes back as 0, not as a deleted value.

Push-SideloadPolicy deliberately never throws. If it did, the state
describing what had already been changed would be lost with it, and the
caller's finally block would have nothing to restore from, leaving the
policy relaxed. Failures are reported through the returned object instead.

Pop-SideloadPolicy indexes its list directly rather than wrapping it in @().
On PowerShell 7.6 / .NET 10, @() over a List[object] throws "Argument types
do not match", which aborted the restore and left the policy relaxed. That
was caught by the round-trip test below and is the exact failure this change
exists to prevent.

Verified against a scratch HKCU key across all three prior states: key
absent, key present without the value, and key present with
AllowAllTrustedApps=0. All three read 1 while relaxed and are byte-identical
to their original state afterwards, with an unrelated value in the same key
left untouched.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant