Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
194bc51
feat(engine): port TS host detectors (lxc, windows_sandbox) to Rust
huzaifa-msft Jul 31, 2026
46b2274
feat(engine): add available_backends() host-capability probe API
huzaifa-msft Aug 5, 2026
ce30d3d
fix(windows_sandbox): address Copilot review on host detectors
huzaifa-msft Aug 5, 2026
58d3ba9
docs: trim comments; drop DISM from windows_sandbox probe
huzaifa-msft Aug 5, 2026
227d208
refactor(appcontainer): centralize isolation-tier name strings
huzaifa-msft Aug 7, 2026
78f01c4
feat(engine): add isolation_session host detection (registration-based)
huzaifa-msft Aug 7, 2026
776ea78
Merge remote-tracking branch 'origin/main' into user/modanish/port-ho…
huzaifa-msft Aug 7, 2026
646cd0f
Potential fix for pull request finding
huzaifa-d Aug 7, 2026
a75faa9
docs: trim comments in the windows_sandbox + isolation_session probes
huzaifa-msft Aug 7, 2026
aea37f8
docs: restore platform_support module rationale
huzaifa-msft Aug 7, 2026
66edc35
docs: restore pre-existing platform.rs comments simplified by this PR
huzaifa-msft Aug 7, 2026
b5783ec
refactor(wxc_common): extract shared system_directory() helper
huzaifa-msft Aug 7, 2026
0580425
fix(engine): address review on host detectors (wslc probe, tier de-dr…
huzaifa-msft Aug 7, 2026
9d4b064
fix(windows_sandbox): route runner preflight through the trusted avai…
huzaifa-msft Aug 7, 2026
e77a329
fix(windows_sandbox): launch WindowsSandbox.exe from the trusted syst…
huzaifa-msft Aug 7, 2026
8015e0e
fix(engine): keep platform_support() limited to SDK-launchable backends
huzaifa-msft Aug 7, 2026
7eb5930
fix(lxc): bound and cache the lxc-ls availability probe
huzaifa-msft Aug 7, 2026
6aafe16
fix(engine): derive probe backend names from the enum; gate BFS tier …
huzaifa-msft Aug 7, 2026
0ea87ea
docs(mxc-sdk): add host-backend discovery usage guidance + before/after
huzaifa-msft Aug 7, 2026
7bf85a5
fix(lxc): drop unread exit code from LxcLsOutcome::ExitedFailure
huzaifa-msft Aug 7, 2026
a81bf80
fix(engine): report BFS tier ceiling from tier2_bfs alone, not bfscfg…
huzaifa-msft Aug 7, 2026
5920cbc
test(mxc-sdk): fence platform_support() to the SDK-launchable set
huzaifa-msft Aug 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions src/backends/appcontainer/common/src/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ mod tests {
}
#[test]
fn dispatch_t1_no_denied_paths_no_dacl() {
let _g = ForceTierGuard::set("base-container");
let _g = ForceTierGuard::set_tier(IsolationTier::BaseContainer);
let req = test_request(empty_policy());
let d = dispatch_with_fallback(&req).expect("T1 dispatch should succeed");
assert!(matches!(d.tier, IsolationTier::BaseContainer));
Expand All @@ -673,7 +673,7 @@ mod tests {
// `deniedPaths`) to BaseContainer's native API; the dispatcher
// attaches no `DaclManager` on the T1 path regardless of the
// `deniedPaths` contents.
let _g = ForceTierGuard::set("base-container");
let _g = ForceTierGuard::set_tier(IsolationTier::BaseContainer);
let (policy, _tmp) = policy_with_denied_temp();
let req = test_request(policy);
let d = dispatch_with_fallback(&req).expect("T1+deny dispatch should succeed");
Expand All @@ -685,7 +685,7 @@ mod tests {
}
#[test]
fn dispatch_t2_with_denied_paths_has_dacl() {
let _g = ForceTierGuard::set("appcontainer-bfs");
let _g = ForceTierGuard::set_tier(IsolationTier::AppContainerBfs);
let (policy, _tmp) = policy_with_denied_temp();
let req = test_request(policy);
let d = dispatch_with_fallback(&req).expect("T2+deny dispatch should succeed");
Expand All @@ -694,7 +694,7 @@ mod tests {
}
#[test]
fn dispatch_t3_always_has_dacl() {
let _g = ForceTierGuard::set("appcontainer-dacl");
let _g = ForceTierGuard::set_tier(IsolationTier::AppContainerDacl);
let (policy, _tmp) = policy_with_rw_temp();
let req = test_request(policy);
let d = dispatch_with_fallback(&req).expect("T3 dispatch should succeed");
Expand Down Expand Up @@ -770,7 +770,7 @@ mod tests {

#[test]
fn dispatch_fallback_disabled_errors() {
let _g = ForceTierGuard::set("appcontainer-dacl");
let _g = ForceTierGuard::set_tier(IsolationTier::AppContainerDacl);
let (mut policy, _tmp) = policy_with_rw_temp();
policy.fallback.allow_dacl_mutation = false;
let req = test_request(policy);
Expand Down Expand Up @@ -939,7 +939,7 @@ mod tests {

#[test]
fn select_backend_t1_builds_base_container_no_dacl() {
let _g = ForceTierGuard::set("base-container");
let _g = ForceTierGuard::set_tier(IsolationTier::BaseContainer);
let req = test_request(empty_policy());
let (backend, dacl, tier, _w) =
select_backend_with_fallback(&req).expect("T1 selection should succeed");
Expand All @@ -956,7 +956,7 @@ mod tests {

#[test]
fn select_backend_t2_no_deny_builds_appcontainer_no_dacl() {
let _g = ForceTierGuard::set("appcontainer-bfs");
let _g = ForceTierGuard::set_tier(IsolationTier::AppContainerBfs);
let req = test_request(empty_policy());
let (backend, dacl, tier, _w) =
select_backend_with_fallback(&req).expect("T2 selection should succeed");
Expand All @@ -970,7 +970,7 @@ mod tests {

#[test]
fn select_backend_t2_with_deny_builds_appcontainer_with_dacl() {
let _g = ForceTierGuard::set("appcontainer-bfs");
let _g = ForceTierGuard::set_tier(IsolationTier::AppContainerBfs);
let (policy, _tmp) = policy_with_denied_temp();
let req = test_request(policy);
let (backend, dacl, tier, _w) =
Expand All @@ -985,7 +985,7 @@ mod tests {

#[test]
fn select_backend_t3_builds_appcontainer_with_dacl() {
let _g = ForceTierGuard::set("appcontainer-dacl");
let _g = ForceTierGuard::set_tier(IsolationTier::AppContainerDacl);
let (policy, _tmp) = policy_with_rw_temp();
let req = test_request(policy);
let (backend, dacl, tier, _w) =
Expand Down
124 changes: 77 additions & 47 deletions src/backends/appcontainer/common/src/fallback_detector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,57 @@ use std::sync::OnceLock;

use wxc_common::models::ContainerPolicy;

/// Selected isolation tier. The variant order corresponds to descending
/// security strength.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IsolationTier {
/// Declares [`IsolationTier`] together with its variant↔string mapping in one
/// place, so [`ALL`](IsolationTier::ALL), [`as_str`](IsolationTier::as_str), and
/// the [`FromStr`] impl are all generated from the same tier list and cannot
/// drift. Adding a tier is a single line in the invocation below; the compiler
/// then forces `as_str`, `from_str`, and `ALL` to cover it.
macro_rules! isolation_tiers {
($($(#[$variant_doc:meta])* $variant:ident => $name:literal),+ $(,)?) => {
/// Selected isolation tier. The variant order corresponds to descending
/// security strength.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IsolationTier {
$($(#[$variant_doc])* $variant,)+
}

impl IsolationTier {
/// Every tier, ordered strongest-first (matching the variant order).
pub const ALL: [IsolationTier; [$(isolation_tiers!(@count $variant)),+].len()] =
[$(IsolationTier::$variant),+];

/// Stable kebab-case identifier for serialization.
pub fn as_str(self) -> &'static str {
match self {
$(IsolationTier::$variant => $name,)+
}
}
}

impl std::str::FromStr for IsolationTier {
type Err = ();

/// Inverse of [`as_str`](Self::as_str). Generated from the same tier
/// list via an exhaustive match, so the two directions cannot drift.
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
$($name => Ok(IsolationTier::$variant),)+
_ => Err(()),
}
}
}
};
// Counts each variant as one array element so `ALL`'s length tracks the list.
(@count $variant:ident) => { () };
}

isolation_tiers! {
/// Tier 1 β€” a supported BaseContainer contract from `processmodel.dll`.
BaseContainer,
BaseContainer => "base-container",
/// Tier 2 β€” AppContainer + `bfscfg.exe` BFS filesystem policy.
AppContainerBfs,
AppContainerBfs => "appcontainer-bfs",
/// Tier 3 β€” AppContainer + DACL-based filesystem policy on host paths.
AppContainerDacl,
}

impl IsolationTier {
/// Stable kebab-case identifier for serialization.
pub fn as_str(self) -> &'static str {
match self {
IsolationTier::BaseContainer => "base-container",
IsolationTier::AppContainerBfs => "appcontainer-bfs",
IsolationTier::AppContainerDacl => "appcontainer-dacl",
}
}
AppContainerDacl => "appcontainer-dacl",
}

/// Outcome of [`detect`]: the chosen tier plus any operator-visible warnings
Expand Down Expand Up @@ -173,7 +203,7 @@ pub(crate) fn detect_with_base_container_capabilities(
// the tests silently no-op'd).
#[cfg(test)]
if let Ok(forced) = std::env::var("MXC_FORCE_TIER") {
if let Some(tier) = parse_force_tier(&forced) {
if let Ok(tier) = forced.parse::<IsolationTier>() {
return forced_decision(tier, policy, denied);
}
}
Expand Down Expand Up @@ -435,16 +465,6 @@ fn check_write_dac_path(path: &Path) -> Result<(), FallbackError> {
}
}

#[cfg(test)]
fn parse_force_tier(s: &str) -> Option<IsolationTier> {
match s {
"base-container" => Some(IsolationTier::BaseContainer),
"appcontainer-bfs" => Some(IsolationTier::AppContainerBfs),
"appcontainer-dacl" => Some(IsolationTier::AppContainerDacl),
_ => None,
}
}

#[cfg(test)]
fn forced_decision(
tier: IsolationTier,
Expand Down Expand Up @@ -705,7 +725,7 @@ mod tests {
}
#[test]
fn empty_policy_t1_when_bc_present_and_preferred() {
let _g = ForceTierGuard::set("base-container");
let _g = ForceTierGuard::set_tier(IsolationTier::BaseContainer);
let policy = empty_policy();
let d = detect(&policy, true).expect("forced base-container should succeed");
assert!(matches!(d.tier, IsolationTier::BaseContainer));
Expand All @@ -714,15 +734,15 @@ mod tests {
}
#[test]
fn empty_policy_no_filesystem_t2_path() {
let _g = ForceTierGuard::set("appcontainer-bfs");
let _g = ForceTierGuard::set_tier(IsolationTier::AppContainerBfs);
let policy = empty_policy();
let d = detect(&policy, true).expect("forced bfs should succeed");
assert!(matches!(d.tier, IsolationTier::AppContainerBfs));
assert!(!d.needs_dacl_augmentation);
}
#[test]
fn denied_paths_disabled_blocks_t1() {
let _g = ForceTierGuard::set("base-container");
let _g = ForceTierGuard::set_tier(IsolationTier::BaseContainer);
let mut policy = policy_with_denied();
policy.fallback.allow_dacl_mutation = false;
assert!(matches!(
Expand All @@ -732,7 +752,7 @@ mod tests {
}
#[test]
fn denied_paths_disabled_blocks_t2() {
let _g = ForceTierGuard::set("appcontainer-bfs");
let _g = ForceTierGuard::set_tier(IsolationTier::AppContainerBfs);
let mut policy = policy_with_denied();
policy.fallback.allow_dacl_mutation = false;
assert!(matches!(
Expand All @@ -742,7 +762,7 @@ mod tests {
}
#[test]
fn denied_paths_disabled_blocks_t3() {
let _g = ForceTierGuard::set("appcontainer-dacl");
let _g = ForceTierGuard::set_tier(IsolationTier::AppContainerDacl);
let mut policy = policy_with_denied();
policy.fallback.allow_dacl_mutation = false;
assert!(matches!(
Expand Down Expand Up @@ -818,20 +838,30 @@ mod tests {
d.warnings
);
}
#[test]
fn tier_name_round_trips_through_from_str() {
// `FromStr` is derived from `as_str` via `ALL`, so every tier must
// round-trip and the set stays in sync automatically.
for tier in IsolationTier::ALL {
assert_eq!(tier.as_str().parse::<IsolationTier>(), Ok(tier));
}
}

#[test]
fn force_tier_env_var_parses_all_three_values() {
assert!(matches!(
parse_force_tier("base-container"),
Some(IsolationTier::BaseContainer)
));
assert!(matches!(
parse_force_tier("appcontainer-bfs"),
Some(IsolationTier::AppContainerBfs)
));
assert!(matches!(
parse_force_tier("appcontainer-dacl"),
Some(IsolationTier::AppContainerDacl)
));
assert_eq!(
"base-container".parse::<IsolationTier>(),
Ok(IsolationTier::BaseContainer)
);
assert_eq!(
"appcontainer-bfs".parse::<IsolationTier>(),
Ok(IsolationTier::AppContainerBfs)
);
assert_eq!(
"appcontainer-dacl".parse::<IsolationTier>(),
Ok(IsolationTier::AppContainerDacl)
);
assert!("not-a-real-tier".parse::<IsolationTier>().is_err());
}
#[test]
fn force_tier_env_var_invalid_value_falls_through_to_real_probes() {
Expand Down Expand Up @@ -989,7 +1019,7 @@ mod tests {
}
#[test]
fn compute_decision_with_force_tier_carries_warnings_empty() {
let _g = ForceTierGuard::set("appcontainer-dacl");
let _g = ForceTierGuard::set_tier(IsolationTier::AppContainerDacl);
let mut policy = empty_policy();
policy.fallback.allow_dacl_mutation = true;
let d = detect(&policy, true).expect("forced dacl with allow_dacl_mutation=true");
Expand Down
6 changes: 3 additions & 3 deletions src/backends/appcontainer/common/src/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ mod tests {

#[test]
fn run_probe_with_force_tier() {
let _g = ForceTierGuard::set("appcontainer-bfs");
let _g = ForceTierGuard::set_tier(IsolationTier::AppContainerBfs);
let policy = ContainerPolicy::default();
let out = run_probe(&policy);
assert_eq!(out.tier, Some("appcontainer-bfs"));
Expand All @@ -280,7 +280,7 @@ mod tests {

#[test]
fn run_probe_handles_dacl_disabled_error() {
let _g = ForceTierGuard::set("appcontainer-dacl");
let _g = ForceTierGuard::set_tier(IsolationTier::AppContainerDacl);
let mut policy = ContainerPolicy::default();
policy.fallback.allow_dacl_mutation = false;
let out = run_probe(&policy);
Expand All @@ -296,7 +296,7 @@ mod tests {

#[test]
fn omitted_fields_when_error() {
let _g = ForceTierGuard::set("appcontainer-dacl");
let _g = ForceTierGuard::set_tier(IsolationTier::AppContainerDacl);
let mut policy = ContainerPolicy::default();
policy.fallback.allow_dacl_mutation = false;
let out = run_probe(&policy);
Expand Down
7 changes: 7 additions & 0 deletions src/backends/appcontainer/common/src/test_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ impl ForceTierGuard {
}
ForceTierGuard { _lock: guard }
}

/// Typed variant: forces a real tier by its canonical serialized name, so
/// call-sites don't hardcode the string. Prefer this over [`set`](Self::set),
/// which remains for negative tests that need an intentionally invalid value.
pub(crate) fn set_tier(tier: crate::fallback_detector::IsolationTier) -> Self {
Self::set(tier.as_str())
}
}

impl Drop for ForceTierGuard {
Expand Down
Loading
Loading