From 248b6bc93904b5cda91b4bb2a3f6551a835308e6 Mon Sep 17 00:00:00 2001 From: Amin Chirazi Date: Sat, 1 Aug 2026 12:53:31 +0000 Subject: [PATCH] docs(adapters): Windows containment stopped being unimplemented MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three comments still described the code as it was before #331 and #342. This repository treats prose describing code that no longer exists as a defect, and one of the three was user-visible: `command_flow()` told a Windows adopter "egress containment is not implemented on Windows yet", which is now simply false. The wording had to change for a second reason, and it is the more interesting one. That reason was written when the answer was permanent — nothing on Windows could be contained, so the tier said so. Now the tier is decided by the RUN, and `command_flow()` is a PRE-RUN probe. Its job changed from "reporting a limitation" to "not predicting a result". So it says what is true of a run that has not started: not contained, and here is whether this host can support it. The `Enforced` arm stays absent for a reason that outlived the filters landing — seven steps can fail after the probe passes, and a prediction reported as a result is the false green of #300 and #301 by optimism rather than by silence. The test that pinned the old wording asserted on the roadmap phrase, so it would have gone green forever while the message rotted. It now asserts the negative: the reason must NOT claim the feature is unimplemented, and the tier must not be optimistic. Same claim it always made, minus the hostage to a sentence. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01XdXrbksFKirm7yW6EDunur --- crates/flowproof-adapters/src/egress.rs | 56 +++++++++++-------- .../flowproof-adapters/src/egress_windows.rs | 30 ++++++---- crates/flowproof-adapters/src/lib.rs | 7 ++- 3 files changed, 58 insertions(+), 35 deletions(-) diff --git a/crates/flowproof-adapters/src/egress.rs b/crates/flowproof-adapters/src/egress.rs index 4c33a0e..fb47b13 100644 --- a/crates/flowproof-adapters/src/egress.rs +++ b/crates/flowproof-adapters/src/egress.rs @@ -72,14 +72,21 @@ impl Containment { crate::egress_linux::probe_containment() } - /// Windows is being built. Until the WFP filters land this ALWAYS returns - /// "not contained" - the probe only makes the REASON specific, so an - /// adopter learns whether their host is ready rather than guessing. + /// Windows decides its tier from the RUN, not from this probe, so this is + /// the answer for a run that has not started - and it is never optimistic. /// - /// The `Enforced` arm is absent rather than conditional. `egress_windows` - /// reports facts about the host and never names this type, so there is no - /// path from a probe result to a containment claim until the step that - /// installs the filters adds one. + /// That is the whole difference from the Linux arm above. There the filter + /// installs in the child's `pre_exec`, so a probe-pass implies an + /// install-success. Here seven steps can still fail after the probe says + /// yes - the account, the logon, the privileges, the desktop grant, the + /// engine, the sublayer, the filters - plus collection, without which + /// there is no audit lane. See [`crate::egress_windows::run`]. + /// + /// So the `Enforced` arm is absent rather than conditional. A tier claimed + /// from a probe would be a PREDICTION reported as a RESULT, which is the + /// false green of #300 and #301 arriving by optimism instead of by + /// silence. The achieved tier travels back on + /// [`crate::agent_runner::AgentRun::containment`] and wins where present. #[cfg(windows)] pub fn command_flow() -> Self { use crate::egress_windows::HostReadiness; @@ -87,14 +94,13 @@ impl Containment { let blockers = host.blockers(); if blockers.is_empty() { Containment::NotContained(format!( - "egress containment is not implemented on Windows yet; this host could \ - support it ({})", + "this run has not been contained; on Windows the tier is decided by the \ + run, and this host can support it ({})", host.summary() )) } else { Containment::NotContained(format!( - "egress containment is not implemented on Windows yet, and this host could \ - not support it as configured: {}", + "this host cannot enforce egress containment as configured: {}", blockers.join("; ") )) } @@ -285,27 +291,33 @@ mod tests { ); } - /// Until the WFP filters exist, a Windows `command:` flow must report - /// "not contained" - whatever the host is capable of. + /// The PRE-RUN prediction is never optimistic on Windows. + /// + /// This test used to say the filters did not exist. They do now, so what + /// it holds has changed while staying the same claim: a tier taken before + /// anything starts must not say `enforced`, because seven steps can fail + /// after the probe passes. A tier that says `enforced` lets + /// `assert_no_egress` certify a run nothing was containing - the false + /// green of #300 and #301 arriving by prediction rather than by silence. /// - /// This is the same false green as #300 and #301 arriving by a third - /// route: a tier that says `enforced` lets `assert_no_egress` certify a - /// run nothing was containing. The probe makes the REASON specific; it - /// must never make the VERDICT optimistic. The step that installs filters - /// is the step that deletes this test. + /// The reason must also describe the RUN, not our roadmap. "Not + /// implemented on Windows yet" was true when this file was written and is + /// now prose describing code that no longer exists, which this repository + /// treats as a defect in its own right. #[cfg(windows)] #[test] - fn enforced_is_unreachable_on_windows() { + fn the_pre_run_prediction_is_never_optimistic_on_windows() { let tier = Containment::command_flow(); assert!( !tier.is_enforced(), - "no filter is installed yet, so nothing can be contained: {}", + "the run has not started, so nothing has been contained yet: {}", tier.report_line() ); let reason = tier.reason().expect("not contained carries a reason"); assert!( - reason.contains("not implemented on Windows yet"), - "the reason must read as our roadmap, not as the adopter's host: {reason}" + !reason.contains("not implemented"), + "Windows containment IS implemented; the reason must describe this run, \ + not a roadmap that has moved on: {reason}" ); } diff --git a/crates/flowproof-adapters/src/egress_windows.rs b/crates/flowproof-adapters/src/egress_windows.rs index 814c676..3c15a4f 100644 --- a/crates/flowproof-adapters/src/egress_windows.rs +++ b/crates/flowproof-adapters/src/egress_windows.rs @@ -1,14 +1,24 @@ -//! Windows egress containment. Still installs no WFP filter and launches -//! nothing; [`identity`] adds the per-run account the filters will be scoped -//! to. +//! Windows egress containment, end to end: a per-run local account +//! ([`identity`]) it is logged on as ([`logon`]) and started under +//! ([`spawn`]), WFP filters scoped to that account's SID ([`wfp`], +//! [`filters`]), and the audit lane that evidences what they refused +//! ([`netevents`], [`audit`]). [`run`] is the sequence; everything else is a +//! step in it. //! -//! **Nothing here can produce a `Containment::Enforced`.** That is structural -//! rather than a convention: this module does not name that type at all. It -//! reports facts; `Containment::command_flow` turns them into a "not -//! contained" reason, and `enforced_is_unreachable_on_windows` in `egress` -//! asserts the tier. Claiming enforcement before a filter exists would let -//! `assert_no_egress` certify a run nothing was containing - the same false -//! green as #300 and #301 by a third route. +//! **Nothing here names `Containment`.** That is structural rather than a +//! convention, and it survives the filters existing: this module is gated on +//! the `windows` DEPENDENCY while `Containment` is gated on the `agent` +//! FEATURE, so naming it would drag this code behind `agent` and cost it the +//! only typecheck it gets without a Windows runner (see the gate on the `pub +//! mod` in `lib.rs`). +//! +//! So [`run::Outcome`] reports what the run ACHIEVED as plain data, and the +//! caller converts. `Containment::command_flow` still cannot return +//! `Enforced` on Windows, for a reason that outlived the filters landing: it +//! is a PRE-RUN probe, and seven steps can fail after it passes. Predicting a +//! tier and reporting it as a result would let `assert_no_egress` certify a +//! run nothing was containing - the same false green as #300 and #301 by a +//! third route. //! //! Probing at all is about the REASON. Windows containment needs three things //! a host can be missing, each of which fails as something else (see diff --git a/crates/flowproof-adapters/src/lib.rs b/crates/flowproof-adapters/src/lib.rs index c8dd1e4..e54f8d0 100644 --- a/crates/flowproof-adapters/src/lib.rs +++ b/crates/flowproof-adapters/src/lib.rs @@ -16,9 +16,10 @@ pub mod egress; #[cfg(all(feature = "agent", target_os = "linux"))] pub mod egress_linux; -// Windows containment is being built (see `spike/windows-containment/LOG.md`). -// Today this module is the capability PROBE only: it installs no filter and -// cannot report "enforced", so a Windows run is still honestly not contained. +// Windows containment (see `spike/windows-containment/LOG.md` for how it was +// established). This module installs the filters and runs the agent behind +// them; the tier it achieved travels back on the run rather than being +// predicted by a probe, because several steps can fail after the probe passes. // // Gated on the `windows` DEPENDENCY (either feature that pulls it) rather than // on `agent`, and that is deliberate. `agent` pulls ureq, whose TLS stack