Add package policy editor - #5335
Marc-André Moreau (mamoreau-devolutions) merged 68 commits into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Moderate correctness, performance, contract-boundary, and post-commit protocol issues remain unresolved.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds a Windows-only Agent policy editor with structured/raw editing, validation, stale-token handling, and secure elevated writes.
Changes:
- Adds policy-management UI, lifecycle guards, localization, and search integration.
- Introduces authenticated NativeAOT elevation and bounded IPC.
- Expands packaging, build automation, and test coverage.
File summaries
| File | Description |
|---|---|
UniGetUI.iss |
Handles helper installation cleanup. |
src/UniGetUI.Windows.slnx |
Adds the elevated-helper project. |
src/UniGetUI.Tests/SettingsSearchIndexTests.cs |
Tests policy search indexing. |
src/UniGetUI.Tests/PolicyEditor/PolicyRuleOperationsTests.cs |
Tests rule mutations. |
src/UniGetUI.Tests/PolicyEditor/PolicyEditorViewModelFakes.cs |
Provides editor test doubles. |
src/UniGetUI.Tests/PolicyEditor/PolicyEditorTestFixtures.cs |
Provides policy test fixtures. |
src/UniGetUI.Tests/PolicyEditor/PolicyEditorTemplatesTests.cs |
Tests draft templates. |
src/UniGetUI.Tests/PolicyEditor/PolicyEditorSessionCloseGuardTests.cs |
Tests dirty-session protection. |
src/UniGetUI.Tests/PolicyEditor/PolicyEditorRetryResolverTests.cs |
Tests conflict retry decisions. |
src/UniGetUI.Tests/PolicyEditor/PolicyEditorLocalizationTests.cs |
Tests localization-key coverage. |
src/UniGetUI.Tests/PolicyEditor/PolicyEditorFindingIndexTests.cs |
Tests bounded finding indexing. |
src/UniGetUI.Tests/PolicyEditor/PolicyEditorDraftFingerprintTests.cs |
Tests canonical fingerprints. |
src/UniGetUI.Tests/AgentPolicyInspectorViewModelTests.cs |
Tests policy-management states. |
src/UniGetUI.PackageEngine.Tests/PolicyWriteElevation/PolicyElevationProtocolTests.cs |
Tests elevation protocol constants. |
src/UniGetUI.PackageEngine.Tests/PolicyWriteElevation/PolicyElevationPathsTests.cs |
Tests authenticated paths. |
src/UniGetUI.PackageEngine.Tests/PolicyWriteElevation/PolicyElevationLaunchArgumentsTests.cs |
Tests launch-argument validation. |
src/UniGetUI.PackageEngine.Tests/PolicyWriteElevation/PolicyElevationHelperLocatorTests.cs |
Tests helper discovery. |
src/UniGetUI.PackageEngine.Tests/PolicyWriteElevation/PolicyElevationFrameTests.cs |
Tests bounded framing. |
src/UniGetUI.PackageEngine.Tests/PackageOperationsTests.cs |
Updates broker-operation regressions. |
src/UniGetUI.PackageEngine.Tests/BrokerPolicyInspectorTests.cs |
Tests policy validation. |
src/UniGetUI.PackageEngine.AgentBroker/UniGetUI.PackageEngine.AgentBroker.csproj |
Updates policy dependencies. |
src/UniGetUI.PackageEngine.AgentBroker/PolicyWriteElevation/Protocol/PolicyElevationPaths.cs |
Defines protocol paths. |
src/UniGetUI.PackageEngine.AgentBroker/PolicyWriteElevation/Protocol/PolicyElevationMessages.cs |
Defines IPC messages. |
src/UniGetUI.PackageEngine.AgentBroker/PolicyWriteElevation/Protocol/PolicyElevationJsonContext.cs |
Supplies generated JSON metadata. |
src/UniGetUI.PackageEngine.AgentBroker/PolicyWriteElevation/Protocol/PolicyElevationEnumConverters.cs |
Serializes protocol enums. |
src/UniGetUI.PackageEngine.AgentBroker/PolicyWriteElevation/Protocol/PolicyElevationArguments.cs |
Parses helper arguments. |
src/UniGetUI.PackageEngine.AgentBroker/PolicyWriteElevation/PolicyElevationPipeServer.cs |
Creates secured IPC pipes. |
src/UniGetUI.PackageEngine.AgentBroker/PolicyWriteElevation/PolicyElevationOutcome.cs |
Models elevation outcomes. |
src/UniGetUI.PackageEngine.AgentBroker/PolicyWriteElevation/PolicyElevationHelperLocator.cs |
Locates the packaged helper. |
src/UniGetUI.PackageEngine.AgentBroker/PolicyWriteElevation/Interop/WindowsProcessInspector.cs |
Inspects process identity. |
src/UniGetUI.PackageEngine.AgentBroker/PolicyWriteElevation/Interop/WindowsPeerAuthenticator.cs |
Authenticates IPC peers. |
src/UniGetUI.PackageEngine.AgentBroker/PolicyWriteElevation/Interop/WindowsAuthenticodeTrustVerifier.cs |
Verifies executable signatures. |
src/UniGetUI.PackageEngine.AgentBroker/PolicyWriteElevation/Interop/PolicyElevationTrustPolicy.cs |
Defines signer trust decisions. |
src/UniGetUI.PackageEngine.AgentBroker/PolicyWriteElevation/Interop/PolicyElevationAccessPolicy.cs |
Defines elevation access checks. |
src/UniGetUI.PackageEngine.AgentBroker/PolicyManagement/BrokerPolicyManagementModels.cs |
Defines management outcomes and limits. |
src/UniGetUI.PackageEngine.AgentBroker/BrokerPolicyInspector.cs |
Strengthens policy validation. |
src/UniGetUI.Avalonia/Views/SoftwarePages/Interfaces/PageInterfaces.cs |
Defines asynchronous leave guards. |
src/UniGetUI.Avalonia/Views/Pages/SettingsPages/SettingsBasePage.axaml.cs |
Guards settings navigation. |
src/UniGetUI.Avalonia/Views/Pages/SettingsPages/PolicyEditor/PolicyEditorDialog.axaml.cs |
Handles editor dialog interactions. |
src/UniGetUI.Avalonia/Views/Pages/SettingsPages/PolicyEditor/PolicyEditorConfirmationPrompt.cs |
Presents save confirmations. |
src/UniGetUI.Avalonia/Views/Pages/SettingsPages/AgentPolicyInspector.axaml.cs |
Opens editor dialogs. |
src/UniGetUI.Avalonia/Views/Pages/SettingsPages/AgentPolicyInspector.axaml |
Adds policy-management controls. |
src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs |
Guards window closure. |
src/UniGetUI.Avalonia/Views/Controls/PolicyJsonEditor.cs |
Provides the raw JSON editor. |
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyRuleOperations.cs |
Implements rule-list mutations. |
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorTemplates.cs |
Creates operation-specific drafts. |
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorSessionCloseGuard.cs |
Protects unsaved changes. |
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorRetry.cs |
Resolves stale-token retries. |
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorRawSyntax.cs |
Implements strict raw parsing. |
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorProductionAdapters.cs |
Connects UI services to Agent APIs. |
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorPolicyContract.cs |
Centralizes policy contract values. |
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorEnums.cs |
Defines editor states and operations. |
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorDependencies.cs |
Defines editor service seams. |
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyDraftFingerprint.cs |
Computes canonical draft fingerprints. |
src/UniGetUI.Avalonia/UniGetUI.Avalonia.csproj |
Publishes and stages the helper. |
src/UniGetUI.Avalonia/Infrastructure/SettingsSearchIndex.cs |
Adds policy-management search actions. |
src/UniGetUI.AgentPolicy.ElevatedHelper/UniGetUI.AgentPolicy.ElevatedHelper.csproj |
Configures NativeAOT publication. |
src/UniGetUI.AgentPolicy.ElevatedHelper/PolicyReplacementExecutor.cs |
Executes elevated replacements. |
src/UniGetUI.AgentPolicy.ElevatedHelper/app.manifest |
Requires administrator elevation. |
scripts/build.ps1 |
Builds and stages the helper. |
.github/workflows/build-release.yml |
Signs, verifies, and packages the helper. |
Review details
Suppressed comments (8)
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorDialogViewModel.cs:182
- This displays parser-generated English text verbatim.
PolicyEditorRawSyntaxsupplies hard-coded/interpolated messages and rawJsonException.Messagevalues, so non-English users see untranslated error details despite neighboring status text being localized. Represent syntax failures with localizable kinds/arguments and translate a bounded message here.
syntaxError.Message,
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorDialogViewModel.cs:181
SyntaxErroralso represents valid JSON that violates the draft contract (wrong schema, policy type, or fixed precedence), so this title incorrectly tells users those documents are not JSON. Use a title such as “The document is not a valid policy draft,” or distinguish JSON parse errors from contract errors.
CoreTools.Translate("The document is not valid JSON"),
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorRawSyntax.cs:113
- The raw document uses the PascalCase
Enforcementproperty, so this case-sensitive JSON Pointer cannot identify the missing block in the editor. Use/Enforcement.
error = new PolicyEditorSyntaxError("Missing enforcement block.", "/enforcement");
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorRawSyntax.cs:121
- Canonical raw JSON names this path
Enforcement/RulePrecedence. JSON Pointer matching is case-sensitive, so the lowercase pointer cannot locate or highlight the rejected field.
"/enforcement/rulePrecedence");
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorRawSyntax.cs:127
- The expected raw property is
Metadata; JSON Pointer member names are case-sensitive. Use/Metadataso this error points to the property the editor accepts/emits.
error = new PolicyEditorSyntaxError("Missing metadata block.", "/metadata");
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorRawSyntax.cs:9
Messageis populated with hard-coded English strings and raw runtime exception messages, then bound directly to the dialog. Non-English users therefore receive untranslated, runtime-dependent syntax details, and translation-source tooling cannot discover them. Carry a stable syntax-error kind plus bounded arguments/pointer and localize it at the presentation layer with literal translation keys instead of exposingException.Message.
public sealed record PolicyEditorSyntaxError(string Message, string Pointer);
src/UniGetUI.Avalonia/Views/Pages/SettingsPages/PolicyEditor/PolicyEditorConfirmationPrompt.cs:113
- This count is computed from the bounded display list, whose final item is a synthetic warning whenever findings are omitted. It can therefore overcount (omitted errors) or severely undercount (omitted warnings) the warnings being acknowledged. Carry the authoritative warning count separately from the bounded findings and display that value.
src/UniGetUI.Tests/PolicyEditor/PolicyEditorLocalizationTests.cs:108 - This regex only discovers literal
CoreTools.Translate("...")calls, but the changed editor also translates dynamic enum/severity/argument labels (for exampleCoreTools.Translate(Severity.ToString())andCoreTools.Translate(label)). Removing one of those keys fromlang_en.jsonwould still leave this test green, so the claimed policy-editor localization coverage is incomplete. Enumerate those bounded dynamic key sets explicitly or refactor them to stable literal translation keys that this check can validate.
- Files reviewed: 91/91 changed files
- Comments generated: 4
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
0418102 to
2715e3f
Compare
There was a problem hiding this comment.
🟡 Changes recommended
A critical policy-rule behavior and multiple unresolved correctness, accessibility, lifecycle, and dependency issues block approval.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 84/88 changed files
- Comments generated: 9
- Review effort level: Balanced
There was a problem hiding this comment.
🔵 Needs a closer look
Moderate issues remain in conflict messaging, UI-thread dirty-state computation, and structured description round-tripping.
Review details
Suppressed comments (6)
Previously missed (3) — in code that hasn't changed since the last review.
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorDialogViewModel.cs:212
- A stale-token response sets both
LastWriteFailureKindandSession.HasConflict, so this earlier branch always wins and the conflict-specific “Review … then choose Overwrite” status below is unreachable. Users see only a generic broker rejection even though the UI is waiting for an exact-token overwrite decision. PrioritizeHasConflictbefore the generic write-failure branch.
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorSession.cs:68 IsDirtysynchronously serializes and compares the entire structured draft on every getter call. Every structured field edit raisesIsDirty, so typing can repeatedly serialize a policy up to the 16 MiB request limit on the UI thread; cache/debounce the structured fingerprint or track dirty generations and perform the full comparison off the input path.
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorStructuredUi.cs:175- This silently converts schema-valid empty or whitespace-only descriptions to
null. That changes user-authored policy data when editing in structured mode, so switching modes or saving no longer round-trips the draft exactly; preserve the value and let authoritative validation handle its length constraints.
This issue also appears in the following locations of the same file:
- line 425
- line 477
- line 483
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorStructuredUi.cs:425
- This silently converts schema-valid empty or whitespace-only rule reasons to
null. Structured editing therefore mutates valid policy content instead of preserving the exact reason; assign the nullable value directly and leave validation to the Agent.
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorStructuredUi.cs:477 - Whitespace is nonempty policy text, but this setter silently converts it to an omitted lower bound instead of submitting the exact value for authoritative validation. Only an empty textbox should clear
MinVersion.
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorStructuredUi.cs:483 - Like
MinVersion, a whitespace-only upper bound is silently rewritten tonull. Preserve nonempty input so structured editing does not change the draft before the Agent validates it.
- Files reviewed: 83/89 changed files
- Comments generated: 0 new
- Review effort level: Balanced
|
Resolved every concern from review #5335 (review) at exact head
Additional race hardening rechecks raw-mode edits after an in-flight authenticated save against the new authoritative baseline and clears stale |
|
Copilot review |
There was a problem hiding this comment.
🟡 Changes recommended
Three moderate issues remain in publisher handling, finding severity mapping, and restart cancellation behavior.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (3)
Previously missed (2) — in code that hasn't changed since the last review.
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorTemplates.cs:26
Publisheris a schema-valid required string even when it contains only whitespace (the existing preservation regression covers this atAgentPolicyInspectorViewModelTests.cs:39-64).ReplaceIdentity()passes the active policy's publisher into this method, so such a valid policy throws here and the editor never opens. Reject only null/empty publishers, and update the whitespace test expectation accordingly.
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyValidationFinding.cs:30Infofindings fall through toWarninghere. Warning findings require acknowledgement before saving (PolicyEditorSession.cs:689-696), so an informational Agent result is incorrectly shown as a warning and adds a confirmation step. Preserve all three severities explicitly.
This issue also appears on line 39 of the same file.
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyValidationFinding.cs:41
- The sanitized-finding path also maps
InfotoWarning. These findings feed the same warning acknowledgement logic, so informational results from the bounded broker adapter unnecessarily block saving behind a warning confirmation. Preserve the informational severity.
- Files reviewed: 82/89 changed files
- Comments generated: 1
- Review effort level: Balanced
|
Review 5113628557 disposition at exact head
Validation: 260 focused tests; full Windows solution including 306 Avalonia and 493/727 PackageEngine tests; Windows x64 and forced-portable builds; fresh metadata-free app/helper NativeAOT; 79-file integrity verification; 59 translation catalogs. The PR remains draft. |
There was a problem hiding this comment.
🔵 Needs a closer look
Privileged-write complexity remains high, with unresolved moderate enum-mapping and metadata-preservation issues.
Review details
Suppressed comments (2)
Previously missed (2) — in code that hasn't changed since the last review.
src/UniGetUI.AgentPolicy.ElevatedHelper/PolicyReplacementExecutor.cs:32
- These casts couple the privileged operation to enum ordinals even though the elevation protocol defines independent wire enums and the contract test establishes name-based equivalence. A future shared-contract reorder or insertion would pass that test but execute a different replacement operation; map each value explicitly so unsupported values fail closed.
src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/PolicyEditor/PolicyEditorStructuredUi.cs:203 - This setter silently converts a schema-valid whitespace-only support URL to
null, so editing and saving an existing policy can lose user-authored metadata. Other free-form metadata and list fields preserve whitespace exactly; only an empty string should represent an omitted optional URL.
- Files reviewed: 84/92 changed files
- Comments generated: 0 new
- Review effort level: Balanced
|
Resolved both suppressed concerns from review #5335 (review) at exact head
The CI whitespace annotations were also corrected with the repository formatter. Full exact-head tests/builds, official NuGet.org-only package hashes, fresh app/helper NativeAOT metadata checks, 79-file integrity validation, and 59 translation catalogs pass. The PR remains draft. |
There was a problem hiding this comment.
🟡 Changes recommended
Two moderate issues remain in replacement-request size preflight and multiline localization-key scanning.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
src/UniGetUI.Tests/PolicyEditor/PolicyEditorLocalizationTests.cs:125
- This regex requires the string literal to start immediately after
Translate(, so existing multiline calls such asCoreTools.Translate(\n "…")are omitted from the source scan. The localization test can therefore pass even when a multiline translation key is missing from the catalogs; allow whitespace before the opening quote.
- Files reviewed: 86/94 changed files
- Comments generated: 1
- Review effort level: Balanced
|
Also fixed the suppressed localization-test finding from review 5114529346 in 9862195: the literal |
|
Copilot review |
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
d4b1a61 to
643bc5e
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Local validation blocks valid catch-all policies, and both shipping projects use dependency versions not covered by the PR’s provenance evidence.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (1)
src/UniGetUI.PackageEngine.AgentBroker/UniGetUI.PackageEngine.AgentBroker.csproj:13
- These shipping dependencies are pinned to
2026.9.17, but the PR description and validation evidence attest only2026.9.15and provide hashes for that version. Dependency provenance is therefore not reproducible from the stated evidence. Either restore the attested version or update the provenance hashes and rerun/update the validation claims for2026.9.17.
- Files reviewed: 90/128 changed files
- Comments generated: 2
- Review effort level: Balanced
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The privileged NativeAOT helper, authenticated IPC, Windows trust verification, and broad editor lifecycle changes warrant final human security review despite strong test coverage.
Review details
- Files reviewed: 89/129 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1f832f0
into
main
GET /v1/policy/managementsnapshot for the page, with correct transport-unavailable versus malformed-response classification.ReplacePolicy; its Agent connection verifies the kernel-reported server process, protected executable path, signer, and identification-only impersonation before transmitting policy data.main.