Skip to content

captureDenials run path does not check guarded capture availability before creating the sandbox #1248

Description

@theelliotm

Relevant area(s)

Windows

Brief description of your issue

mxc_engine already has a cheap, read only availability check for the guarded WPR captureDenials fallback, but only the probe uses it. The run path does not, so a request that is known to be unsatisfiable is accepted, a sandbox is built, and the failure arrives late as an untyped Process error with exit -1.

guarded_capture::is_available() (src/core/mxc_engine/src/guarded_capture.rs:97) verifies WPR is present and runs plm::trust::verify_and_pin_launch_binary against plm.exe, which is the same trust check that later gates the elevation. The probe calls it (src/core/mxc_engine/src/probe.rs:101) and correctly reports guarded WPR captureDenials fallback is unavailable.

The run path calls only factory_for_request, which checks request.policy.capture_denials.is_some() and nothing else. See src/core/mxc_engine/src/run.rs:226 and src/core/mxc_engine/src/dispatch.rs:177.

To be clear, the trust failure itself is correct and should stay. plm.exe self elevates, so refusing to elevate an unsigned binary is the right call. The problem is only when and how the failure surfaces. Any unsigned build hits this, including every local developer build, so it is not specific to CI.

7 failing assertions in the 2026-09-23 nightly run. P15a fails on all four jobs, and P14c adds three more on the T3 job:

Related: #785 was the inverse, where the probe ignored captureDenials and reported a launchable tier. Here the probe is right and the run path ignores it. #1233 hits the same psec_policy_compatible fallback routing but fails further downstream on WRITE_DAC.

Steps to reproduce

  1. Build normally, so plm.exe is unsigned.
  2. Run a config combining processContainer.leastPrivilege with processContainer.captureDenials.
  3. Compare wxc-exec --probe against the actual run.

Covered by tests/scripts/run_processcontainer_privilege_test.ps1 (P15a) and tests/scripts/run_processcontainer_capture_denials_test.ps1 (P14c).

Expected behavior

The run path applies the same availability gate the probe uses and rejects before creating a sandbox, with a typed error. .github/copilot-instructions.md states unsupported policy must fail closed and be rejected before the sandbox is created. docs/schema.md:219 documents this combination as a tier constraint with a guarded WPR fallback, so callers reasonably expect either a working fallback or a clean rejection.

Actual behavior

The backend log shows the full sandbox being built first:

tier selected
AppContainer SID created
network policy applied
Process created successfully (PID: ...)
UI job object assigned

and only then:

Process error: captureDenials guarded WPR session failed to start: guarded WPR session
start failed: refusing to elevate the guarded PLM binary at ...\plm.exe:
Authenticode verification failed (WinVerifyTrust status 0x800b0100)

The suspended child is terminated, the sandbox is torn down, and the process exits -1.

The three P14c failures are downstream of this. The run never launches, so no denials output path is ever reported:

P14c :: the actual denials output path is reported on stderr (reportedPaths=0/2)
P14c :: the reported path is not the verbatim requested path (reported=)
P14c :: two runs of the same config report different output paths (run1=; run2=)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Backend-ProcessContainerWindows ProcessContainer behavior, including BaseContainer and AppContainer isolation tiers.Issue-BugSomething is not working as intended.OS-WindowsApplies specifically to Windows hosts or Windows behavior.Priority2High-impact issue affecting key functionality; prioritize for the next appropriate release.Release v1.0Issues tracked for the version 1.0 release

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions