Skip to content

fix(agent): harden package broker caller identity - #1977

Open
Benoît Cortier (CBenoit) wants to merge 10 commits into
cbenoit-complete-watcher-securityfrom
cbenoit-policy-caller-hardening
Open

fix(agent): harden package broker caller identity#1977
Benoît Cortier (CBenoit) wants to merge 10 commits into
cbenoit-complete-watcher-securityfrom
cbenoit-policy-caller-hardening

Conversation

@CBenoit

Copy link
Copy Markdown
Member

Retains the named-pipe client process and token identity, verifies that the retained executable object backs the process main image, and validates its Authenticode signature and trusted-writer path through retained handles.

Policy replacement independently requires the actual client token to be elevated with the built-in Administrators group enabled. Blocking identity capture remains bounded by the connection semaphore, while existing package-manager executable path compatibility is preserved.

This authenticates current process, image, and filesystem provenance after the initial process open. It does not attest runtime memory integrity, reconstruct historical ACL integrity, or close the interval before that initial process handle is acquired.

The change adds approximately 628 production lines and 543 inline test lines across seven files. Validator findings and readiness work inherited from the policy-write core remain tracked in #1975 and are intentionally not duplicated here.

Issue: #1937

@CBenoit
Benoît Cortier (CBenoit) changed the base branch from cbenoit-plan-agent-policy-endpoint to cbenoit-complete-watcher-security September 7, 2026 23:55
@CBenoit

Copy link
Copy Markdown
Member Author

Implementation notes:

  • Retains the pipe-reported process handle and token identity, then checks liveness throughout request authentication.
  • Binds a local-volume executable handle to the process main-image section before applying retained-handle embedded or catalog signature verification.
  • Pins caller executable ancestors without write/delete sharing and rejects reparse-backed or untrusted-writable paths; existing package-manager path verification remains unchanged.
  • Keeps the existing pre-body authentication middleware and independent elevated, enabled-Administrators write gate from the policy core.
  • Adds focused process-exit, path replacement, junction retarget, same-stream rewrite, WinTrust handle, and bounded-capture regression coverage.

Validated with targeted default/development broker suites, retained crypt and ACL tests, the i686 broker check, nightly formatting, warnings-denied workspace Clippy, and the full workspace test suite.

Note

Human-tuned, LLM-assisted content.

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.

🟡 Changes recommended

The new native structure size assertion references an unimported function and prevents compilation.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Hardens Windows package-broker authentication by retaining and validating client process, token, executable, and filesystem identities.

Changes:

  • Adds retained-handle Authenticode and process-image verification.
  • Pins trusted executable ancestors and enforces elevated Administrator identity.
  • Bounds blocking identity capture and adds regression tests.

Prose verification was skipped because only Rust code and rustdoc changed.

File summaries
File Description
crates/win-api-wrappers/src/undoc.rs Adds native process-image definitions.
crates/win-api-wrappers/src/security/crypt.rs Verifies signatures through retained file handles.
crates/win-api-wrappers/src/process.rs Adds image-mapping and process-instance checks.
crates/now-package-broker/src/policy_security.rs Pins and validates executable ancestors.
crates/now-package-broker/src/pipe.rs Moves identity capture to bounded blocking work.
crates/now-package-broker/src/auth.rs Retains and authenticates client identity.
crates/devolutions-agent-shared/src/windows/code_signing.rs Exposes retained-file signature validation.
Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread crates/win-api-wrappers/src/undoc.rs

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.

🟡 Changes recommended

The trusted-writer requirement rejects the coordinated client’s default per-user installation, and one changed block fails repository formatting requirements.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread crates/now-package-broker/src/auth.rs Outdated
Comment thread crates/now-package-broker/src/pipe.rs

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.

🟡 Changes recommended

The security bypass is broader than documented, and retained-path validation rejects supported no-drive-letter volumes.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (2)

crates/now-package-broker/src/auth.rs:85

  • skip_signature_validation is documented in task.rs and the Agent config as skipping only Authenticode validation, but using it as enforce_executable_security also disables the new DACL validation and ancestor pinning. In development-feature builds, this setting now removes a separate security control without saying so. Keep trusted-writer validation enabled, or introduce and document a separately named broad executable-security bypass.
        let client = Self::from_process(
            process_instance,
            Arc::clone(&process),
            !signature_validation_skipped(skip_signature_validation),
        )?;

crates/now-package-broker/src/pipe.rs:84

  • This new closure and its match arms are not rustfmt-formatted, unlike the surrounding control flow. Format the block before merging.
                                let capture = spawn_bounded_capture(permit, move || {
                                let client = PipeClient::from_connected_pipe(&server, skip_signature_validation);
                                (server, client)
                                });
                                let (_permit, server, client) = match capture.await {
  • Files reviewed: 7/7 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread crates/now-package-broker/src/policy_security.rs

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.

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

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.

🔵 Needs a closer look

The security boundary relies on undocumented Windows process APIs and should receive final human validation.

Review details
  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

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.

🔵 Needs a closer look

The security boundary depends on undocumented NT behavior and coordinated process, filesystem, token, and signature guarantees requiring final human validation.

Review details
  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Retain the named-pipe connector process and reject recycled or exited process identities before authorizing requests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Resolve the connector's kernel-backed main image, reject non-local image paths, and retain the exact mapped file object for subsequent authentication.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Verify Authenticode, trusted writers, and reparse-safe ancestors through the retained process-image file object so path swaps and writable images fail closed.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Run blocking caller-image checks outside the accept loop while retaining the connection permit until abandoned work actually completes.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keep test-only process helpers out of production builds and accept expected fail-closed outcomes from locally modified WindowsApps security.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
State which handles establish identity, how capture is bounded, and which runtime and historical guarantees remain outside this layer.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keep existing package-manager support for secured reparse-backed layouts while applying stricter retained ancestor pinning only to broker callers.

Bind the WinGet regression to the resolved target and document the residual pre-capture identity interval.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keep package-manager path compatibility and existing path-based signature behavior while applying retained handles, pinned ancestors, and full diagnostics only to broker callers.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keep trusted-writer and ancestor validation active when only Authenticode is skipped, and support retained callers on local volume-GUID paths.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Run the LocalSystem policy tester from an atomically protected ProgramData leaf so production caller ACL checks remain enabled in CI.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

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.

🔵 Needs a closer look

The security-sensitive design relies on undocumented Windows process APIs and cross-layer identity guarantees requiring final human validation.

Review details
  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@CBenoit
Benoît Cortier (CBenoit) marked this pull request as ready for review September 8, 2026 15:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants