Files
cowork-local/core/windows_sandbox_vm.py
f9f6bc01fd
CI / test (push) Canceled after 0s
Feature/delta team/epic r04 (#7)
## Summary

epic r04 - begin refactor

## Change Type

- [x] Cowork feature
- [ ] Bug fix
- [ ] Core AI contribution
- [ ] Test / hardening
- [ ] Performance
- [ ] Documentation

## Related Work

Cowork Task:

Core Repo: http://34.143.229.138/gitea-admin/fsg-ai-core-assets

Core AI Issue:

Core Task:

Related PR:

## Scope

What is intentionally included?

What is intentionally NOT included?

## Validation

- [ ] Unit tests
- [ ] Integration tests
- [ ] Manual verification
- [ ] Regression check

Commands / evidence:

## Security Impact

Permission / credential / network / customer data impact:

## Compatibility

- [ ] No breaking change
- [ ] Breaking change documented

## Reviewer Notes

Anything Cowork reviewers should pay attention to.

---------

Co-authored-by: Anh Tran Nguyen Minh <anhtnm1@fpt.com>
Co-authored-by: Huong Le Thi Thien <huongltt35@fpt.com>
Co-authored-by: Nam Pham Dinh Thanh <nampdt@fpt.com>
Co-authored-by: Vu Dam Tuan <vudt15@fpt.com>
Co-authored-by: Hiep Ha Van <hiephv3@fpt.com>
Co-authored-by: Lam Hoang Van <lamhv7@fpt.com>
Reviewed-on: #7
Co-authored-by: Duy Le Huu <duylh19@fpt.com>
2026-08-31 05:15:13 +00:00

168 lines
5.8 KiB
Python

"""Windows Sandbox VM — high-risk execution in ephemeral VM isolation.
Uses .wsb configuration files to launch Windows Sandbox with:
- Full filesystem isolation
- Optional full network disablement
- Disabled clipboard, printer, audio input, video input, vGPU
- Mounts only approved workspace folder
- Captures stdout, stderr, exit code back to safe output files
"""
from __future__ import annotations
import os
import platform
import subprocess
import sys
import tempfile
from pathlib import Path
from typing import Any, Dict, Optional
_IS_WINDOWS = sys.platform == "win32"
def is_windows_sandbox_available() -> bool:
"""Check if Windows Sandbox is available (Win 10/11 Pro/Enterprise with virtualization)."""
if not _IS_WINDOWS:
return False
try:
import winreg
key = winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE,
r"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Virtualization",
0,
winreg.KEY_READ,
)
val, _ = winreg.QueryValueEx(key, "VirtualizationEnabled")
winreg.CloseKey(key)
return val == 1
except Exception:
pass
return False
class WindowsSandboxVM:
"""Sandbox using Windows Sandbox VM for critical-risk execution."""
def run_command(
self,
command: str,
workdir: str = "",
block_network: bool = True,
memory_mb: int = 1024,
timeout_sec: int = 300,
) -> Dict[str, Any]:
"""Run command in Windows Sandbox VM.
Generates a temporary .wsb config, launches the sandbox, runs the
command inside, captures output, and cleans up.
"""
if not _IS_WINDOWS:
return self._error("Windows Sandbox is only available on Windows")
if not is_windows_sandbox_available():
return self._error(
"Windows Sandbox is not available or not enabled on this system"
)
# Create output capture files
stdout_file = Path(tempfile.gettempdir()) / f"wsb_stdout_{os.getpid()}.txt"
stderr_file = Path(tempfile.gettempdir()) / f"wsb_stderr_{os.getpid()}.txt"
exit_file = Path(tempfile.gettempdir()) / f"wsb_exit_{os.getpid()}.txt"
# Escape command for batch
safe_cmd = command.replace('"', '"^"')
# Build batch script to capture output
batch = (
f'cmd /c ("{safe_cmd}" > "{stdout_file}" 2> "{stderr_file}" && '
f'echo %errorlevel% > "{exit_file}" || echo %errorlevel% > "{exit_file}")'
)
# Build .wsb config
wsb_content = [
"<Configuration>",
f" <MemoryMB>{memory_mb}</MemoryMB>",
]
if block_network:
wsb_content.append(" <Networking>Disable</Networking>")
wsb_content.append(" <Clipboard>Disable</Clipboard>")
wsb_content.append(" <Printer>Disable</Printer>")
wsb_content.append(" <AudioInput>Disable</AudioInput>")
wsb_content.append(" <VideoInput>Disable</VideoInput>")
wsb_content.append(" <VGpu>Disable</VGpu>")
if workdir:
wsb_content.append(f" <Volume>{workdir}={workdir}</Volume>")
wsb_content.append(f' <LogonCommand>')
wsb_content.append(f' <Command>{batch}</Command>')
wsb_content.append(f" </LogonCommand>")
wsb_content.append("</Configuration>")
wsb_path = Path(tempfile.gettempdir()) / f"cowork_sandbox_{os.getpid()}.wsb"
try:
wsb_path.write_text("\n".join(wsb_content), encoding="utf-8")
# Launch Windows Sandbox
proc = subprocess.Popen(
[str(wsb_path)],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
# Wait for the sandbox to complete (it exits when logon command finishes)
try:
proc.wait(timeout=timeout_sec)
except subprocess.TimeoutExpired:
proc.kill()
return {
"ok": False,
"stdout": "",
"stderr": f"Timeout after {timeout_sec}s",
"returncode": -1,
"sandbox": "windows_sandbox",
}
# Read results
stdout_text = ""
stderr_text = ""
returncode = proc.returncode or 0
if stdout_file.exists():
stdout_text = stdout_file.read_text(encoding="utf-8", errors="replace")
if stderr_file.exists():
stderr_text = stderr_file.read_text(encoding="utf-8", errors="replace")
if exit_file.exists():
try:
returncode = int(exit_file.read_text().strip())
except ValueError:
pass
return {
"ok": returncode == 0,
"stdout": stdout_text,
"stderr": stderr_text,
"returncode": returncode,
"sandbox": "windows_sandbox",
}
except Exception as exc:
return self._error(str(exc))
finally:
# Cleanup temp files
for f in (wsb_path, stdout_file, stderr_file, exit_file):
try:
if f.exists():
f.unlink()
except OSError:
pass
def _error(self, message: str) -> Dict[str, Any]:
"""Kết quả lỗi theo đúng khuôn chung của bộ chạy sandbox."""
return {
"ok": False,
"stdout": "",
"stderr": message,
"returncode": -1,
"sandbox": "windows_sandbox",
}