feat(agent): add package policy management - #1975
feat(agent): add package policy management#1975Benoît Cortier (CBenoit) wants to merge 8 commits into
Conversation
|
Implementation notes:
Note Human-tuned, LLM-assisted content. |
There was a problem hiding this comment.
🟡 Changes recommended
The Windows policy end-to-end check fails because the new test directory is classified as an invalid policy path.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds the package-policy management layer extracted from #1963.
Changes:
- Adds authenticated management, validation, and replacement endpoints.
- Introduces serialized storage, HMAC receipts, monitoring, and path security checks.
- Expands Windows end-to-end coverage.
File summaries
| File | Description |
|---|---|
Cargo.lock |
Locks the HMAC dependency. |
crates/agent-policy-tester/src/windows.rs |
Expands policy-management integration tests. |
crates/now-package-broker/Cargo.toml |
Adds HMAC and Win32 globalization support. |
crates/now-package-broker/src/auth.rs |
Captures client elevation and administrator membership. |
crates/now-package-broker/src/lib.rs |
Exposes the policy-store module. |
crates/now-package-broker/src/policy_security.rs |
Adds policy-path and ACL validation. |
crates/now-package-broker/src/policy_store/mod.rs |
Implements policy state, concurrency, and persistence. |
crates/now-package-broker/src/policy_store/receipt.rs |
Implements validation receipts and store tests. |
crates/now-package-broker/src/policy_store/validation.rs |
Implements authoritative draft validation. |
crates/now-package-broker/src/policy_watcher.rs |
Integrates fail-closed store reloading. |
crates/now-package-broker/src/server/mod.rs |
Exposes management API routes and authorization. |
crates/now-package-broker/src/task.rs |
Initializes and monitors the policy store. |
crates/win-api-wrappers/src/token.rs |
Adds token group-membership checks. |
Review details
- Files reviewed: 12/13 changed files
- Comments generated: 1
- Review effort level: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
🟡 Changes recommended
Policy writes currently authorize the process primary token instead of the connected named-pipe client token.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 12/13 changed files
- Comments generated: 1
- Review effort level: Balanced
There was a problem hiding this comment.
🟡 Changes recommended
Validation truncation incorrectly rejects warnings-only policies, and several management diagnostics diverge from the shared API contract.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
crates/now-package-broker/src/policy_store/validation.rs:721
- These generated finding paths can name fields that do not exist in the submitted draft. For example, an explicit
Constraintsobject produces/Constraints/SkipHashCheckand/Constraints/PreRelease, although the actual fields areAllowSkipHashCheckandAllowPreRelease; omitted defaulted fields are also addressed as if present. The API definesPathas a JSON Pointer into the submitted draft, and the shared fixture points an explicit skip-hash warning at/Rules/0/Match/SkipHashCheck. Build the path from the actual match/constraint member that triggered the warning, or use the containing rule when no concrete member exists.
let path = if rule.constraints.is_some() {
format!("/Rules/{index}/Constraints/{option}")
} else {
format!("/Rules/{index}")
};
- Files reviewed: 12/13 changed files
- Comments generated: 3
- Review effort level: Balanced
|
Review follow-up for the suppressed sensitive-warning pointer observation:
Note Human-tuned, LLM-assisted content. |
There was a problem hiding this comment.
🔵 Needs a closer look
Equal validity timestamps are incorrectly accepted despite the shared contract requiring ValidUntil to be later than ValidFrom.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
crates/now-package-broker/src/policy_store/validation.rs:397
- Equal
ValidFrom/ValidUntiltimestamps currently pass validation, even though the shared contract defines this finding as requiringValidUntilto be afterValidFrom. A zero-length validity interval can therefore receive a receipt and be committed. Reject equality as well and add it to the interval regression test.
- Files reviewed: 12/13 changed files
- Comments generated: 0 new
- Review effort level: Balanced
|
Disposition for the suppressed validity-interval observation in review Fixed in Note Human-tuned, LLM-assisted content. |
There was a problem hiding this comment.
🔵 Needs a closer look
The exported receipt-less validator can produce a validation result that the shared API contract refuses to serialize.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
crates/now-package-broker/src/policy_store/validation.rs:71
- This public function returns a
PolicyValidationResultwithIsValid = truebut noValidationReceipt. The shared API type rejects serialization of that combination, so callers of the exportedpolicy_store::validation::validate_draftAPI receive a value they cannot put on the wire. Keep this receipt-less validator internal and expose onlyPolicyStore::validate_draft, which adds the receipt.
- Files reviewed: 12/13 changed files
- Comments generated: 0 new
- Review effort level: Balanced
|
Disposition for the suppressed receipt-less validator observation in review Fixed in Note Human-tuned, LLM-assisted content. |
There was a problem hiding this comment.
🔵 Needs a closer look
Draft validation currently accepts duplicate values that violate the official policy schema, and management requests perform redundant signature verification.
Review details
Suppressed comments (2)
Previously missed (2) — in code that hasn't changed since the last review.
crates/now-package-broker/src/policy_store/validation.rs:282
- The raw bounds pass does not enforce the draft schema's
uniqueItems: truerequirement for the set-backedMatcharrays (Operations, Managers, Sources, PackageIdentifiers, PackageNames, Versions, Scopes, Architectures, and Elevation). Serde then collects these arrays intoBTreeSets, silently removing duplicates, so an invalid draft is reported valid and committed in a different canonical form. Detect duplicate raw values here before typed deserialization and return a schema/value finding for the affected JSON pointer.
crates/now-package-broker/src/server/mod.rs:124 - Every protected route performs Authenticode validation twice: this middleware calls
validate_connection, then each ofpolicy_management,validate_policy, andreplace_policycalls it again. Since validation invokes the full signature check on disk, each request pays that cost twice. Keep the pre-body middleware check and remove the redundant handler checks (or pass an authenticated marker to the handlers).
- Files reviewed: 12/13 changed files
- Comments generated: 0 new
- Review effort level: Balanced
|
Disposition for the two suppressed observations in review
Note Human-tuned, LLM-assisted content. |
There was a problem hiding this comment.
🔵 Needs a closer look
Inconsistent path normalization can rotate a newly returned store token after the write-triggered watcher reload.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
crates/now-package-broker/src/policy_store/mod.rs:784
- The authoritative post-write reload fingerprints
path, which isobservation.configured_path(the canonical display path), while normal loads/reloads fingerprintself.configured_path(the original configured path). On Windows,canonicalize()commonly adds the\\?\prefix, so these fingerprints differ even for the same file. The watcher event caused by this write will then rotate the just-returned store token, making a follow-up request unexpectedly stale. Normalize the path before every fingerprint calculation, or perform the post-write observation using the store's original configured path.
- Files reviewed: 12/13 changed files
- Comments generated: 0 new
- Review effort level: Balanced
|
Disposition for the suppressed post-write fingerprint observation in review Fixed in The LocalSystem harness now exercises real Create → watcher reload → Update flows for both ordinary and Note Human-tuned, LLM-assisted content. |
There was a problem hiding this comment.
🔵 Needs a closer look
It introduces security-sensitive Windows authorization and persistence while explicitly deferring crash recovery and external-write CAS protections.
Review details
- Files reviewed: 12/13 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Add strict validation, keyed receipts, serialized optimistic writes, elevated Administrator authorization, and atomic policy persistence. Harden watcher trust by rejecting reparse-backed policy paths, validating full ancestor chains, and failing closed when monitoring is unavailable. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Create the LocalSystem policy fixture directly under ProgramData so every ancestor satisfies the production policy-path checks. Canonicalize the ordinary-file fixture before comparing it with its handle-resolved path on Windows runners that expose an 8.3 temp path. Issue: #1937 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keep warning-only validation valid at the diagnostic cap and bind receipts to canonical semantic warning identities rather than raw-input locations. Report contract-compatible diagnostic paths and arguments while preserving bounded deterministic validation. Issue: #1937 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Reject equal policy validity timestamps because ValidUntil must be strictly later than ValidFrom. Advance the validator version so receipts issued under the previous semantics cannot be reused. Issue: #1937 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Expose only PolicyStore validation so every valid public result carries its keyed receipt. Keep receipt-less raw and committed-policy validation private to the containing store implementation. Issue: #1937 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Reject exact duplicates in set-backed draft fields before canonicalization. Authenticate protected policy routes once before body extraction and require the request-scoped marker in handlers. Issue: #1937 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Reobserve published policies through the original configured path so watcher reloads derive the same fingerprint and retain returned store tokens. Exercise Create and Update through the real storage backend for ordinary and verbatim Windows paths. Issue: #1937 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
fbbddb3 to
e69db2a
Compare
There was a problem hiding this comment.
🔵 Needs a closer look
The validator accepts an empty prerelease-only range, and the security-sensitive Windows persistence surface warrants final human review.
Review details
- Files reviewed: 12/13 changed files
- Comments generated: 1
- Review effort level: Balanced
Reject prerelease-only bounds when prerelease matching is disabled and no stable version can satisfy the range. Advance the validator version so receipts issued under the previous semantics cannot be reused. Issue: #1937 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The change combines authorization, Windows filesystem security, persistence, concurrency, and public API behavior and needs final human validation.
Review details
- Files reviewed: 12/13 changed files
- Comments generated: 0 new
- Review effort level: Balanced
Adds JSON-only package policy management using the official policy crates, including management snapshots, authoritative validation, and Create, Update, ReplaceIdentity, and Repair operations.
Uses exact store tokens to serialize API concurrency, keeps monitoring failures sticky, and fails closed for reparse-backed policy paths. The core change adds approximately 3,300 lines across 13 paths.
Privileged external filesystem compare-and-swap and crash recovery are deferred. Authorization, storage hardening, installer integration, audit events, and broader end-to-end coverage will follow in separate layers.
Issue: #1937
Source: #1963