Block admin uninstall of user package#6345
Conversation
|
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 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 |
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. |
There was a problem hiding this comment.
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_PROHIBITEDto...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. |
| // 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) |
|
/azp run |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
📖 Description
Block admin uninstall of user package.
Microsoft Reviewers: Open in CodeFlow