Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
34 changes: 34 additions & 0 deletions src/core/mxc-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ let policy = SandboxPolicy {
network: None,
ui: None,
timeout_ms: Some(10_000),
capture_denials: None,
};
let mut request = build_request(&policy, None)?;
request.set_script("echo hello");
Expand Down Expand Up @@ -55,6 +56,38 @@ dirs), [`user_profile_policy`], and [`temporary_files_policy`].
[`platform_support`] is the Rust port of `getPlatformSupport` β€” reports host
support and the available containment backends.

## Denial capture (Windows)

`SandboxPolicy::capture_denials` enables the Windows ProcessContainer's
learning-mode capture: the runner records every access the policy does not
grant and writes them to a JSON denials document.

```rust
use mxc_sdk::policy::{CaptureDenialsMode, CaptureDenialsSection};
use mxc_sdk::SandboxPolicy;

let policy = SandboxPolicy {
version: "0.7.0-alpha".to_string(),
filesystem: None,
network: None,
ui: None,
timeout_ms: None,
capture_denials: Some(CaptureDenialsSection {
// `Block` (the default) keeps deny-by-default and records the denial;
// `Allow` runs permissively and records what *would* have been denied.
mode: CaptureDenialsMode::Block,
// Absolute path; a per-run id is stamped into the stem
// (`denials.json` -> `denials.<run-id>.json`). `None` uses a managed temp.
output_path: None,
}),
};
```

`Allow` relaxes containment for the run β€” it is reported through `warnings()`.
Read the resulting file path and denial summary from `output_metadata()` after
the process terminates. The section is ignored on Linux and macOS, whose
backends have no learning-mode API.

## Live stdio + kill (streaming)

[`spawn_sandbox`] returns a [`Sandbox`] you can drive
Expand All @@ -71,6 +104,7 @@ let policy = SandboxPolicy {
network: None,
ui: None,
timeout_ms: None,
capture_denials: None,
};
let mut request = build_request(&policy, None)?;
request.set_script("cat"); // echoes stdin until EOF
Expand Down
2 changes: 2 additions & 0 deletions src/core/mxc-sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
//! network: None,
//! ui: None,
//! timeout_ms: None,
//! capture_denials: None,
//! };
//! let mut request = build_request(&policy, None)?;
//! request.set_script("echo hi");
Expand Down Expand Up @@ -66,6 +67,7 @@
//! # let policy = SandboxPolicy {
//! # version: "0.7.0-alpha".to_string(),
//! # filesystem: None, network: None, ui: None, timeout_ms: None,
//! # capture_denials: None,
//! # };
//! // Run a command inside a WSL container (Windows, --features wslc).
//! let wslc = WslcSection { image: "python:3.12".to_string(), ..Default::default() };
Expand Down
3 changes: 3 additions & 0 deletions src/core/mxc-sdk/tests/sandbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ fn seatbelt_request(command: &str, timeout_ms: u32) -> SandboxRequest {
} else {
Some(timeout_ms)
},
capture_denials: None,
};
let mut request = build_request(&policy, None).expect("build_request should succeed");
request.set_script(command);
Expand All @@ -57,6 +58,7 @@ fn process_container_request(version: &str, command: &str, timeout_ms: u32) -> S
} else {
Some(timeout_ms)
},
capture_denials: None,
};
let mut request = build_request(&policy, None).expect("build_request should succeed");
request.set_script(command);
Expand Down Expand Up @@ -123,6 +125,7 @@ fn version_older_than_supported_is_rejected() {
network: None,
ui: None,
timeout_ms: None,
capture_denials: None,
};

let err =
Expand Down
3 changes: 3 additions & 0 deletions src/core/mxc-sdk/tests/sdk_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ fn build_request_rejects_empty_version() {
network: None,
ui: None,
timeout_ms: None,
capture_denials: None,
};

let err = build_request(&policy, None).expect_err("an empty policy version must be rejected");
Expand All @@ -147,6 +148,7 @@ fn build_request_host_rules_require_outbound() {
}),
ui: None,
timeout_ms: None,
capture_denials: None,
};

// Unix backends accept host rules without `allowOutbound`; only Windows
Expand Down Expand Up @@ -179,6 +181,7 @@ fn build_request_then_run_seatbelt() {
network: None,
ui: None,
timeout_ms: Some(10000),
capture_denials: None,
};

let mut request = build_request(&policy, None).expect("build_request should succeed");
Expand Down
1 change: 1 addition & 0 deletions src/core/mxc-sdk/tests/streaming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ fn seatbelt_request(command: &str, timeout_ms: u32) -> SandboxRequest {
} else {
Some(timeout_ms)
},
capture_denials: None,
};
let mut request = build_request(&policy, None).expect("build_request should succeed");
request.set_script(command);
Expand Down
1 change: 1 addition & 0 deletions src/core/mxc-sdk/tests/streaming_processcontainer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ fn streaming_processcontainer_bidirectional_stdio() {
network: None,
ui: None,
timeout_ms: None,
capture_denials: None,
};
let mut request = build_request(&policy, None).expect("build_request");
// `cmd /c more` echoes stdin to stdout until EOF, then exits.
Expand Down
2 changes: 2 additions & 0 deletions src/core/mxc_engine/src/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ mod tests {
network: None,
ui: None,
timeout_ms: None,
capture_denials: None,
}
}

Expand Down Expand Up @@ -329,6 +330,7 @@ mod tests {
network: None,
ui: None,
timeout_ms: None,
capture_denials: None,
};
let mut request = build_request(&policy, None).expect("build_request");
request.set_script("echo hi");
Expand Down
Loading
Loading