Verify staged files in AppContainer

This commit is contained in:
Hamza Ayed
2026-10-03 12:51:26 +03:00
parent ce70fda360
commit 82bb5189a1
3 changed files with 53 additions and 2 deletions
@@ -123,6 +123,37 @@ static void SetEnvironmentValue(
block.push_back(L'\0');
}
static bool FilesMatch(const std::wstring& leftPath, const std::wstring& rightPath) {
HANDLE left = CreateFileW(leftPath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
HANDLE right = CreateFileW(rightPath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (left == INVALID_HANDLE_VALUE || right == INVALID_HANDLE_VALUE) {
if (left != INVALID_HANDLE_VALUE) CloseHandle(left);
if (right != INVALID_HANDLE_VALUE) CloseHandle(right);
return false;
}
LARGE_INTEGER leftSize{}, rightSize{};
bool matches = GetFileSizeEx(left, &leftSize) && GetFileSizeEx(right, &rightSize) &&
leftSize.QuadPart == rightSize.QuadPart && leftSize.QuadPart >= 0 &&
leftSize.QuadPart <= 1024 * 1024;
std::vector<char> leftBytes(matches ? static_cast<size_t>(leftSize.QuadPart) : 0);
std::vector<char> rightBytes(matches ? static_cast<size_t>(rightSize.QuadPart) : 0);
DWORD leftRead = 0, rightRead = 0;
if (matches && !leftBytes.empty()) {
matches = ReadFile(left, leftBytes.data(), static_cast<DWORD>(leftBytes.size()),
&leftRead, nullptr) &&
ReadFile(right, rightBytes.data(), static_cast<DWORD>(rightBytes.size()),
&rightRead, nullptr) &&
leftRead == static_cast<DWORD>(leftBytes.size()) &&
rightRead == static_cast<DWORD>(rightBytes.size()) &&
leftBytes == rightBytes;
}
CloseHandle(left);
CloseHandle(right);
return matches;
}
static DWORD RunContained(
PSID appContainerSid,
const std::wstring& command,
@@ -274,11 +305,15 @@ int wmain() {
std::wstring appDataPath(appContainerFolder);
std::wstring appTempPath = appDataPath + L"\\Temp";
std::wstring sandboxWorkspacePath = appDataPath + L"\\agent-workspace";
std::wstring stagedInputPath = sandboxWorkspacePath + L"\\README.md";
std::wstring stagedCopyPath = sandboxWorkspacePath + L"\\staged-copy.md";
CreateDirectoryW(appTempPath.c_str(), nullptr);
bool workspaceFolderReady = CreateDirectoryW(sandboxWorkspacePath.c_str(), nullptr) != FALSE ||
GetLastError() == ERROR_ALREADY_EXISTS;
workspaceFolderReady = workspaceFolderReady &&
GrantContainerFolderAccess(sandboxWorkspacePath, appContainerSid);
bool stagedInputCopied = workspaceFolderReady &&
CopyFileW(L"README.md", stagedInputPath.c_str(), FALSE);
std::wstring curlPath = sandboxWorkspacePath + L"\\curl.exe";
bool curlCopied = workspaceFolderReady &&
CopyFileW(L"C:\\Windows\\System32\\curl.exe", curlPath.c_str(), FALSE);
@@ -304,6 +339,14 @@ int wmain() {
? RunContained(appContainerSid, L"echo contained>" + allowedWritePath,
environment.data(), cwd, job)
: ERROR_INVALID_HANDLE;
std::wstring stagedFileUrl = L"file:///";
for (wchar_t ch : stagedInputPath) {
stagedFileUrl += ch == L'\\' ? L'/' : ch;
}
DWORD stagedReadResult = jobReady && stagedInputCopied && curlCopied
? RunContained(appContainerSid, QuoteArg(curlPath) + L" --fail --silent " +
stagedFileUrl + L" -o " + stagedCopyPath, environment.data(), cwd, job)
: ERROR_INVALID_HANDLE;
DWORD curlVersionResult = jobReady && curlCopied
? RunContained(appContainerSid, QuoteArg(curlPath) + L" --version",
environment.data(), cwd, job)
@@ -336,11 +379,15 @@ int wmain() {
&count, nullptr) && count >= 9 && memcmp(contents, "contained", 9) == 0;
CloseHandle(allowedOutput);
}
bool stagedInputRoundTripMatches = stagedInputCopied && FilesMatch(
L"README.md", stagedCopyPath);
if (job) CloseHandle(job);
if (appContainerFolder) CoTaskMemFree(appContainerFolder);
FreeSid(appContainerSid);
DeleteFileW(curlPath.c_str());
DeleteFileW(stagedInputPath.c_str());
DeleteFileW(stagedCopyPath.c_str());
DeleteAppContainerProfile(profileName.c_str());
DeleteFileW(secretPath.c_str());
DeleteFileW(writePath.c_str());
@@ -354,11 +401,15 @@ int wmain() {
writeWasBlocked ? L"true" : L"false");
wprintf(L"profile_write_exit=%lu\nprofile_write_visible=%s\n",
allowedWriteResult, allowedWorkspaceWriteVisible ? L"true" : L"false");
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"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 ||
!curlCopied || curlVersionResult != 0 || curlNetworkResult == 0) return 20;
return 0;
}
@@ -15,7 +15,7 @@ $obj = Join-Path $outputDir 'appcontainer_probe.obj'
try {
Push-Location $repoRoot
try {
$compile = 'call "{0}" && cl.exe /nologo /EHsc /W4 /Fo:"{1}" "{2}" /Fe:"{3}" /link Userenv.lib Ole32.lib Advapi32.lib' -f $vcvars, $obj, $source, $exe
$compile = 'call "{0}" && cl.exe /nologo /EHsc /W4 /MT /Fo:"{1}" "{2}" /Fe:"{3}" /link Userenv.lib Ole32.lib Advapi32.lib' -f $vcvars, $obj, $source, $exe
& cmd.exe /d /s /c $compile
if ($LASTEXITCODE -ne 0) {
throw "C++ probe compilation failed with exit code $LASTEXITCODE"