first commit
This commit is contained in:
commit
ff482ebe23
251 files changed
+19126
No files matched your search
@@ -0,0 +1,83 @@
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@unittest.skipUnless(sys.platform == "win32", "Windows Job Object integration")
|
||||
class WindowsContainmentTests(unittest.TestCase):
|
||||
def run_child(self, source):
|
||||
source = f"import sys; sys.path.insert(0, {str(Path(__file__).parent)!r});\n" + source
|
||||
return subprocess.run([sys.executable, "-c", source], cwd=Path(__file__).parent,
|
||||
capture_output=True, text=True, timeout=20,
|
||||
creationflags=subprocess.CREATE_NO_WINDOW)
|
||||
|
||||
def test_process_limit_and_no_breakaway(self):
|
||||
result = self.run_child("""
|
||||
import json, subprocess, sys
|
||||
from windows_containment import contain_inspector
|
||||
status=contain_inspector()
|
||||
children=[]
|
||||
try:
|
||||
for i in range(3):
|
||||
children.append(subprocess.Popen([sys.executable,'-c','import time; time.sleep(15)'],creationflags=subprocess.CREATE_NO_WINDOW))
|
||||
denied=False
|
||||
try:
|
||||
extra=subprocess.Popen([sys.executable,'-c','pass'],creationflags=subprocess.CREATE_NO_WINDOW)
|
||||
extra.wait()
|
||||
except OSError:
|
||||
denied=True
|
||||
breakaway=False
|
||||
try:
|
||||
escaped=subprocess.Popen([sys.executable,'-c','pass'],creationflags=subprocess.CREATE_NO_WINDOW | subprocess.CREATE_BREAKAWAY_FROM_JOB)
|
||||
escaped.wait()
|
||||
except OSError:
|
||||
breakaway=True
|
||||
print(json.dumps({'status':status,'denied':denied,'breakawayDenied':breakaway}),flush=True)
|
||||
finally:
|
||||
for child in children:
|
||||
child.terminate();child.wait()
|
||||
""")
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
proof = json.loads(result.stdout)
|
||||
self.assertTrue(proof["status"]["active"])
|
||||
self.assertEqual(proof["status"]["memoryBytes"], 4 * 1024 ** 3)
|
||||
self.assertTrue(proof["denied"])
|
||||
self.assertTrue(proof["breakawayDenied"])
|
||||
|
||||
def test_descendant_is_killed_when_owner_is_terminated(self):
|
||||
source = """
|
||||
import subprocess,sys,time
|
||||
from windows_containment import contain_inspector
|
||||
contain_inspector()
|
||||
child=subprocess.Popen([sys.executable,'-c','import time; time.sleep(60)'],creationflags=subprocess.CREATE_NO_WINDOW)
|
||||
print(child.pid,flush=True)
|
||||
time.sleep(60)
|
||||
"""
|
||||
source = f"import sys; sys.path.insert(0, {str(Path(__file__).parent)!r});\n" + source
|
||||
owner = subprocess.Popen([sys.executable, "-c", source], cwd=Path(__file__).parent,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
|
||||
creationflags=subprocess.CREATE_NO_WINDOW)
|
||||
handle = None
|
||||
try:
|
||||
# Opening the descendant before termination permits a reliable wait,
|
||||
# avoiding PID reuse or process enumeration heuristics.
|
||||
child_pid = int(owner.stdout.readline())
|
||||
import ctypes
|
||||
kernel = ctypes.WinDLL("kernel32", use_last_error=True)
|
||||
kernel.OpenProcess.argtypes = [ctypes.c_uint32, ctypes.c_int, ctypes.c_uint32]
|
||||
kernel.OpenProcess.restype = ctypes.c_void_p
|
||||
kernel.WaitForSingleObject.argtypes = [ctypes.c_void_p, ctypes.c_uint32]
|
||||
kernel.CloseHandle.argtypes = [ctypes.c_void_p]
|
||||
handle = kernel.OpenProcess(0x00100000, False, child_pid)
|
||||
self.assertTrue(handle)
|
||||
owner.terminate();owner.wait(timeout=10)
|
||||
self.assertEqual(kernel.WaitForSingleObject(handle, 10000), 0)
|
||||
finally:
|
||||
if owner.poll() is None:
|
||||
owner.terminate();owner.wait(timeout=10)
|
||||
if handle:
|
||||
kernel.CloseHandle(handle)
|
||||
owner.stdout.close();owner.stderr.close()
|
||||
Reference in new issue
Block a user