Run Python smoke code in AppContainer
This commit is contained in:
@@ -154,9 +154,126 @@ static bool FilesMatch(const std::wstring& leftPath, const std::wstring& rightPa
|
||||
return matches;
|
||||
}
|
||||
|
||||
static DWORD RunContained(
|
||||
static constexpr ULONGLONG kMaxPythonRuntimeBytes = 100ull * 1024 * 1024;
|
||||
static constexpr DWORD kMaxPythonRuntimeFiles = 5000;
|
||||
|
||||
static bool CopyRuntimeFile(
|
||||
const std::wstring& source, const std::wstring& destination,
|
||||
ULONGLONG& copiedBytes, DWORD& copiedFiles
|
||||
) {
|
||||
WIN32_FILE_ATTRIBUTE_DATA attributes{};
|
||||
if (!GetFileAttributesExW(source.c_str(), GetFileExInfoStandard, &attributes) ||
|
||||
(attributes.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))) {
|
||||
return false;
|
||||
}
|
||||
const ULONGLONG size = (static_cast<ULONGLONG>(attributes.nFileSizeHigh) << 32) |
|
||||
attributes.nFileSizeLow;
|
||||
if (copiedFiles >= kMaxPythonRuntimeFiles || size > kMaxPythonRuntimeBytes - copiedBytes) {
|
||||
return false;
|
||||
}
|
||||
if (!CopyFileW(source.c_str(), destination.c_str(), FALSE)) return false;
|
||||
copiedBytes += size;
|
||||
++copiedFiles;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CopyRuntimeTree(
|
||||
const std::wstring& source, const std::wstring& destination,
|
||||
ULONGLONG& copiedBytes, DWORD& copiedFiles, unsigned depth = 0
|
||||
) {
|
||||
if (depth > 24 || copiedFiles > kMaxPythonRuntimeFiles) return false;
|
||||
if (!CreateDirectoryW(destination.c_str(), nullptr) && GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||
return false;
|
||||
}
|
||||
WIN32_FIND_DATAW entry{};
|
||||
HANDLE search = FindFirstFileW((source + L"\\*").c_str(), &entry);
|
||||
if (search == INVALID_HANDLE_VALUE) return false;
|
||||
bool success = true;
|
||||
do {
|
||||
if (wcscmp(entry.cFileName, L".") == 0 || wcscmp(entry.cFileName, L"..") == 0) continue;
|
||||
if (entry.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) continue;
|
||||
const std::wstring sourcePath = source + L"\\" + entry.cFileName;
|
||||
const std::wstring destinationPath = destination + L"\\" + entry.cFileName;
|
||||
if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
if (wcscmp(entry.cFileName, L"site-packages") == 0 ||
|
||||
wcscmp(entry.cFileName, L"__pycache__") == 0 ||
|
||||
wcscmp(entry.cFileName, L"test") == 0 ||
|
||||
wcscmp(entry.cFileName, L"tests") == 0 ||
|
||||
wcscmp(entry.cFileName, L"idlelib") == 0 ||
|
||||
wcscmp(entry.cFileName, L"tkinter") == 0 ||
|
||||
wcscmp(entry.cFileName, L"ensurepip") == 0) continue;
|
||||
success = CopyRuntimeTree(sourcePath, destinationPath, copiedBytes,
|
||||
copiedFiles, depth + 1);
|
||||
} else {
|
||||
success = CopyRuntimeFile(sourcePath, destinationPath, copiedBytes, copiedFiles);
|
||||
}
|
||||
if (!success) break;
|
||||
} while (FindNextFileW(search, &entry));
|
||||
const DWORD finalError = GetLastError();
|
||||
FindClose(search);
|
||||
return success && finalError == ERROR_NO_MORE_FILES;
|
||||
}
|
||||
|
||||
static bool CopyPythonRuntime(
|
||||
const std::wstring& sourceRoot, const std::wstring& destinationRoot,
|
||||
ULONGLONG& copiedBytes, DWORD& copiedFiles
|
||||
) {
|
||||
copiedBytes = 0;
|
||||
copiedFiles = 0;
|
||||
if (!CreateDirectoryW(destinationRoot.c_str(), nullptr) && GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||
return false;
|
||||
}
|
||||
static const wchar_t* const runtimeFiles[] = {
|
||||
L"python.exe", L"python3.dll", L"python314.dll",
|
||||
L"vcruntime140.dll", L"vcruntime140_1.dll"
|
||||
};
|
||||
for (const wchar_t* name : runtimeFiles) {
|
||||
const std::wstring source = sourceRoot + L"\\" + name;
|
||||
const std::wstring destination = destinationRoot + L"\\" + name;
|
||||
WIN32_FILE_ATTRIBUTE_DATA attributes{};
|
||||
if (!GetFileAttributesExW(source.c_str(), GetFileExInfoStandard, &attributes)) {
|
||||
if (wcscmp(name, L"vcruntime140_1.dll") == 0) continue;
|
||||
return false;
|
||||
}
|
||||
if (!CopyRuntimeFile(source, destination, copiedBytes, copiedFiles)) return false;
|
||||
}
|
||||
return CopyRuntimeTree(sourceRoot + L"\\Lib", destinationRoot + L"\\Lib",
|
||||
copiedBytes, copiedFiles) &&
|
||||
CopyRuntimeTree(sourceRoot + L"\\DLLs", destinationRoot + L"\\DLLs",
|
||||
copiedBytes, copiedFiles);
|
||||
}
|
||||
|
||||
static bool WriteFileBytes(const std::wstring& path, const char* bytes, DWORD length) {
|
||||
HANDLE file = CreateFileW(path.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (file == INVALID_HANDLE_VALUE) return false;
|
||||
DWORD written = 0;
|
||||
bool success = WriteFile(file, bytes, length, &written, nullptr) && written == length;
|
||||
CloseHandle(file);
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool IsVersionText(const std::wstring& path) {
|
||||
HANDLE file = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (file == INVALID_HANDLE_VALUE) return false;
|
||||
char contents[64]{};
|
||||
DWORD count = 0;
|
||||
bool valid = ReadFile(file, contents, sizeof(contents) - 1, &count, nullptr) &&
|
||||
count >= 5 && count < sizeof(contents);
|
||||
CloseHandle(file);
|
||||
bool sawDot = false;
|
||||
for (DWORD index = 0; valid && index < count; ++index) {
|
||||
if (contents[index] == '.') sawDot = true;
|
||||
else if (contents[index] < '0' || contents[index] > '9') valid = false;
|
||||
}
|
||||
return valid && sawDot;
|
||||
}
|
||||
|
||||
static DWORD RunContainedExe(
|
||||
PSID appContainerSid,
|
||||
const std::wstring& command,
|
||||
const std::wstring& application,
|
||||
const std::wstring& arguments,
|
||||
const wchar_t* environment,
|
||||
const std::wstring& cwd,
|
||||
HANDLE job
|
||||
@@ -200,14 +317,14 @@ static DWORD RunContained(
|
||||
startup.StartupInfo.wShowWindow = SW_HIDE;
|
||||
startup.lpAttributeList = attributes;
|
||||
PROCESS_INFORMATION process{};
|
||||
std::wstring commandLine = QuoteArg(L"C:\\Windows\\System32\\cmd.exe") +
|
||||
L" /d /s /c \"" + command + L"\"";
|
||||
std::wstring commandLine = QuoteArg(application);
|
||||
if (!arguments.empty()) commandLine += L" " + arguments;
|
||||
std::wstring mutableLine = commandLine;
|
||||
constexpr DWORD flags = EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT |
|
||||
CREATE_NO_WINDOW | CREATE_SUSPENDED;
|
||||
|
||||
BOOL created = CreateProcessW(
|
||||
L"C:\\Windows\\System32\\cmd.exe",
|
||||
application.c_str(),
|
||||
mutableLine.empty() ? nullptr : &mutableLine[0],
|
||||
nullptr,
|
||||
nullptr,
|
||||
@@ -243,6 +360,18 @@ static DWORD RunContained(
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
static DWORD RunContained(
|
||||
PSID appContainerSid,
|
||||
const std::wstring& command,
|
||||
const wchar_t* environment,
|
||||
const std::wstring& cwd,
|
||||
HANDLE job
|
||||
) {
|
||||
const std::wstring shell = L"C:\\Windows\\System32\\cmd.exe";
|
||||
return RunContainedExe(appContainerSid, shell, L"/d /s /c \"" + command + L"\"",
|
||||
environment, cwd, job);
|
||||
}
|
||||
|
||||
int wmain() {
|
||||
wchar_t tempPath[MAX_PATH]{};
|
||||
if (!GetTempPathW(MAX_PATH, tempPath)) return 10;
|
||||
@@ -307,6 +436,9 @@ int wmain() {
|
||||
std::wstring sandboxWorkspacePath = appDataPath + L"\\agent-workspace";
|
||||
std::wstring stagedInputPath = sandboxWorkspacePath + L"\\README.md";
|
||||
std::wstring stagedCopyPath = sandboxWorkspacePath + L"\\staged-copy.md";
|
||||
std::wstring pythonSandboxPath = sandboxWorkspacePath + L"\\python";
|
||||
std::wstring pythonProbePath = sandboxWorkspacePath + L"\\python_probe.py";
|
||||
std::wstring pythonVersionPath = sandboxWorkspacePath + L"\\python-version.txt";
|
||||
CreateDirectoryW(appTempPath.c_str(), nullptr);
|
||||
bool workspaceFolderReady = CreateDirectoryW(sandboxWorkspacePath.c_str(), nullptr) != FALSE ||
|
||||
GetLastError() == ERROR_ALREADY_EXISTS;
|
||||
@@ -317,11 +449,36 @@ int wmain() {
|
||||
std::wstring curlPath = sandboxWorkspacePath + L"\\curl.exe";
|
||||
bool curlCopied = workspaceFolderReady &&
|
||||
CopyFileW(L"C:\\Windows\\System32\\curl.exe", curlPath.c_str(), FALSE);
|
||||
wchar_t pythonRootBuffer[32768]{};
|
||||
DWORD pythonRootLength = GetEnvironmentVariableW(
|
||||
L"SOVEREIGNAI_PYTHON_HOME", pythonRootBuffer, 32768);
|
||||
std::wstring pythonSourcePath = pythonRootLength > 0 && pythonRootLength < 32768
|
||||
? std::wstring(pythonRootBuffer, pythonRootLength) : std::wstring();
|
||||
ULONGLONG pythonRuntimeBytes = 0;
|
||||
DWORD pythonRuntimeFiles = 0;
|
||||
bool pythonRuntimeCopied = workspaceFolderReady && !pythonSourcePath.empty() &&
|
||||
CopyPythonRuntime(pythonSourcePath, pythonSandboxPath,
|
||||
pythonRuntimeBytes, pythonRuntimeFiles);
|
||||
const char pythonSource[] =
|
||||
"import pathlib, sys\n"
|
||||
"pathlib.Path(__file__).with_name('python-version.txt').write_text(\n"
|
||||
" '.'.join(map(str, sys.version_info[:3])), encoding='ascii')\n";
|
||||
bool pythonProbeWritten = pythonRuntimeCopied &&
|
||||
WriteFileBytes(pythonProbePath, pythonSource, sizeof(pythonSource) - 1);
|
||||
SetEnvironmentValue(environment, L"PATH", L"C:\\Windows\\System32");
|
||||
SetEnvironmentValue(environment, L"APPDATA", appDataPath);
|
||||
SetEnvironmentValue(environment, L"LOCALAPPDATA", appDataPath);
|
||||
SetEnvironmentValue(environment, L"TEMP", appTempPath);
|
||||
SetEnvironmentValue(environment, L"TMP", appTempPath);
|
||||
SetEnvironmentValue(environment, L"USERPROFILE", appDataPath);
|
||||
SetEnvironmentValue(environment, L"HOMEDRIVE", appDataPath.substr(0, 2));
|
||||
SetEnvironmentValue(environment, L"HOMEPATH", appDataPath.size() > 2
|
||||
? appDataPath.substr(2) : L"\\");
|
||||
SetEnvironmentValue(environment, L"PATH", pythonSandboxPath + L"\\DLLs;" +
|
||||
pythonSandboxPath + L";C:\\Windows\\System32");
|
||||
SetEnvironmentValue(environment, L"PYTHONHOME", pythonSandboxPath);
|
||||
SetEnvironmentValue(environment, L"PYTHONNOUSERSITE", L"1");
|
||||
SetEnvironmentValue(environment, L"PYTHONDONTWRITEBYTECODE", L"1");
|
||||
std::wstring cwd = L"C:\\Windows\\System32";
|
||||
DWORD shellResult = jobReady
|
||||
? RunContained(appContainerSid, L"exit 0", environment.data(), cwd, job)
|
||||
@@ -347,6 +504,11 @@ int wmain() {
|
||||
? RunContained(appContainerSid, QuoteArg(curlPath) + L" --fail --silent " +
|
||||
stagedFileUrl + L" -o " + stagedCopyPath, environment.data(), cwd, job)
|
||||
: ERROR_INVALID_HANDLE;
|
||||
std::wstring pythonExecutable = pythonSandboxPath + L"\\python.exe";
|
||||
DWORD pythonRunResult = jobReady && pythonProbeWritten
|
||||
? RunContainedExe(appContainerSid, pythonExecutable,
|
||||
L"-s " + QuoteArg(pythonProbePath), environment.data(), cwd, job)
|
||||
: ERROR_INVALID_HANDLE;
|
||||
DWORD curlVersionResult = jobReady && curlCopied
|
||||
? RunContained(appContainerSid, QuoteArg(curlPath) + L" --version",
|
||||
environment.data(), cwd, job)
|
||||
@@ -381,6 +543,7 @@ int wmain() {
|
||||
}
|
||||
bool stagedInputRoundTripMatches = stagedInputCopied && FilesMatch(
|
||||
L"README.md", stagedCopyPath);
|
||||
bool pythonVersionVisible = IsVersionText(pythonVersionPath);
|
||||
|
||||
if (job) CloseHandle(job);
|
||||
if (appContainerFolder) CoTaskMemFree(appContainerFolder);
|
||||
@@ -388,6 +551,8 @@ int wmain() {
|
||||
DeleteFileW(curlPath.c_str());
|
||||
DeleteFileW(stagedInputPath.c_str());
|
||||
DeleteFileW(stagedCopyPath.c_str());
|
||||
DeleteFileW(pythonProbePath.c_str());
|
||||
DeleteFileW(pythonVersionPath.c_str());
|
||||
DeleteAppContainerProfile(profileName.c_str());
|
||||
DeleteFileW(secretPath.c_str());
|
||||
DeleteFileW(writePath.c_str());
|
||||
@@ -404,12 +569,17 @@ int wmain() {
|
||||
wprintf(L"staged_copy_exit=%lu\nstaged_input_copied=%s\nstaged_roundtrip_matches=%s\n",
|
||||
stagedReadResult, stagedInputCopied ? L"true" : L"false",
|
||||
stagedInputRoundTripMatches ? L"true" : L"false");
|
||||
wprintf(L"python_runtime_copied=%s\npython_runtime_bytes=%llu\npython_runtime_files=%lu\npython_run_exit=%lu\npython_version_written=%s\n",
|
||||
pythonRuntimeCopied ? L"true" : L"false", pythonRuntimeBytes,
|
||||
pythonRuntimeFiles, pythonRunResult, pythonVersionVisible ? L"true" : L"false");
|
||||
wprintf(L"curl_version_exit=%lu\ncurl_local_health_exit=%lu\n",
|
||||
curlVersionResult, curlNetworkResult);
|
||||
if (!jobReady || shellResult != 0 || readResult == 0 || writeResult == 0 ||
|
||||
!hostSecretPreserved || !writeWasBlocked || !workspaceFolderReady ||
|
||||
allowedWriteResult != 0 || !allowedWorkspaceWriteVisible ||
|
||||
!stagedInputCopied || stagedReadResult != 0 || !stagedInputRoundTripMatches ||
|
||||
!pythonRuntimeCopied || !pythonProbeWritten || pythonRunResult != 0 ||
|
||||
!pythonVersionVisible ||
|
||||
!curlCopied || curlVersionResult != 0 || curlNetworkResult == 0) return 20;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ $outputDir = Join-Path $env:TEMP ("SovereignAI-AppContainer-" + [guid]::NewGuid(
|
||||
New-Item -ItemType Directory -Path $outputDir | Out-Null
|
||||
$exe = Join-Path $outputDir 'appcontainer_probe.exe'
|
||||
$obj = Join-Path $outputDir 'appcontainer_probe.obj'
|
||||
$previousPythonHome = $env:SOVEREIGNAI_PYTHON_HOME
|
||||
|
||||
try {
|
||||
Push-Location $repoRoot
|
||||
@@ -21,6 +22,11 @@ try {
|
||||
throw "C++ probe compilation failed with exit code $LASTEXITCODE"
|
||||
}
|
||||
|
||||
$pythonHome = (& python -c 'import sys; print(sys.base_prefix)').Trim()
|
||||
if ($LASTEXITCODE -ne 0 -or -not (Test-Path -LiteralPath $pythonHome -PathType Container)) {
|
||||
throw 'Could not locate the active Python runtime to test isolated Python execution.'
|
||||
}
|
||||
$env:SOVEREIGNAI_PYTHON_HOME = $pythonHome
|
||||
& $exe
|
||||
$probeExit = $LASTEXITCODE
|
||||
if ($probeExit -ne 0) {
|
||||
@@ -30,6 +36,11 @@ try {
|
||||
Pop-Location
|
||||
}
|
||||
} finally {
|
||||
if ($null -eq $previousPythonHome) {
|
||||
Remove-Item Env:SOVEREIGNAI_PYTHON_HOME -ErrorAction SilentlyContinue
|
||||
} else {
|
||||
$env:SOVEREIGNAI_PYTHON_HOME = $previousPythonHome
|
||||
}
|
||||
$resolvedOutput = (Resolve-Path -LiteralPath $outputDir -ErrorAction SilentlyContinue).Path
|
||||
$tempRoot = [IO.Path]::GetFullPath($env:TEMP).TrimEnd('\') + '\'
|
||||
if ($resolvedOutput -and $resolvedOutput.StartsWith($tempRoot, [StringComparison]::OrdinalIgnoreCase) -and
|
||||
|
||||
Reference in New Issue
Block a user