105 lines
3.6 KiB
Python
105 lines
3.6 KiB
Python
#!/usr/bin/env python3
|
|
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
|
from urllib.parse import urlparse, parse_qs
|
|
import urllib.request
|
|
import os
|
|
import re
|
|
|
|
# Whitelist of allowed domains
|
|
ALLOWED_DOMAINS = [
|
|
'en.wikipedia.org',
|
|
'wikipedia.org',
|
|
'example.com',
|
|
'github.com',
|
|
'www.github.com',
|
|
]
|
|
|
|
class Handler(SimpleHTTPRequestHandler):
|
|
def do_GET(self):
|
|
if self.path.startswith('/proxy?'):
|
|
qs = urlparse(self.path).query
|
|
params = parse_qs(qs)
|
|
if 'url' not in params:
|
|
self.send_error(400)
|
|
return
|
|
|
|
url = params['url'][0]
|
|
|
|
# Security: Validate URL
|
|
try:
|
|
parsed = urlparse(url)
|
|
|
|
# Block dangerous schemes
|
|
if parsed.scheme not in ('http', 'https'):
|
|
self.send_error(403, 'Invalid URL scheme')
|
|
return
|
|
|
|
# Block localhost/private IPs
|
|
hostname = parsed.hostname or ''
|
|
if hostname in ('localhost', '127.0.0.1', '::1') or hostname.startswith('192.168.') or hostname.startswith('10.'):
|
|
self.send_error(403, 'Local network access blocked')
|
|
return
|
|
|
|
# Check whitelist (optional - comment out to allow all)
|
|
# if not any(hostname.endswith(domain) for domain in ALLOWED_DOMAINS):
|
|
# self.send_error(403, 'Domain not whitelisted')
|
|
# return
|
|
|
|
except Exception as e:
|
|
self.send_error(400, 'Invalid URL')
|
|
return
|
|
|
|
try:
|
|
req = urllib.request.Request(url, headers={
|
|
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X)',
|
|
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
'Accept-Language': 'en-US,en;q=0.5'
|
|
})
|
|
response = urllib.request.urlopen(req, timeout=30)
|
|
data = response.read(1000000).decode('utf-8', errors='ignore')
|
|
|
|
# Add base tag for relative URLs
|
|
base_tag = f'<base href="{url}">'
|
|
if '<head>' in data:
|
|
data = data.replace('<head>', f'<head>{base_tag}', 1)
|
|
else:
|
|
data = f'<head>{base_tag}</head>' + data
|
|
|
|
# Inject click handler
|
|
script = '''<script>
|
|
document.addEventListener('click', (e) => {
|
|
const link = e.target.closest('a[href]');
|
|
if (link) {
|
|
e.preventDefault();
|
|
window.parent.postMessage({
|
|
type: 'linkTapped',
|
|
href: link.href,
|
|
label: link.textContent.trim()
|
|
}, '*');
|
|
}
|
|
}, true);
|
|
</script>'''
|
|
|
|
if '</body>' in data:
|
|
data = data.replace('</body>', script + '</body>', 1)
|
|
else:
|
|
data += script
|
|
|
|
self.send_response(200)
|
|
self.send_header('Content-Type', 'text/html; charset=utf-8')
|
|
self.send_header('X-Frame-Options', 'ALLOWALL')
|
|
self.end_headers()
|
|
self.wfile.write(data.encode('utf-8'))
|
|
except Exception as e:
|
|
self.send_error(500)
|
|
return
|
|
|
|
super().do_GET()
|
|
|
|
def log_message(self, *args):
|
|
pass
|
|
|
|
if __name__ == '__main__':
|
|
os.chdir('/tmp/machine.unlimited.pizza')
|
|
HTTPServer(('127.0.0.1', 9001), Handler).serve_forever()
|