From e889339631417681110c5d23769eb9d82c409be3 Mon Sep 17 00:00:00 2001 From: Prekshi Vyas Date: Tue, 29 Sep 2026 12:30:49 -0700 Subject: [PATCH] docs(mxc): clarify mapper schema versions Signed-off-by: Prekshi Vyas --- crates/openshell-driver-mxc/src/lib.rs | 6 ++--- crates/openshell-driver-mxc/src/policy.rs | 14 ++++++----- .../src/policy_map/config.rs | 14 +++++++++-- .../src/policy_map/map.rs | 9 +++---- .../src/policy_map/mod.rs | 14 ++++++++--- .../src/policy_map/report.rs | 5 +++- .../tests/policy_mapper_matrix.rs | 24 +++++++++++++++++-- 7 files changed, 65 insertions(+), 21 deletions(-) diff --git a/crates/openshell-driver-mxc/src/lib.rs b/crates/openshell-driver-mxc/src/lib.rs index dce22b3b49..fe12c4253c 100644 --- a/crates/openshell-driver-mxc/src/lib.rs +++ b/crates/openshell-driver-mxc/src/lib.rs @@ -51,7 +51,7 @@ pub use relay::RelayHandle; pub use policy::{EmbeddedPolicyMapper, MapCtx, MapError, MappedConfig, PolicyMapper}; #[cfg(target_os = "windows")] pub use policy_map::{ - DEFAULT_COMMAND, DEFAULT_CONTAINMENT, DEFAULT_MXC_VERSION, LossItem, MxcMappingOptions, - MxcMappingResult, OPEN_SHELL_SUPERSET_GAPS, SplitPolicyResult, build_loss_report, map_to_mxc, - render_readme, split_policy, + DEFAULT_COARSE_MXC_VERSION, DEFAULT_COMMAND, DEFAULT_CONTAINMENT, DEFAULT_MXC_VERSION, + LossItem, MxcMappingOptions, MxcMappingResult, OPEN_SHELL_SUPERSET_GAPS, SplitPolicyResult, + build_loss_report, map_to_mxc, render_readme, split_policy, }; diff --git a/crates/openshell-driver-mxc/src/policy.rs b/crates/openshell-driver-mxc/src/policy.rs index 2a5c9883aa..3d823c3fc0 100644 --- a/crates/openshell-driver-mxc/src/policy.rs +++ b/crates/openshell-driver-mxc/src/policy.rs @@ -8,11 +8,12 @@ //! the standalone `openshell-policy-mapper` crate). This file defines the trait //! seam plus: //! -//! - [`EmbeddedPolicyMapper`] — the **primary** impl. Calls -//! [`crate::policy_map::map_to_mxc`] directly on the typed `SandboxPolicy` -//! proto (no YAML bridge), extracts the MXC filesystem shares, normalizes -//! their paths to Windows form, and rejects the create on any `error`-severity -//! loss. +//! - [`EmbeddedPolicyMapper`] — the **primary** impl. Calls the coarse mapper or +//! governed-egress split directly on the typed `SandboxPolicy` proto (no YAML +//! bridge), extracts the MXC filesystem/UI fragment, normalizes paths, and +//! rejects the create on any `error`-severity loss. The generated mapping JSON +//! is an intermediate representation: live requests are rebuilt by +//! [`crate::mxc`] using its active schema version. //! //! **Rule: never silently drop policy.** Unmappable rules surface as //! `MapError::Unsupported` and are rejected by `CreateSandbox` before lifecycle side effects. @@ -181,7 +182,8 @@ impl PolicyMapper for EmbeddedPolicyMapper { // Map directly off the typed proto. The default MXC driver path runs // an isolation session, so use that containment: its network branch // yields an `error` loss for any host allowlist, which rejects - // network policy below. + // network policy below. This coarse JSON is never sent to wxc-exec; + // only the extracted filesystem/UI policy enters the live request. let opts = crate::policy_map::MxcMappingOptions { containment: ctx.containment.clone(), container_id: ctx.sandbox_id.clone(), diff --git a/crates/openshell-driver-mxc/src/policy_map/config.rs b/crates/openshell-driver-mxc/src/policy_map/config.rs index f64b64a54a..fba9593d0d 100644 --- a/crates/openshell-driver-mxc/src/policy_map/config.rs +++ b/crates/openshell-driver-mxc/src/policy_map/config.rs @@ -12,8 +12,18 @@ use super::loss::{LossItem, add_loss}; /// not supply a real workload command. pub const DEFAULT_COMMAND: &str = "sh -lc \"echo OpenShell policy mapped to MXC; replace process.commandLine before running a real workload\""; -/// Default MXC schema version emitted in `version`. -pub const DEFAULT_MXC_VERSION: &str = "0.7.0-alpha"; +/// Default schema for the standalone coarse mapper's host-list config. +/// +/// This is deliberately independent from [`crate::mxc::MXC_SCHEMA_VERSION`]: +/// the coarse artifact uses the MXC 0.7 `allowedHosts` shape, while live driver +/// requests and the governed-egress split use MXC 0.8 directional networking. +pub const DEFAULT_COARSE_MXC_VERSION: &str = "0.7.0-alpha"; + +/// Compatibility name for [`DEFAULT_COARSE_MXC_VERSION`]. +/// +/// This value belongs only to [`super::map_to_mxc`] output. It is not the +/// schema version used for live `wxc-exec` requests. +pub const DEFAULT_MXC_VERSION: &str = DEFAULT_COARSE_MXC_VERSION; /// Default MXC containment backend for the coarse mapping. pub const DEFAULT_CONTAINMENT: &str = "bubblewrap"; diff --git a/crates/openshell-driver-mxc/src/policy_map/map.rs b/crates/openshell-driver-mxc/src/policy_map/map.rs index 4af3290715..47a159c33c 100644 --- a/crates/openshell-driver-mxc/src/policy_map/map.rs +++ b/crates/openshell-driver-mxc/src/policy_map/map.rs @@ -17,7 +17,7 @@ use openshell_core::proto::{ use serde_json::{Value, json}; use super::config::{ - DEFAULT_COMMAND, DEFAULT_CONTAINMENT, DEFAULT_MXC_VERSION, add_backend_network_loss, + DEFAULT_COARSE_MXC_VERSION, DEFAULT_COMMAND, DEFAULT_CONTAINMENT, add_backend_network_loss, add_backend_specific_config, default_enforcement_mode, filesystem_default_deny_message, }; use super::loss::{LossItem, add_loss}; @@ -27,8 +27,9 @@ use crate::mxc::MXC_SCHEMA_VERSION; /// coarse map (e.g. `proxy_redirect`) are reserved for the governed-egress split. #[derive(Clone, Debug)] pub struct MxcMappingOptions { - /// MXC schema version written into coarse-map output. The governed-egress - /// split uses the driver's MXC 0.8 schema. + /// Caller-selectable schema version written only into standalone coarse-map + /// output. The governed-egress split and live driver requests instead use + /// [`MXC_SCHEMA_VERSION`]. pub mxc_version: String, /// MXC containment backend. pub containment: String, @@ -52,7 +53,7 @@ pub struct MxcMappingOptions { impl Default for MxcMappingOptions { fn default() -> Self { Self { - mxc_version: DEFAULT_MXC_VERSION.to_owned(), + mxc_version: DEFAULT_COARSE_MXC_VERSION.to_owned(), containment: DEFAULT_CONTAINMENT.to_owned(), command: DEFAULT_COMMAND.to_owned(), container_id: "openshell-policy".to_owned(), diff --git a/crates/openshell-driver-mxc/src/policy_map/mod.rs b/crates/openshell-driver-mxc/src/policy_map/mod.rs index 670e31a617..a184fb353e 100644 --- a/crates/openshell-driver-mxc/src/policy_map/mod.rs +++ b/crates/openshell-driver-mxc/src/policy_map/mod.rs @@ -17,11 +17,17 @@ //! policy is flattened into an MXC host allowlist (`network.allowedHosts`), //! and anything MXC cannot express (ports, protocol, L7 rules, binary scope) //! is recorded in the loss report. Use this when MXC enforces network on its -//! own, with no `OpenShell` proxy in the loop. +//! own, with no `OpenShell` proxy in the loop. Its default schema is MXC 0.7, +//! matching that host-list shape; the caller can override the coarse target. //! - [`split_policy`] — the *lossless* split for the Windows MXC compute //! driver: MXC handles filesystem + containment + loopback-only egress, //! while the full `OpenShell` network policy is preserved in a trimmed policy -//! enforced by the host CONNECT proxy. +//! enforced by the host CONNECT proxy. This path uses the live driver's MXC +//! 0.8 directional-network schema. +//! +//! The coarse mapper's schema version describes its generated artifact, not the +//! live driver's operating schema. The embedded driver may use a coarse mapping +//! as an intermediate policy translation but does not send that JSON to MXC. //! //! The report/loss-report helpers are only exercised by the example and the //! integration tests, so the Windows lib build would otherwise warn on them; @@ -36,7 +42,9 @@ mod loss; mod map; mod report; -pub use config::{DEFAULT_COMMAND, DEFAULT_CONTAINMENT, DEFAULT_MXC_VERSION}; +pub use config::{ + DEFAULT_COARSE_MXC_VERSION, DEFAULT_COMMAND, DEFAULT_CONTAINMENT, DEFAULT_MXC_VERSION, +}; pub use loss::{LossItem, OPEN_SHELL_SUPERSET_GAPS}; pub use map::{MxcMappingOptions, MxcMappingResult, SplitPolicyResult, map_to_mxc, split_policy}; pub use report::{build_loss_report, render_readme}; diff --git a/crates/openshell-driver-mxc/src/policy_map/report.rs b/crates/openshell-driver-mxc/src/policy_map/report.rs index 0b1ec37b77..fdea60efa7 100644 --- a/crates/openshell-driver-mxc/src/policy_map/report.rs +++ b/crates/openshell-driver-mxc/src/policy_map/report.rs @@ -7,7 +7,10 @@ use serde_json::{Value, json}; use super::loss::{LossItem, OPEN_SHELL_SUPERSET_GAPS, summarize_missing_mxc}; -/// Build the structured `loss-report.json` value. +/// Build the structured `loss-report.json` value for a generated mapper artifact. +/// +/// `target.schemaVersion` describes that artifact's caller-selected schema. It +/// must not be interpreted as the live MXC driver's request schema. pub fn build_loss_report( source_policy: &str, generated_config: &str, diff --git a/crates/openshell-driver-mxc/tests/policy_mapper_matrix.rs b/crates/openshell-driver-mxc/tests/policy_mapper_matrix.rs index 3d2b8647f8..2a82546773 100644 --- a/crates/openshell-driver-mxc/tests/policy_mapper_matrix.rs +++ b/crates/openshell-driver-mxc/tests/policy_mapper_matrix.rs @@ -27,8 +27,8 @@ use openshell_core::proto::{ NetworkPolicyRule, ProcessPolicy, SandboxPolicy, UiClipboardAccess, UiPolicy, }; use openshell_driver_mxc::{ - EmbeddedPolicyMapper, MapCtx, MapError, MxcMappingOptions, PolicyMapper, map_to_mxc, - split_policy, + DEFAULT_COARSE_MXC_VERSION, DEFAULT_MXC_VERSION, EmbeddedPolicyMapper, MapCtx, MapError, + MxcMappingOptions, PolicyMapper, map_to_mxc, split_policy, }; use openshell_policy::{serialize_sandbox_policy, validate_sandbox_policy}; use serde_json::Value; @@ -132,6 +132,26 @@ fn assert_single_loss( // ─── QUADRANT A: mappable fields, assert exact MXC output ─────────────────── +/// The standalone coarse mapper and live governed-egress path intentionally +/// target different schema shapes. Keep the compatibility constant scoped to +/// the coarse artifact and prevent either side from silently drifting. +#[test] +fn a_schema_versions_match_their_distinct_network_shapes() { + let policy = SandboxPolicy::default(); + let coarse = map_to_mxc(&policy, &default_opts()).config; + assert_eq!(DEFAULT_MXC_VERSION, DEFAULT_COARSE_MXC_VERSION); + assert_eq!(coarse["version"], DEFAULT_COARSE_MXC_VERSION); + assert!(coarse["network"].get("allowedHosts").is_some()); + assert!(coarse["network"].get("egress").is_none()); + + let governed = split_policy(&policy, &pc_split_opts()) + .expect("governed split must exist when a proxy redirect is configured") + .mxc_config; + assert_eq!(governed["version"], "0.8.0-alpha"); + assert!(governed["network"].get("allowedHosts").is_none()); + assert!(governed["network"].get("egress").is_some()); +} + /// filesystem.read_write → readwritePaths verbatim, order preserved. #[test] fn a_rw_paths_verbatim() {