Route Windows sandbox proxy traffic by restricting SID (#34613)

## Why

Elevated Windows sandboxes need stable managed-proxy ports while preserving the network policy and environment attribution of each sandboxed process.

## What changed

- Keep shared HTTP and SOCKS5 loopback ingress listeners alive across managed-proxy instances.
- Add a per-route restricting SID to elevated sandbox tokens and dispatch incoming connections to the matching proxy policy after attributing the client process.
- Reject connections without exactly one registered route, remove routes when their proxy handle is dropped, and keep unsandboxed Windows launches off the managed ingress.
- Provision the elevated sandbox with the configured proxy ports and local-binding setting, honoring the selected profile and CLI overrides.

## Testing

- Add Windows unit tests for TCP ownership attribution, route selection, restricting-token propagation, and setup settings.
- Add an end-to-end Windows test covering stable ports, isolated environment policies, HTTP and SOCKS5 routing, and route teardown.

GitOrigin-RevId: 783fac6e0f904dc9bb1955b75d4a5895e8bb9690
This commit is contained in:
iceweasel-oai authored and copyberry committed 2026-07-21 21:06:04 +00:00
1 parent dfd2d8133c
commit 999a715089
40 files changed
+2792 -191

No files matched your search

@@ -273,19 +273,37 @@ fn spawn_ipc_process(req: &SpawnRequest) -> Result<IpcSpawnedProcess> {
if cap_psids.is_empty() {
anyhow::bail!("runner: empty capability SID list");
}
let network_proxy_restricting_sid = req
.network_proxy_restricting_sid
.as_deref()
.map(LocalSid::from_string)
.transpose()
.context("ConvertStringSidToSidW failed for network proxy restricting SID")?;
// The token helpers still take raw SID pointers, but we keep ownership in `LocalSid`
// wrappers for as long as possible. That way any failure after SID parsing but before the
// child is fully spawned still releases the backing LocalAlloc memory automatically.
let cap_psid_ptrs: Vec<*mut _> = cap_psids.iter().map(LocalSid::as_ptr).collect();
let additional_restricting_sid_ptrs: Vec<*mut _> = network_proxy_restricting_sid
.iter()
.map(LocalSid::as_ptr)
.collect();
let base = OwnedWinHandle::new(unsafe { get_current_token_for_restriction()? });
let h_token = OwnedWinHandle::new(unsafe {
match token_mode {
WindowsSandboxTokenMode::ReadOnlyCapability => {
create_readonly_token_with_caps_and_user_from(base.raw(), &cap_psid_ptrs)
create_readonly_token_with_caps_and_user_from(
base.raw(),
&cap_psid_ptrs,
&additional_restricting_sid_ptrs,
)
}
WindowsSandboxTokenMode::WritableRootsCapability => {
create_workspace_write_token_with_caps_and_user_from(base.raw(), &cap_psid_ptrs)
create_workspace_write_token_with_caps_and_user_from(
base.raw(),
&cap_psid_ptrs,
&additional_restricting_sid_ptrs,
)
}
}
}?);
@@ -26,7 +26,7 @@ use std::path::PathBuf;
const MAX_FRAME_LEN: usize = 8 * 1024 * 1024;
/// Protocol version shared by the parent process and elevated command runner.
pub const IPC_PROTOCOL_VERSION: u8 = 4;
pub const IPC_PROTOCOL_VERSION: u8 = 5;
/// Length-prefixed, JSON-encoded frame.
#[derive(Debug, Serialize, Deserialize, Clone)]
@@ -65,6 +65,9 @@ pub struct SpawnRequest {
pub codex_home: PathBuf,
pub real_codex_home: PathBuf,
pub cap_sids: Vec<String>,
/// Optional managed-network identity added only to the child's restricting SID set.
#[serde(default)]
pub network_proxy_restricting_sid: Option<String>,
pub timeout_ms: Option<u64>,
pub tty: bool,
#[serde(default)]
@@ -224,6 +227,7 @@ mod tests {
codex_home: PathBuf::from(r"C:\codex"),
real_codex_home: PathBuf::from(r"C:\Users\codex"),
cap_sids: vec!["S-1-15-3-1024-1".to_string()],
network_proxy_restricting_sid: Some("S-1-5-21-100-200-300-400".to_string()),
timeout_ms: Some(1000),
tty: false,
stdin_open: false,
@@ -245,6 +249,10 @@ mod tests {
};
assert_eq!(PermissionProfile::read_only(), payload.permission_profile);
assert_eq!(workspace_roots, payload.workspace_roots);
assert_eq!(
Some("S-1-5-21-100-200-300-400"),
payload.network_proxy_restricting_sid.as_deref()
);
}
#[test]
@@ -15,6 +15,7 @@ pub struct ElevatedSandboxProfileCaptureRequest<'a> {
pub cancellation: Option<crate::WindowsSandboxCancellationToken>,
pub use_private_desktop: bool,
pub proxy_enforced: bool,
pub network_proxy_restricting_sid: Option<String>,
pub read_roots_override: Option<&'a [PathBuf]>,
pub read_roots_include_platform_defaults: bool,
pub write_roots_override: Option<&'a [PathBuf]>,
@@ -110,6 +111,7 @@ mod windows_impl {
cancellation,
use_private_desktop,
proxy_enforced,
network_proxy_restricting_sid,
read_roots_override,
read_roots_include_platform_defaults,
write_roots_override,
@@ -190,6 +192,7 @@ mod windows_impl {
codex_home: sandbox_base.clone(),
real_codex_home: codex_home.to_path_buf(),
cap_sids,
network_proxy_restricting_sid,
timeout_ms,
tty: false,
stdin_open: false,
+9
View File
@@ -44,6 +44,15 @@ pub enum WindowsSandboxProxySettingsMode {
Preserve,
}
/// Network settings installed by an administrator during managed Windows sandbox setup.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct WindowsSandboxProvisioningSettings {
/// Loopback proxy ports permitted for the offline sandbox identity.
pub proxy_ports: Vec<u16>,
/// Whether the offline sandbox identity may exchange arbitrary loopback traffic.
pub allow_local_binding: bool,
}
#[cfg(target_os = "windows")]
mod acl;
#[cfg(target_os = "windows")]
+25 -4
View File
@@ -705,6 +705,10 @@ const PROXY_ENV_KEYS: &[&str] = &[
"wss_proxy",
];
const ALLOW_LOCAL_BINDING_ENV_KEY: &str = "CODEX_NETWORK_ALLOW_LOCAL_BINDING";
// Internal wire format shared with network-proxy/src/proxy.rs. The value is a comma-separated,
// sorted list of non-zero loopback proxy ports used only when computing the Windows offline
// sandbox setup marker.
const WINDOWS_SANDBOX_PROXY_PORTS_ENV_KEY: &str = "CODEX_WINDOWS_SANDBOX_PROXY_PORTS";
pub(crate) fn offline_proxy_settings_from_env(
env_map: &HashMap<String, String>,
@@ -744,6 +748,14 @@ pub(crate) fn proxy_ports_from_env(env_map: &HashMap<String, String>) -> Vec<u16
ports.insert(port);
}
}
if let Some(value) = env_map.get(WINDOWS_SANDBOX_PROXY_PORTS_ENV_KEY) {
ports.extend(
value
.split(',')
.filter_map(|port| port.trim().parse::<u16>().ok())
.filter(|port| *port != 0),
);
}
ports.into_iter().collect()
}
@@ -1034,7 +1046,11 @@ fn run_elevated_setup_inner(
run_setup_exe(&payload, needs_elevation, request.codex_home)
}
pub fn run_elevated_provisioning_setup(codex_home: &Path, real_user: &str) -> Result<()> {
pub fn run_elevated_provisioning_setup(
codex_home: &Path,
real_user: &str,
settings: crate::WindowsSandboxProvisioningSettings,
) -> Result<()> {
let sbx_dir = sandbox_dir(codex_home);
std::fs::create_dir_all(&sbx_dir).map_err(|err| {
failure(
@@ -1063,8 +1079,8 @@ pub fn run_elevated_provisioning_setup(codex_home: &Path, real_user: &str) -> Re
write_roots: Vec::new(),
deny_read_paths: Vec::new(),
deny_write_paths: Vec::new(),
proxy_ports: Vec::new(),
allow_local_binding: false,
proxy_ports: settings.proxy_ports,
allow_local_binding: settings.allow_local_binding,
otel: codex_otel::global_statsig_metrics_settings(),
real_user: real_user.to_string(),
mode: SetupMode::ProvisionOnly,
@@ -1264,6 +1280,7 @@ fn filter_sensitive_write_roots(mut roots: Vec<PathBuf>, codex_home: &Path) -> V
#[cfg(test)]
mod tests {
use super::WINDOWS_PLATFORM_DEFAULT_READ_ROOTS;
use super::WINDOWS_SANDBOX_PROXY_PORTS_ENV_KEY;
use super::build_payload_roots;
use super::find_setup_exe_for_current_exe;
use super::gather_full_read_roots_for_permissions;
@@ -1599,8 +1616,12 @@ mod tests {
"HTTPS_PROXY".to_string(),
"https://example.com:9999".to_string(),
);
env.insert(
WINDOWS_SANDBOX_PROXY_PORTS_ENV_KEY.to_string(),
"8080,43129,0,invalid, 43128,65536".to_string(),
);
assert_eq!(proxy_ports_from_env(&env), vec![1081, 8080]);
assert_eq!(proxy_ports_from_env(&env), vec![1081, 8080, 43128, 43129]);
}
#[test]
+33 -6
View File
@@ -381,7 +381,8 @@ pub unsafe fn create_workspace_write_token_with_caps_from(
create_token_with_caps_from(base_token, psid_capabilities, &[])
}
/// Create a restricted token that includes all provided capability SIDs plus the token user SID.
/// Create a restricted token that includes all provided capability SIDs, the token user SID, and
/// any additional restricting SIDs.
///
/// This is intended for the elevated sandbox backend, where the token user is the dedicated
/// sandbox account rather than the real signed-in user.
@@ -391,10 +392,13 @@ pub unsafe fn create_workspace_write_token_with_caps_from(
pub unsafe fn create_workspace_write_token_with_caps_and_user_from(
base_token: HANDLE,
psid_capabilities: &[*mut c_void],
additional_restricting_sids: &[*mut c_void],
) -> Result<HANDLE> {
let mut user_sid_bytes = get_user_sid_bytes(base_token)?;
let psid_user = user_sid_bytes.as_mut_ptr() as *mut c_void;
create_token_with_caps_from(base_token, psid_capabilities, &[psid_user])
create_token_with_caps_user_and_additional_restrictions_from(
base_token,
psid_capabilities,
additional_restricting_sids,
)
}
/// Create a restricted token that includes all provided capability SIDs.
@@ -408,7 +412,8 @@ pub unsafe fn create_readonly_token_with_caps_from(
create_token_with_caps_from(base_token, psid_capabilities, &[])
}
/// Create a restricted token that includes all provided capability SIDs plus the token user SID.
/// Create a restricted token that includes all provided capability SIDs, the token user SID, and
/// any additional restricting SIDs.
///
/// This is intended for the elevated sandbox backend, where the token user is the dedicated
/// sandbox account rather than the real signed-in user.
@@ -418,10 +423,26 @@ pub unsafe fn create_readonly_token_with_caps_from(
pub unsafe fn create_readonly_token_with_caps_and_user_from(
base_token: HANDLE,
psid_capabilities: &[*mut c_void],
additional_restricting_sids: &[*mut c_void],
) -> Result<HANDLE> {
create_token_with_caps_user_and_additional_restrictions_from(
base_token,
psid_capabilities,
additional_restricting_sids,
)
}
unsafe fn create_token_with_caps_user_and_additional_restrictions_from(
base_token: HANDLE,
psid_capabilities: &[*mut c_void],
additional_restricting_sids: &[*mut c_void],
) -> Result<HANDLE> {
let mut user_sid_bytes = get_user_sid_bytes(base_token)?;
let psid_user = user_sid_bytes.as_mut_ptr() as *mut c_void;
create_token_with_caps_from(base_token, psid_capabilities, &[psid_user])
let mut extra_restricting_sids = Vec::with_capacity(additional_restricting_sids.len() + 1);
extra_restricting_sids.push(psid_user);
extra_restricting_sids.extend_from_slice(additional_restricting_sids);
create_token_with_caps_from(base_token, psid_capabilities, &extra_restricting_sids)
}
unsafe fn create_token_with_caps_from(
@@ -472,6 +493,8 @@ unsafe fn create_token_with_caps_from(
return Err(anyhow!("CreateRestrictedToken failed: {}", GetLastError()));
}
// Additional restricting SIDs are identity markers, not capabilities. Deliberately exclude
// them from the default DACL so possessing a route identity cannot grant object access.
let mut dacl_sids: Vec<*mut c_void> = Vec::with_capacity(psid_capabilities.len() + 2);
dacl_sids.push(psid_logon);
dacl_sids.push(psid_everyone);
@@ -481,3 +504,7 @@ unsafe fn create_token_with_caps_from(
enable_single_privilege(new_token, "SeChangeNotifyPrivilege")?;
Ok(new_token)
}
#[cfg(test)]
#[path = "token_tests.rs"]
mod tests;
@@ -0,0 +1,69 @@
use super::*;
use windows_sys::Win32::Security::EqualSid;
use windows_sys::Win32::Security::TokenRestrictedSids;
unsafe fn token_has_restricting_sid(token: HANDLE, expected_sid: *mut c_void) -> Result<bool> {
let mut needed = 0;
GetTokenInformation(
token,
TokenRestrictedSids,
std::ptr::null_mut(),
0,
&mut needed,
);
if needed == 0 {
return Err(anyhow!(
"GetTokenInformation(TokenRestrictedSids) size query failed: {}",
GetLastError()
));
}
let mut buffer = vec![0_u8; needed as usize];
if GetTokenInformation(
token,
TokenRestrictedSids,
buffer.as_mut_ptr().cast(),
needed,
&mut needed,
) == 0
{
return Err(anyhow!(
"GetTokenInformation(TokenRestrictedSids) failed: {}",
GetLastError()
));
}
let group_count = std::ptr::read_unaligned(buffer.as_ptr().cast::<u32>()) as usize;
let after_count = buffer.as_ptr().add(std::mem::size_of::<u32>()) as usize;
let align = std::mem::align_of::<SID_AND_ATTRIBUTES>();
let entries_addr = (after_count + (align - 1)) & !(align - 1);
let restricting_sids =
std::slice::from_raw_parts(entries_addr as *const SID_AND_ATTRIBUTES, group_count);
Ok(restricting_sids
.iter()
.any(|entry| EqualSid(entry.Sid, expected_sid) != 0))
}
#[test]
fn elevated_token_includes_network_proxy_restricting_sid() -> Result<()> {
let capability_sid = LocalSid::from_string("S-1-5-21-10-20-30-40")?;
let network_proxy_sid = LocalSid::from_string("S-1-5-21-50-60-70-80")?;
let base_token = unsafe { get_current_token_for_restriction()? };
let restricted_token = unsafe {
create_readonly_token_with_caps_and_user_from(
base_token,
&[capability_sid.as_ptr()],
&[network_proxy_sid.as_ptr()],
)?
};
let has_network_proxy_sid =
unsafe { token_has_restricting_sid(restricted_token, network_proxy_sid.as_ptr()) };
unsafe {
CloseHandle(restricted_token);
CloseHandle(base_token);
}
assert!(has_network_proxy_sid?);
Ok(())
}
@@ -116,6 +116,7 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated_for_permission_profil
cwd: &Path,
mut env_map: HashMap<String, String>,
proxy_enforced: bool,
network_proxy_restricting_sid: Option<String>,
proxy_settings_mode: crate::WindowsSandboxProxySettingsMode,
timeout_ms: Option<u64>,
read_roots_override: Option<&[PathBuf]>,
@@ -171,6 +172,7 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated_for_permission_profil
codex_home: elevated.sandbox_base,
real_codex_home: codex_home.to_path_buf(),
cap_sids: elevated.cap_sids,
network_proxy_restricting_sid,
timeout_ms,
tty,
stdin_open,
@@ -76,6 +76,7 @@ fn retry_uses_original_unified_exec_request_and_stops_after_second_failure() {
codex_home: PathBuf::from(r"C:\Users\codex\.sandbox"),
real_codex_home: PathBuf::from(r"C:\Users\codex"),
cap_sids: vec!["S-1-15-3-1024-1".to_string()],
network_proxy_restricting_sid: Some("S-1-5-21-100-200-300-400".to_string()),
timeout_ms: Some(5_000),
tty: true,
stdin_open: true,
@@ -10,6 +10,7 @@
mod backends;
use anyhow::Result;
use anyhow::bail;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::PermissionProfile;
use codex_utils_absolute_path::AbsolutePathBuf;
@@ -31,6 +32,7 @@ pub struct WindowsSandboxSessionRequest<'a> {
pub env_map: HashMap<String, String>,
pub windows_sandbox_level: WindowsSandboxLevel,
pub proxy_enforced: bool,
pub network_proxy_restricting_sid: Option<String>,
pub proxy_settings_mode: crate::WindowsSandboxProxySettingsMode,
pub timeout_ms: Option<u64>,
pub read_roots_override: Option<&'a [PathBuf]>,
@@ -57,6 +59,7 @@ pub async fn spawn_windows_sandbox_session_for_level(
request.cwd,
request.env_map,
request.proxy_enforced,
request.network_proxy_restricting_sid,
request.proxy_settings_mode,
request.timeout_ms,
request.read_roots_override,
@@ -70,6 +73,9 @@ pub async fn spawn_windows_sandbox_session_for_level(
)
.await
} else {
if request.network_proxy_restricting_sid.is_some() {
bail!("network proxy restricting SID requires the elevated Windows sandbox backend");
}
spawn_windows_sandbox_session_legacy(
request.permission_profile,
request.workspace_roots,
@@ -129,6 +135,7 @@ pub async fn spawn_windows_sandbox_session_elevated_for_permission_profile(
cwd: &Path,
env_map: HashMap<String, String>,
proxy_enforced: bool,
network_proxy_restricting_sid: Option<String>,
timeout_ms: Option<u64>,
read_roots_override: Option<&[PathBuf]>,
read_roots_include_platform_defaults: bool,
@@ -147,6 +154,7 @@ pub async fn spawn_windows_sandbox_session_elevated_for_permission_profile(
cwd,
env_map,
proxy_enforced,
network_proxy_restricting_sid,
crate::WindowsSandboxProxySettingsMode::Reconcile,
timeout_ms,
read_roots_override,
@@ -24,6 +24,7 @@ const CODEX_HOME_FLAG: &str = "--codex-home";
const DENY_READ_PATHS_JSON_FLAG: &str = "--deny-read-paths-json";
const DENY_WRITE_PATHS_JSON_FLAG: &str = "--deny-write-paths-json";
const ENV_JSON_FLAG: &str = "--env-json";
const NETWORK_PROXY_RESTRICTING_SID_FLAG: &str = "--network-proxy-restricting-sid";
const PERMISSION_PROFILE_FLAG: &str = "--permission-profile";
const PRIVATE_DESKTOP_FLAG: &str = "--windows-sandbox-private-desktop";
const PRESERVE_PROXY_SETTINGS_FLAG: &str = "--preserve-proxy-settings";
@@ -44,6 +45,7 @@ pub fn create_windows_sandbox_command_args_for_permission_profile(
windows_sandbox_level: WindowsSandboxLevel,
windows_sandbox_private_desktop: bool,
proxy_enforced: bool,
network_proxy_restricting_sid: Option<&str>,
proxy_settings_mode: crate::WindowsSandboxProxySettingsMode,
read_roots_override: Option<&[PathBuf]>,
read_roots_include_platform_defaults: bool,
@@ -84,6 +86,10 @@ pub fn create_windows_sandbox_command_args_for_permission_profile(
if proxy_enforced {
args.push(PROXY_ENFORCED_FLAG.to_string());
}
if let Some(network_proxy_restricting_sid) = network_proxy_restricting_sid {
args.push(NETWORK_PROXY_RESTRICTING_SID_FLAG.to_string());
args.push(network_proxy_restricting_sid.to_string());
}
if proxy_settings_mode == crate::WindowsSandboxProxySettingsMode::Preserve {
args.push(PRESERVE_PROXY_SETTINGS_FLAG.to_string());
}
@@ -159,6 +165,7 @@ struct WindowsSandboxWrapperRequest {
windows_sandbox_level: WindowsSandboxLevel,
windows_sandbox_private_desktop: bool,
proxy_enforced: bool,
network_proxy_restricting_sid: Option<String>,
proxy_settings_mode: crate::WindowsSandboxProxySettingsMode,
read_roots_override: Option<Vec<PathBuf>>,
read_roots_include_platform_defaults: bool,
@@ -182,6 +189,7 @@ async fn run_windows_sandbox_wrapper_request(request: WindowsSandboxWrapperReque
env_map: request.env_map,
windows_sandbox_level: request.windows_sandbox_level,
proxy_enforced: request.proxy_enforced,
network_proxy_restricting_sid: request.network_proxy_restricting_sid,
proxy_settings_mode: request.proxy_settings_mode,
timeout_ms: None,
read_roots_override: request.read_roots_override.as_deref(),
@@ -208,6 +216,7 @@ fn parse_windows_sandbox_wrapper_args(args: Vec<String>) -> Result<WindowsSandbo
let mut windows_sandbox_level = None;
let mut windows_sandbox_private_desktop = false;
let mut proxy_enforced = false;
let mut network_proxy_restricting_sid = None;
let mut proxy_settings_mode = crate::WindowsSandboxProxySettingsMode::Reconcile;
let mut read_roots_override = None;
let mut read_roots_include_platform_defaults = false;
@@ -252,6 +261,9 @@ fn parse_windows_sandbox_wrapper_args(args: Vec<String>) -> Result<WindowsSandbo
proxy_settings_mode = crate::WindowsSandboxProxySettingsMode::Preserve;
}
PROXY_ENFORCED_FLAG => proxy_enforced = true,
NETWORK_PROXY_RESTRICTING_SID_FLAG => {
network_proxy_restricting_sid = Some(next_flag_value(&mut args, &arg)?);
}
READ_ROOTS_INCLUDE_PLATFORM_DEFAULTS_FLAG => {
read_roots_include_platform_defaults = true;
}
@@ -293,6 +305,7 @@ fn parse_windows_sandbox_wrapper_args(args: Vec<String>) -> Result<WindowsSandbo
.ok_or_else(|| anyhow!("missing required {SANDBOX_LEVEL_FLAG}"))?,
windows_sandbox_private_desktop,
proxy_enforced,
network_proxy_restricting_sid,
proxy_settings_mode,
read_roots_override,
read_roots_include_platform_defaults,
@@ -14,6 +14,7 @@ use super::COMMAND_CWD_FLAG;
use super::DENY_READ_PATHS_JSON_FLAG;
use super::DENY_WRITE_PATHS_JSON_FLAG;
use super::ENV_JSON_FLAG;
use super::NETWORK_PROXY_RESTRICTING_SID_FLAG;
use super::PERMISSION_PROFILE_FLAG;
use super::PRESERVE_PROXY_SETTINGS_FLAG;
use super::PRIVATE_DESKTOP_FLAG;
@@ -62,6 +63,7 @@ fn windows_wrapper_args_round_trip() {
WindowsSandboxLevel::Elevated,
/*windows_sandbox_private_desktop*/ true,
/*proxy_enforced*/ true,
/*network_proxy_restricting_sid*/ Some("S-1-5-21-100-200-300-400"),
crate::WindowsSandboxProxySettingsMode::Preserve,
Some(read_roots_override.as_slice()),
/*read_roots_include_platform_defaults*/ true,
@@ -80,6 +82,7 @@ fn windows_wrapper_args_round_trip() {
assert!(args.contains(&SANDBOX_LEVEL_FLAG.to_string()));
assert!(args.contains(&PRIVATE_DESKTOP_FLAG.to_string()));
assert!(args.contains(&PROXY_ENFORCED_FLAG.to_string()));
assert!(args.contains(&NETWORK_PROXY_RESTRICTING_SID_FLAG.to_string()));
assert!(args.contains(&PRESERVE_PROXY_SETTINGS_FLAG.to_string()));
assert!(args.contains(&READ_ROOTS_JSON_FLAG.to_string()));
assert!(args.contains(&READ_ROOTS_INCLUDE_PLATFORM_DEFAULTS_FLAG.to_string()));
@@ -101,6 +104,10 @@ fn windows_wrapper_args_round_trip() {
assert_eq!(parsed.windows_sandbox_level, WindowsSandboxLevel::Elevated);
assert_eq!(parsed.windows_sandbox_private_desktop, true);
assert_eq!(parsed.proxy_enforced, true);
assert_eq!(
parsed.network_proxy_restricting_sid.as_deref(),
Some("S-1-5-21-100-200-300-400")
);
assert_eq!(
parsed.proxy_settings_mode,
crate::WindowsSandboxProxySettingsMode::Preserve