Skip to content

Block admin uninstall of user package#6345

Open
JohnMcPMS wants to merge 3 commits into
microsoft:masterfrom
JohnMcPMS:uninstall-scope-check
Open

Block admin uninstall of user package#6345
JohnMcPMS wants to merge 3 commits into
microsoft:masterfrom
JohnMcPMS:uninstall-scope-check

Conversation

@JohnMcPMS

@JohnMcPMS JohnMcPMS commented Jul 3, 2026

Copy link
Copy Markdown
Member

📖 Description

Block admin uninstall of user package.

Microsoft Reviewers: Open in CodeFlow

@JohnMcPMS
JohnMcPMS requested a review from a team as a code owner July 3, 2026 00:21
florelis
florelis previously approved these changes Jul 3, 2026
@Trenly

Trenly commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

I understand this is for security reasons, but it does make me wonder about the implications. Theres a few scenarios where applications installed elevated still write to user scoped registry keys, but require elevation to fully uninstall because they wrote their data to Program Files instead of a location accessible by a non-elevated context.

In a similar vein, many everyday users have a misconception that anything they can do non-elevated, they should also be able to do elevated. If a user can uninstall a package to the user scope while not elevated, the likely expect to be able to uninstall it elevated too. Granted the messaging and error will help address that case.

Or, if a user can install a package to the user scope while elevated, then why is removing it from the machine scope while elevated prohibited? Which brings the question - why is this this limited only to uninstall?


Nothing about these comments should block the PR, would just like some additional perspective from the team and/or @denelon on this, since such a strong move seems like it will have a negative impact on the users of WinGet, and may even be breaking for some packages

@JohnMcPMS

Copy link
Copy Markdown
Member Author

Or, if a user can install a package to the user scope while elevated, then why is removing it from the machine scope while elevated prohibited? Which brings the question - why is this this limited only to uninstall?

It isn't, repair already has the same check. Do you mean why is installed allowed for instance? Writing isn't an issue, reading is.

The better solution long term would be to write code to launch an unelevated process, then have these become invoking that path.

florelis
florelis previously approved these changes Jul 15, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates WinGet’s handling of “admin-context operations on user-scope packages” by generalizing the existing “admin repair prohibited” return code into an “admin action prohibited” code, and by adding a new uninstall-time guard to block elevated (TokenElevationTypeFull) uninstalls of user-scope packages for certain installer technologies.

Changes:

  • Renames the HRESULT/return-code identity from ...ADMIN_CONTEXT_REPAIR_PROHIBITED to ...ADMIN_CONTEXT_ACTION_PROHIBITED, updating CLI/COM/PowerShell surfaces, docs, and E2E constants accordingly.
  • Adds Runtime::IsRunningWithNonDefaultFullToken() and uses it to gate “admin-context prohibited” behavior for repair and the new uninstall block.
  • Adds uninstall blocking logic for elevated admin context + user-scope packages (for selected installer types), including a new user-facing resource string.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetRepairPackageException.cs Updates exception mapping to the renamed “admin action prohibited” error code.
src/PowerShell/Microsoft.WinGet.Client.Engine/Common/ErrorCode.cs Renames the PowerShell engine constant to AdminContextActionProhibited (same numeric value).
src/Microsoft.Management.Deployment/Converters.h Updates WinRT status mapping for the new error code, including Uninstall/Repair-specific statuses.
src/AppInstallerSharedLib/Runtime.cpp Adds IsRunningWithNonDefaultFullToken() implementation based on TOKEN_ELEVATION_TYPE.
src/AppInstallerSharedLib/Public/winget/Runtime.h Exposes IsRunningWithNonDefaultFullToken() with documentation about when it returns true.
src/AppInstallerSharedLib/Public/AppInstallerErrors.h Renames the public HRESULT define to APPINSTALLER_CLI_ERROR_ADMIN_CONTEXT_ACTION_PROHIBITED.
src/AppInstallerSharedLib/Errors.cpp Updates HRESULT info text to a generalized “requested operation not permitted…” message.
src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw Renames the error string key and adds a new uninstall-specific user message string.
src/AppInstallerCLIE2ETests/RepairCommand.cs Updates expected exit code constant name for the admin-context repair test.
src/AppInstallerCLIE2ETests/Interop/RepairInterop.cs Updates expected error code constant name for the repair interop test.
src/AppInstallerCLIE2ETests/Constants.cs Renames the E2E constant to ERROR_ADMIN_CONTEXT_ACTION_PROHIBITED (same numeric value).
src/AppInstallerCLICore/Workflows/UninstallFlow.cpp Adds elevated-admin uninstall blocking for user-scope packages (selected installer types) and reuses cached metadata.
src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp Switches repair blocking from IsRunningAsAdmin() to IsRunningWithNonDefaultFullToken() and updates HRESULT used.
src/AppInstallerCLICore/Resources.h Adds resource string ID for NoAdminUninstallForUserScopePackage.
doc/windows/package-manager/winget/returnCodes.md Updates documentation for the renamed return code and message.

Comment on lines +235 to +239
// When running as admin, block attempt to uninstall user scope package to prevent elevation of privilege paths.
if (AdminExecutionShouldBlockUserScopePackages(installedType) && Runtime::IsRunningWithNonDefaultFullToken())
{
auto scopeEnum = ConvertToScopeEnum(packageMetadata[PackageVersionMetadata::InstalledScope]);
if (scopeEnum == ScopeEnum::User)
@JohnMcPMS

Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

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.

4 participants