test(mxc): sync Windows policies and isolated CLI harness fixes

Signed-off-by: Drew Newberry <anewberry@nvidia.com>
This commit is contained in:
Drew Newberry
2026-10-13 08:24:12 -07:00
parent 3e4b8faf9d
commit ccf8e87293
10 changed files with 513 additions and 19 deletions
@@ -456,6 +456,12 @@ skill or design doc.
## Final Report Checklist
When syncing from the `windows` branch, consult the selective integration
checkpoint in `crates/openshell-driver-mxc/WINDOWS-INTEGRATION.md`. Preserve
the host-supervisor/Windows-boundary architecture rather than restoring legacy
relay/proxy demo wrappers. The WebSocket, OpenClaw, and inference policy fixtures
have parser/mapper tests; do not report them as live runtime scenarios.
Every substantial Windows build run should report:
| Item | Required detail |
+15 -1
View File
@@ -158,7 +158,10 @@ from Cargo's passed count.
The E2E runner uses native release artifacts (override with `-BinaryDir`), OpenSSL
on PATH for disposable Ed25519 keys, and per-run writable fixtures under
`target/windows-e2e-results`. It preserves the tracked gateway TOML and isolates
CLI registration via `XDG_CONFIG_HOME`. Signing keys have an owner-only DACL,
CLI registration via disposable APPDATA, LOCALAPPDATA, and XDG directories.
Inherited gateway overrides are cleared for the run and restored exactly
(including empty versus absent entries). The caller's gateway aliases are not
modified. Signing keys have an owner-only DACL,
remain outside result bundles, and are removed unless `-KeepRunning` is requested.
E2E runs real MXC only: there is no mock mode, and an inherited
@@ -181,3 +184,14 @@ mode, exposes a named endpoint with `openshell service expose`, and verifies two
unique GET responses through gateway host-based routing. It deletes the endpoint
and requires HTTP 404 afterward. Requests connect only to the gateway with the
exposed URL's Host header, so no wildcard DNS setup is required.
The `ws-agent.yaml` and `openclaw-gateway.yaml` policies preserve the Windows
branch's workload grants. `inference.yaml` is a template: replace its filesystem,
curl executable, inference host, and port placeholders before use. Authorize
curl, which owns the network socket, not its cmd launcher. These policies have
parser/mapper regression coverage; they do not add live WebSocket, OpenClaw,
or provider-backed inference scenarios to the six-scenario E2E runner.
With `-KeepRunning`, the runner restores the caller's environment but retains
the disposable CLI/runtime directory for the live gateway and prints its path.
Remove that directory only after stopping the gateway.
@@ -0,0 +1,22 @@
# Windows integration checkpoint
Reviewed Windows branch through
`d89e4359b810d3d7205bb7d4a706f3c773a59e1f` (PR #4252).
This is a selective port, not a branch merge or a claim of complete demo parity.
- #4252 (`d89e4359b`): MXC 1.0.0 schema, directional networking, and UI contract
already ported in `ce2ca474a`.
- #4199 (`f6ecca206`): authorize the socket-owning curl executable in the
inference policy template and real HTTPS test, rather than its cmd launcher.
- #4192 (`ed35eb9f3`): disposable CLI state, cleared inherited gateway
overrides, exact environment restoration, and strict fresh gateway
registration in the E2E and OCSF runners.
- Restore the WebSocket and OpenClaw policy fixtures with their original
grants, updating comments to the host-supervisor/Windows-boundary architecture.
Keep the always-present `openshell-windows-sandbox`, authenticated Sandbox
Protocol, and host isolation-backend supervisor. Do not restore the old relay
executable, driver-owned CONNECT proxy, or legacy demo/task wrappers requiring
them. Provider-backed inference, live WebSocket, OpenClaw, and OCSF demo coverage
are not implied by policy parsing or by the six core E2E scenarios. Network-fence
evidence remains the documented compatibility TODO, not a verified guarantee.
@@ -0,0 +1,32 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
# OpenClaw gateway policy fixture ported from windows.
# Stage Node, OpenClaw, home, temporary files, and logs under this writable path.
# Node loads user32/gdi32: explicitly permit graphical UI to disable MXC 1.0
# win32k lockdown. Clipboard and input injection remain disabled.
# The host supervisor governs the Node outbound grant. Managed ingress uses the
# Windows sandbox boundary; neither path needs a driver-owned proxy or relay.
# Fixture validation is not live OpenClaw coverage or verified exclusive fencing.
version: 1
filesystem_policy:
include_workdir: false
read_only: []
read_write:
- "C:/openshell-openclaw"
ui:
allow_graphical_ui: true
clipboard: none
allow_input_injection: false
network_policies:
qualification_allowed:
name: qualification-allowed
endpoints:
- host: example.com
port: 443
protocol: tcp
binaries:
- path: "C:/openshell-openclaw/node.exe"
@@ -0,0 +1,16 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
# WebSocket-agent policy fixture ported from windows.
# Stage the workload under the read-only directory; adapt the path before use.
# Managed ingress uses the always-present Windows sandbox boundary and host
# supervisor, not an optional relay binary or a driver-owned network proxy.
# No network_policies means no authorized outbound destinations. This fixture
# does not itself exercise a live WebSocket session or prove network fencing.
version: 1
filesystem_policy:
include_workdir: false
read_only:
- "C:/work/openshell-mxc-ws"
read_write: []
@@ -0,0 +1,25 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
# Policy template: replace the path, host, and port placeholders before use.
# Authorize the socket-owning curl executable, not its cmd.exe launcher (#4199).
# The host supervisor evaluates REST policy and injects provider credentials.
version: 1
filesystem_policy:
include_workdir: false
read_only: []
read_write:
- "__OPENSHELL_DEMO_SHARE__"
network_policies:
nvidia_inference:
name: nvidia-inference
endpoints:
- host: "__INFERENCE_HOST__"
port: __INFERENCE_PORT__
protocol: rest
access: read-write
enforcement: enforce
binaries:
- path: '__CURL_EXE__'
@@ -76,12 +76,120 @@ if (-not $BinaryDir) {
$BinaryDir = Join-Path $targetRoot "$target/release"
}
$savedEnv = @{}
foreach ($name in @("OPENSHELL_GATEWAY_CONFIG", "OPENSHELL_GATEWAY", "OPENSHELL_MXC_MOCK_WXC", "OPENSHELL_WXC_EXEC_PATH", "OPENSHELL_COMPUTE_DRIVER", "OPENSHELL_MXC_SHARE_DIR", "XDG_CONFIG_HOME")) {
$savedEnv[$name] = [Environment]::GetEnvironmentVariable($name, "Process")
$processEnvironment = [Environment]::GetEnvironmentVariables("Process")
foreach ($name in @("OPENSHELL_MXC_MOCK_WXC", "OPENSHELL_WXC_EXEC_PATH", "OPENSHELL_COMPUTE_DRIVER", "OPENSHELL_MXC_SHARE_DIR")) {
$exists = $processEnvironment.Contains($name)
$savedEnv[$name] = [pscustomobject]@{
Exists = $exists
Value = if ($exists) { [string] $processEnvironment[$name] } else { $null }
}
}
$secretDir = Join-Path $resultsRoot "keys-$stamp-$([guid]::NewGuid().ToString('N'))"
$transcriptStarted = $false
$cliStateRoot = $null
$cliEnvironmentSnapshot = @{}
$cliEnvironmentNames = @(
"APPDATA", "LOCALAPPDATA", "XDG_CONFIG_HOME", "XDG_STATE_HOME", "XDG_DATA_HOME",
"OPENSHELL_GATEWAY", "OPENSHELL_GATEWAY_ENDPOINT", "OPENSHELL_GATEWAY_INSECURE",
"OPENSHELL_GATEWAY_CONFIG", "OPENSHELL_GATEWAY_NAME"
)
function Set-ProcessEnvironmentVariableExact {
param(
[Parameter(Mandatory = $true)]
[string] $Name,
[Parameter(Mandatory = $true)]
[bool] $Exists,
[AllowNull()]
[string] $Value
)
if (-not $Exists) {
Remove-Item "Env:$Name" -ErrorAction SilentlyContinue
return
}
if ($Value.Length -eq 0) {
# Windows PowerShell 5.1 maps an empty value passed through
# Environment.SetEnvironmentVariable to deletion. Call Win32 directly
# so an inherited empty entry remains distinguishable from absence.
if (-not ("OpenShellMxcProcessEnvironmentNative" -as [type])) {
Add-Type -TypeDefinition @'
using System.Runtime.InteropServices;
public static class OpenShellMxcProcessEnvironmentNative
{
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetEnvironmentVariable(string name, string value);
}
'@
}
if (-not [OpenShellMxcProcessEnvironmentNative]::SetEnvironmentVariable($Name, [string]::Empty)) {
$errorCode = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
throw "failed to restore empty process environment variable '$Name' (Win32 error $errorCode)"
}
return
}
[Environment]::SetEnvironmentVariable($Name, $Value, "Process")
}
function Enter-IsolatedCliEnvironment {
$processEnvironment = [Environment]::GetEnvironmentVariables("Process")
foreach ($name in $cliEnvironmentNames) {
$exists = $processEnvironment.Contains($name)
$script:cliEnvironmentSnapshot[$name] = [pscustomobject]@{
Exists = $exists
Value = if ($exists) { [string] $processEnvironment[$name] } else { $null }
}
}
$script:cliStateRoot = Join-Path ([IO.Path]::GetTempPath()) "openshell-mxc-e2e-cli-$PID-$([Guid]::NewGuid().ToString('N'))"
$isolatedPaths = @{
APPDATA = Join-Path $script:cliStateRoot "appdata"
LOCALAPPDATA = Join-Path $script:cliStateRoot "localappdata"
XDG_CONFIG_HOME = Join-Path $script:cliStateRoot "xdg-config"
XDG_STATE_HOME = Join-Path $script:cliStateRoot "xdg-state"
XDG_DATA_HOME = Join-Path $script:cliStateRoot "xdg-data"
}
try {
New-Item -ItemType Directory -Force -Path @($isolatedPaths.Values) | Out-Null
foreach ($entry in $isolatedPaths.GetEnumerator()) {
[Environment]::SetEnvironmentVariable($entry.Key, $entry.Value, "Process")
}
foreach ($name in $cliEnvironmentNames | Where-Object { -not $isolatedPaths.ContainsKey($_) }) {
Remove-Item "Env:$name" -ErrorAction SilentlyContinue
}
} catch {
Exit-IsolatedCliEnvironment
throw
}
}
function Exit-IsolatedCliEnvironment {
foreach ($name in $cliEnvironmentNames) {
if (-not $script:cliEnvironmentSnapshot.ContainsKey($name)) { continue }
$snapshot = $script:cliEnvironmentSnapshot[$name]
Set-ProcessEnvironmentVariableExact -Name $name -Exists $snapshot.Exists -Value $snapshot.Value
}
if ($script:cliStateRoot -and (Test-Path -LiteralPath $script:cliStateRoot)) {
if ($KeepRunning -and $gw -and -not $gw.HasExited) {
Write-Host "Retaining isolated CLI/runtime state for live gateway: $script:cliStateRoot"
return
}
# Resolve the generated target before recursive cleanup; never remove an
# arbitrary caller path or the temporary directory itself.
$cleanupPath = [IO.Path]::GetFullPath($script:cliStateRoot)
$tempPrefix = [IO.Path]::GetFullPath([IO.Path]::GetTempPath()).TrimEnd('\') + '\'
if (-not $cleanupPath.StartsWith($tempPrefix, [StringComparison]::OrdinalIgnoreCase) -or
(Split-Path $cleanupPath -Leaf) -notmatch '^openshell-mxc-(e2e|ocsf)-cli-[0-9]+-[0-9a-f]{32}$') {
throw "refusing unsafe CLI state cleanup: $cleanupPath"
}
Remove-Item -LiteralPath $cleanupPath -Recurse -Force -ErrorAction SilentlyContinue
}
}
function Step([string]$m) { Write-Host "`n=== $m ===" -ForegroundColor Cyan }
function Info([string]$m) { Write-Host " $m" }
function Ok([string]$m) { Write-Host "[OK] $m" -ForegroundColor Green }
@@ -247,14 +355,13 @@ function Stop-Gw($p) {
function Register-Cli {
if ($script:registered) { return }
$env:OPENSHELL_GATEWAY = ""
$addResult = Invoke-NativeCaptured $cli @(
"gateway", "add", "http://127.0.0.1:$Port", "--local", "--name", $GatewayName
)
$addText = ($addResult.Output -join "`n")
if ($addText) { $addResult.Output | ForEach-Object { Info $_ } }
if ($addResult.ExitCode -ne 0 -and $addText -notmatch '(?i)already exists') {
if ($addResult.ExitCode -ne 0) {
throw "gateway add failed (exit $($addResult.ExitCode)): $addText"
}
@@ -510,6 +617,7 @@ $backendProbe = @{ Live = $false; Reason = "not probed" }
$runId = Get-Date -Format 'MMddHHmmss'
try {
Enter-IsolatedCliEnvironment
# Start the transcript inside the guarded region so a Start-Transcript failure
# is caught and the results bundle is still produced. Pre-flight runs
# immediately below, so the transcript still captures the whole run.
@@ -541,7 +649,6 @@ try {
# Capture the pristine TOML once; every scenario renders a fresh copy from this.
$tomlBase = Get-Content $tomlTemplate -Raw
$env:XDG_CONFIG_HOME = Join-Path $resultDir "cli-config"
$openssl = (Get-Command openssl -ErrorAction Stop).Source
New-Item -ItemType Directory $secretDir | Out-Null
$ownerSid = [Security.Principal.WindowsIdentity]::GetCurrent().User.Value
@@ -929,7 +1036,11 @@ ingress works, not that direct host ingress is blocked.
}
if (Test-Path $secretDir) { Remove-Item -LiteralPath $secretDir -ErrorAction SilentlyContinue }
}
foreach ($name in $savedEnv.Keys) { [Environment]::SetEnvironmentVariable($name, $savedEnv[$name], "Process") }
Exit-IsolatedCliEnvironment
foreach ($name in $savedEnv.Keys) {
$snapshot = $savedEnv[$name]
Set-ProcessEnvironmentVariableExact -Name $name -Exists $snapshot.Exists -Value $snapshot.Value
}
}
if ($harnessError -or $failCount -gt 0) {
@@ -61,6 +61,109 @@ $resultDir = Join-Path $here "results-$stamp"
New-Item -ItemType Directory -Force $resultDir | Out-Null
Start-Transcript -Path (Join-Path $resultDir "transcript.txt") -Force | Out-Null
$cliStateRoot = $null
$cliEnvironmentSnapshot = @{}
$cliEnvironmentNames = @(
"APPDATA", "LOCALAPPDATA", "XDG_CONFIG_HOME", "XDG_STATE_HOME", "XDG_DATA_HOME",
"OPENSHELL_GATEWAY", "OPENSHELL_GATEWAY_ENDPOINT", "OPENSHELL_GATEWAY_INSECURE",
"OPENSHELL_GATEWAY_CONFIG", "OPENSHELL_GATEWAY_NAME"
)
function Set-ProcessEnvironmentVariableExact {
param(
[Parameter(Mandatory = $true)]
[string] $Name,
[Parameter(Mandatory = $true)]
[bool] $Exists,
[AllowNull()]
[string] $Value
)
if (-not $Exists) {
Remove-Item "Env:$Name" -ErrorAction SilentlyContinue
return
}
if ($Value.Length -eq 0) {
# Windows PowerShell 5.1 maps an empty value passed through
# Environment.SetEnvironmentVariable to deletion. Call Win32 directly
# so an inherited empty entry remains distinguishable from absence.
if (-not ("OpenShellMxcProcessEnvironmentNative" -as [type])) {
Add-Type -TypeDefinition @'
using System.Runtime.InteropServices;
public static class OpenShellMxcProcessEnvironmentNative
{
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetEnvironmentVariable(string name, string value);
}
'@
}
if (-not [OpenShellMxcProcessEnvironmentNative]::SetEnvironmentVariable($Name, [string]::Empty)) {
$errorCode = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
throw "failed to restore empty process environment variable '$Name' (Win32 error $errorCode)"
}
return
}
[Environment]::SetEnvironmentVariable($Name, $Value, "Process")
}
function Enter-IsolatedCliEnvironment {
$processEnvironment = [Environment]::GetEnvironmentVariables("Process")
foreach ($name in $cliEnvironmentNames) {
$exists = $processEnvironment.Contains($name)
$script:cliEnvironmentSnapshot[$name] = [pscustomobject]@{
Exists = $exists
Value = if ($exists) { [string] $processEnvironment[$name] } else { $null }
}
}
$script:cliStateRoot = Join-Path ([IO.Path]::GetTempPath()) "openshell-mxc-ocsf-cli-$PID-$([Guid]::NewGuid().ToString('N'))"
$isolatedPaths = @{
APPDATA = Join-Path $script:cliStateRoot "appdata"
LOCALAPPDATA = Join-Path $script:cliStateRoot "localappdata"
XDG_CONFIG_HOME = Join-Path $script:cliStateRoot "xdg-config"
XDG_STATE_HOME = Join-Path $script:cliStateRoot "xdg-state"
XDG_DATA_HOME = Join-Path $script:cliStateRoot "xdg-data"
}
try {
New-Item -ItemType Directory -Force -Path @($isolatedPaths.Values) | Out-Null
foreach ($entry in $isolatedPaths.GetEnumerator()) {
[Environment]::SetEnvironmentVariable($entry.Key, $entry.Value, "Process")
}
foreach ($name in $cliEnvironmentNames | Where-Object { -not $isolatedPaths.ContainsKey($_) }) {
Remove-Item "Env:$name" -ErrorAction SilentlyContinue
}
} catch {
Exit-IsolatedCliEnvironment
throw
}
}
function Exit-IsolatedCliEnvironment {
foreach ($name in $cliEnvironmentNames) {
if (-not $script:cliEnvironmentSnapshot.ContainsKey($name)) { continue }
$snapshot = $script:cliEnvironmentSnapshot[$name]
Set-ProcessEnvironmentVariableExact -Name $name -Exists $snapshot.Exists -Value $snapshot.Value
}
if ($script:cliStateRoot -and (Test-Path -LiteralPath $script:cliStateRoot)) {
if ($KeepRunning -and $gw -and -not $gw.HasExited) {
Write-Host "Retaining isolated CLI/runtime state for live gateway: $script:cliStateRoot"
return
}
# Resolve the generated target before recursive cleanup; never remove an
# arbitrary caller path or the temporary directory itself.
$cleanupPath = [IO.Path]::GetFullPath($script:cliStateRoot)
$tempPrefix = [IO.Path]::GetFullPath([IO.Path]::GetTempPath()).TrimEnd('\') + '\'
if (-not $cleanupPath.StartsWith($tempPrefix, [StringComparison]::OrdinalIgnoreCase) -or
(Split-Path $cleanupPath -Leaf) -notmatch '^openshell-mxc-(e2e|ocsf)-cli-[0-9]+-[0-9a-f]{32}$') {
throw "refusing unsafe CLI state cleanup: $cleanupPath"
}
Remove-Item -LiteralPath $cleanupPath -Recurse -Force -ErrorAction SilentlyContinue
}
}
function Step([string]$m) { Write-Host "`n=== $m ===" -ForegroundColor Cyan }
function Info([string]$m) { Write-Host " $m" }
function Ok([string]$m) { Write-Host "[OK] $m" -ForegroundColor Green }
@@ -94,6 +197,7 @@ $gatewayEtwSessions = @()
$passed = $true
try {
Enter-IsolatedCliEnvironment
# 1. Validate artifacts + privilege.
Step "Validate package artifacts"
foreach ($f in @($gateway, $cli, $supervisor, $sandbox, $policySrc, $tomlSrc)) {
@@ -238,11 +342,10 @@ try {
# 9. Register CLI -> gateway.
Step "Register CLI -> gateway"
$env:OPENSHELL_GATEWAY = ""
try { & $cli gateway add "http://127.0.0.1:$Port" --local --name $GatewayName 2>&1 | ForEach-Object { Info $_ } }
catch { Info "gateway add: $($_.Exception.Message) (continuing - likely already registered)" }
try { & $cli gateway select $GatewayName 2>&1 | ForEach-Object { Info $_ } }
catch { Info "gateway select: $($_.Exception.Message) (continuing)" }
& $cli gateway add "http://127.0.0.1:$Port" --local --name $GatewayName 2>&1 | ForEach-Object { Info $_ }
if ($LASTEXITCODE -ne 0) { throw "gateway add failed (exit $LASTEXITCODE)" }
& $cli gateway select $GatewayName 2>&1 | ForEach-Object { Info $_ }
if ($LASTEXITCODE -ne 0) { throw "gateway select failed (exit $LASTEXITCODE)" }
Ok "selected gateway '$GatewayName'"
# 10. Create N sandboxes. Each drives the Sandboxing provider -> a full OCSF
@@ -284,6 +387,7 @@ finally {
}
# ---- summarise the OCSF audit trail --------------------------------------
Exit-IsolatedCliEnvironment
$logText = @()
if (Test-Path (Join-Path $resultDir "gateway.log")) { $logText += Get-Content (Join-Path $resultDir "gateway.log") }
if (Test-Path (Join-Path $resultDir "gateway.err.log")) { $logText += Get-Content (Join-Path $resultDir "gateway.err.log") }
@@ -0,0 +1,167 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
//! Selectively ported Windows policy and harness regressions. These are not
//! substitutes for real MXC WebSocket, `OpenClaw`, or inference runtime coverage.
#![cfg(target_os = "windows")]
use std::path::{Path, PathBuf};
use std::process::Command;
use openshell_core::proto::{NetworkAccessPreset, NetworkEnforcementMode, SandboxPolicy};
use openshell_driver_mxc::{EmbeddedPolicyMapper, MapCtx, MappedConfig, PolicyMapper};
use openshell_policy::{parse_sandbox_policy, validate_sandbox_policy};
fn examples() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("examples")
}
fn map_fixture(yaml: &str) -> (SandboxPolicy, MappedConfig) {
let policy = parse_sandbox_policy(yaml).expect("valid authored policy");
validate_sandbox_policy(&policy).expect("valid policy semantics");
let mapped = EmbeddedPolicyMapper
.map(
Some(&policy),
&MapCtx {
sandbox_id: "windows-policy-fixture".into(),
egress: Some("127.0.0.1:18080".parse().unwrap()),
containment: "processcontainer".into(),
},
)
.expect("policy maps with supervisor-owned egress");
(policy, mapped)
}
#[test]
fn websocket_fixture_grants_read_only_workload_without_egress() {
let yaml = std::fs::read_to_string(examples().join("e2e-policies/ws-agent.yaml")).unwrap();
let (policy, mapped) = map_fixture(&yaml);
assert!(policy.network_policies.is_empty());
assert_eq!(mapped.readonly_paths, [r"C:\work\openshell-mxc-ws"]);
assert!(mapped.readwrite_paths.is_empty());
assert!(mapped.ui.unwrap().disable);
}
#[test]
fn openclaw_fixture_preserves_explicit_ui_and_node_network_grants() {
let yaml =
std::fs::read_to_string(examples().join("e2e-policies/openclaw-gateway.yaml")).unwrap();
let (policy, mapped) = map_fixture(&yaml);
assert_eq!(mapped.readwrite_paths, [r"C:\openshell-openclaw"]);
assert!(mapped.readonly_paths.is_empty());
let ui = mapped.ui.unwrap();
assert!(!ui.disable);
assert!(!ui.injection);
let config = openshell_driver_mxc::map_to_mxc(
&policy,
&openshell_driver_mxc::MxcMappingOptions::default(),
);
assert_eq!(config.config["ui"]["clipboard"], "none");
let rule = &policy.network_policies["qualification_allowed"];
assert_eq!(rule.binaries[0].path, "C:/openshell-openclaw/node.exe");
assert_eq!(rule.endpoints[0].host, "example.com");
assert_eq!(rule.endpoints[0].ports, [443]);
assert_eq!(
mapped.trimmed_policy.unwrap().network_policies,
policy.network_policies
);
}
#[test]
fn inference_template_authorizes_socket_owning_curl_not_cmd() {
let yaml = std::fs::read_to_string(examples().join("inference.yaml"))
.unwrap()
.replace("__OPENSHELL_DEMO_SHARE__", "C:/work/inference")
.replace("__CURL_EXE__", r"C:\Windows\System32\curl.exe")
.replace("__INFERENCE_HOST__", "integrate.api.nvidia.com")
.replace("__INFERENCE_PORT__", "443");
assert!(!yaml.contains("__"));
let (policy, mapped) = map_fixture(&yaml);
assert_eq!(mapped.readwrite_paths, [r"C:\work\inference"]);
let rule = &policy.network_policies["nvidia_inference"];
assert_eq!(rule.binaries.len(), 1);
assert_eq!(rule.binaries[0].path, r"C:\Windows\System32\curl.exe");
let endpoint = &rule.endpoints[0];
assert_eq!(endpoint.access, NetworkAccessPreset::ReadWrite as i32);
assert_eq!(endpoint.enforcement, NetworkEnforcementMode::Enforce as i32);
assert_eq!(endpoint.protocol, "rest");
assert_eq!(
mapped.trimmed_policy.unwrap().network_policies,
policy.network_policies
);
}
#[test]
fn runners_isolate_and_restore_exact_process_environment() {
// Parse the actual runners, but execute only their environment helpers:
// no gateway, executor, firewall, ETW session, or caller CLI state is touched.
let script = r#"
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
$tokens = $null
$errors = $null
$ast = [System.Management.Automation.Language.Parser]::ParseFile(
$env:OPENSHELL_MXC_RUNNER, [ref]$tokens, [ref]$errors)
if ($errors.Count -gt 0) { throw "runner parse errors: $($errors.Message -join '; ')" }
foreach ($functionName in @("Set-ProcessEnvironmentVariableExact", "Enter-IsolatedCliEnvironment", "Exit-IsolatedCliEnvironment")) {
$function = $ast.Find({
param($node)
$node -is [System.Management.Automation.Language.FunctionDefinitionAst] -and $node.Name -eq $functionName
}, $true)
if ($null -eq $function) { throw "missing $functionName" }
Invoke-Expression $function.Extent.Text
}
$cliStateRoot = $null
$cliEnvironmentSnapshot = @{}
$cliEnvironmentNames = @(
"APPDATA", "LOCALAPPDATA", "XDG_CONFIG_HOME", "XDG_STATE_HOME", "XDG_DATA_HOME",
"OPENSHELL_GATEWAY", "OPENSHELL_GATEWAY_ENDPOINT", "OPENSHELL_GATEWAY_INSECURE",
"OPENSHELL_GATEWAY_CONFIG", "OPENSHELL_GATEWAY_NAME")
$KeepRunning = $false
$gw = $null
$before = [Environment]::GetEnvironmentVariables("Process")
if (-not $before.Contains("OPENSHELL_GATEWAY") -or ([string]$before["OPENSHELL_GATEWAY"]).Length -ne 0) {
throw "empty entry was not inherited"
}
Enter-IsolatedCliEnvironment
try {
$isolated = [Environment]::GetEnvironmentVariables("Process")
foreach ($name in $cliEnvironmentNames) {
if ($name.StartsWith("OPENSHELL_")) {
if ($isolated.Contains($name)) { throw "$name not removed" }
} elseif (-not ([string]$isolated[$name]).StartsWith($cliStateRoot)) {
throw "$name not isolated"
}
}
} finally { Exit-IsolatedCliEnvironment }
$after = [Environment]::GetEnvironmentVariables("Process")
foreach ($name in $cliEnvironmentNames) {
if ($before.Contains($name) -ne $after.Contains($name) -or $before[$name] -cne $after[$name]) {
throw "environment entry $name not restored exactly"
}
}
if (Test-Path -LiteralPath $cliStateRoot) { throw "temporary state not removed" }
"#;
for runner in ["run-mxc-e2e.ps1", "run-ocsf-audit.ps1"] {
let output = Command::new("powershell.exe")
.args([
"-NoProfile",
"-ExecutionPolicy",
"Bypass",
"-Command",
script,
])
.env("OPENSHELL_MXC_RUNNER", examples().join(runner))
.env("OPENSHELL_GATEWAY", "")
.env("OPENSHELL_GATEWAY_ENDPOINT", "http://127.0.0.1:1")
.env_remove("OPENSHELL_GATEWAY_NAME")
.output()
.expect("launch Windows PowerShell");
assert!(
output.status.success(),
"{runner} environment regression failed:\nstdout:\n{}\nstderr:\n{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
}
}
@@ -854,6 +854,7 @@ async fn pc_https_egress_reads_injected_ca_bundle() {
let output_path_string = output_path.to_string_lossy().into_owned();
let certificate_path_string = certificate_path.to_string_lossy().into_owned();
let cmd_string = cmd.to_string_lossy().into_owned();
let curl_string = curl.to_string_lossy().into_owned();
let script = format!(
"type \"%CURL_CA_BUNDLE%\" 1>NUL && \
\"{}\" --fail --silent --show-error --cacert \"%CURL_CA_BUNDLE%\" \
@@ -861,12 +862,7 @@ async fn pc_https_egress_reads_injected_ca_bundle() {
--write-out \"%{{certs}}\" 1>\"{certificate_path_string}\"",
curl.display()
);
let command = vec![
cmd_string.clone(),
"/d".to_string(),
"/c".to_string(),
script,
];
let command = vec![cmd_string, "/d".to_string(), "/c".to_string(), script];
let serde_json::Value::Object(driver_config) = serde_json::json!({
"command": command,
"cwd": output_dir_string,
@@ -894,7 +890,8 @@ async fn pc_https_egress_reads_injected_ca_bundle() {
access: NetworkAccessPreset::ReadOnly as i32,
..Default::default()
}],
binaries: vec![NetworkBinary { path: cmd_string }],
// curl owns the socket; cmd only launches it (#4199).
binaries: vec![NetworkBinary { path: curl_string }],
},
)]),
..Default::default()