64 lines
3.3 KiB
Python
64 lines
3.3 KiB
Python
"""Run the complete Chrome suite and preserve its own descendant sandbox evidence."""
|
|||
|
|
from datetime import datetime, timezone
|
||
|
|
import json
|
||
|
|
import os
|
||
|
|
from pathlib import Path
|
||
|
|
import shlex
|
||
|
|
import subprocess
|
||
|
|
import sys
|
||
|
|
import time
|
||
|
|
|
||
|
|
ROOT = Path(__file__).resolve().parents[4]
|
||
|
|
EVIDENCE = Path(__file__).resolve().parent
|
||
|
|
|
||
|
|
def now(): return datetime.now(timezone.utc).isoformat()
|
||
|
|
|
||
|
|
def descendants(runner_pid):
|
||
|
|
processes = {}
|
||
|
|
for directory in Path('/proc').iterdir():
|
||
|
|
if not directory.name.isdigit(): continue
|
||
|
|
try:
|
||
|
|
stat = (directory / 'stat').read_text().rsplit(')', 1)[1].split()
|
||
|
|
arguments = (directory / 'cmdline').read_bytes().decode(errors='replace').rstrip('\0').split('\0')
|
||
|
|
status = (directory / 'status').read_text().splitlines()
|
||
|
|
processes[int(directory.name)] = {'pid': int(directory.name), 'ppid': int(stat[1]),
|
||
|
|
'arguments': arguments, 'commandLine': shlex.join(arguments),
|
||
|
|
'sandboxStatus': [line for line in status if line.startswith(('NoNewPrivs:', 'Seccomp:', 'Seccomp_filters:', 'NSpid:'))]}
|
||
|
|
except (OSError, ValueError): continue
|
||
|
|
family = {runner_pid}
|
||
|
|
while True:
|
||
|
|
children = {pid for pid, record in processes.items() if record['ppid'] in family}
|
||
|
|
if children <= family: break
|
||
|
|
family |= children
|
||
|
|
return [record for pid, record in processes.items() if pid in family and record['arguments']
|
||
|
|
and record['arguments'][0].startswith('/opt/google/chrome/')
|
||
|
|
and 'crashpad' not in record['arguments'][0]]
|
||
|
|
|
||
|
|
env = os.environ.copy()
|
||
|
|
env.pop('PLAYWRIGHT_NO_SANDBOX', None)
|
||
|
|
env['CHROME_PATH'] = '/usr/bin/google-chrome'
|
||
|
|
env['PLAYWRIGHT_JSON_OUTPUT_FILE'] = str(EVIDENCE / 'browser-results.json')
|
||
|
|
command = ['npx', 'playwright', 'test', '--reporter=line,json', '--output', str(EVIDENCE / 'browser-final')]
|
||
|
|
started = now()
|
||
|
|
observed = {}
|
||
|
|
with (EVIDENCE / '06-browser.log').open('x') as log:
|
||
|
|
log.write('$ ' + shlex.join(command) + '\nStarted UTC: ' + started + '\nCHROME_PATH=/usr/bin/google-chrome; PLAYWRIGHT_NO_SANDBOX unset; chromiumSandbox=true\n')
|
||
|
|
log.flush()
|
||
|
|
process = subprocess.Popen(command, cwd=ROOT, env=env, stdout=log, stderr=subprocess.STDOUT)
|
||
|
|
while process.poll() is None:
|
||
|
|
for record in descendants(process.pid):
|
||
|
|
observed[record['pid']] = record
|
||
|
|
time.sleep(2)
|
||
|
|
log.write('\nExit code: ' + str(process.returncode) + '\nFinished UTC: ' + now() + '\n')
|
||
|
|
assert observed, 'No suite Chrome processes observed'
|
||
|
|
assert all('--no-sandbox' not in row['arguments'] for row in observed.values())
|
||
|
|
report = {'startedAtUtc': started, 'completedAtUtc': now(), 'runnerPid': process.pid,
|
||
|
|
'command': command, 'chrome': subprocess.check_output(['/usr/bin/google-chrome', '--version'], text=True).strip(),
|
||
|
|
'method': 'Actual Chrome descendants of this suite runner; Playwright chromiumSandbox=true and opt-out unset.',
|
||
|
|
'noSandboxFlagPresent': False, 'exitCode': process.returncode,
|
||
|
|
'processes': list(observed.values())}
|
||
|
|
with (EVIDENCE / 'chrome-sandbox.json').open('x') as out: out.write(json.dumps(report, indent=2) + '\n')
|
||
|
|
print(json.dumps({'exitCode': process.returncode, 'observedChromeProcesses': len(observed), 'noSandboxFlagPresent': False}))
|
||
|
|
print('\n'.join((EVIDENCE / '06-browser.log').read_text().splitlines()[-16:]))
|
||
|
|
sys.exit(process.returncode)
|