Some checks failed
Use-case regression gate / usecase-regressions (push) Has been cancelled
Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
119 lines
4.7 KiB
Python
119 lines
4.7 KiB
Python
"""Bounded synthetic SMTP/mailbox fixture, available only inside a network-none peer."""
|
|
|
|
import email
|
|
import email.policy
|
|
import json
|
|
import re
|
|
import socketserver
|
|
import threading
|
|
import urllib.parse
|
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
from pathlib import Path
|
|
|
|
MESSAGES = []
|
|
LOCK = threading.Lock()
|
|
|
|
|
|
class SMTP(socketserver.StreamRequestHandler):
|
|
def handle(self):
|
|
self.connection.settimeout(15)
|
|
recipient = None
|
|
sender = False
|
|
|
|
def reply(line):
|
|
self.wfile.write((line + '\r\n').encode())
|
|
self.wfile.flush()
|
|
|
|
reply('220 synthetic fixture')
|
|
while True:
|
|
raw = self.rfile.readline(8193)
|
|
if not raw or len(raw) > 8192:
|
|
return
|
|
command = raw.decode(errors='replace').strip()
|
|
upper = command.upper()
|
|
if upper.startswith(('EHLO ', 'HELO ')):
|
|
reply('250 synthetic fixture')
|
|
elif upper.startswith('MAIL FROM:'):
|
|
sender = upper == 'MAIL FROM:<NO-REPLY@EXAMPLE.TEST>'
|
|
reply('250 OK' if sender else '550 fixture sender only')
|
|
elif upper.startswith('RCPT TO:'):
|
|
recipient = ('alice@example.test'
|
|
if upper == 'RCPT TO:<ALICE@EXAMPLE.TEST>' else None)
|
|
reply('250 OK' if recipient else '550 fixture recipient only')
|
|
elif upper == 'DATA' and sender and recipient:
|
|
reply('354 End with dot')
|
|
lines, size = [], 0
|
|
while True:
|
|
line = self.rfile.readline(65537)
|
|
if not line or len(line) > 65536:
|
|
return
|
|
if line == b'.\r\n':
|
|
break
|
|
size += len(line)
|
|
if size > 65536:
|
|
reply('552 fixture message too large')
|
|
return
|
|
lines.append(line[1:] if line.startswith(b'..') else line)
|
|
message = email.message_from_bytes(b''.join(lines), policy=email.policy.default)
|
|
body = message.get_body(preferencelist=('plain', 'html'))
|
|
text = body.get_content() if body else message.get_content()
|
|
links = re.findall(r'https://auth\.recovery\.test:9091/'
|
|
r'reset-password/step2\?token=[A-Za-z0-9_.-]+', str(text))
|
|
with LOCK:
|
|
if len(MESSAGES) >= 20:
|
|
reply('452 fixture mailbox full')
|
|
return
|
|
MESSAGES.append({'number': len(MESSAGES) + 1, 'recipient': recipient,
|
|
'reset_links': list(dict.fromkeys(links))})
|
|
recipient, sender = None, False
|
|
reply('250 fixture accepted')
|
|
elif upper in {'RSET', 'NOOP'}:
|
|
recipient, sender = None, False
|
|
reply('250 OK')
|
|
elif upper == 'QUIT':
|
|
reply('221 bye')
|
|
return
|
|
else:
|
|
reply('502 fixture command refused')
|
|
|
|
|
|
class SMTPServer(socketserver.ThreadingTCPServer):
|
|
daemon_threads = True
|
|
allow_reuse_address = True
|
|
|
|
|
|
class Mailbox(BaseHTTPRequestHandler):
|
|
def log_message(self, *args):
|
|
pass
|
|
|
|
def do_GET(self): # noqa: N802
|
|
if urllib.parse.urlsplit(self.path).path == '/client-callback':
|
|
body = b'{"status":"synthetic_callback_receiver"}'
|
|
self.send_response(200)
|
|
self.send_header('Content-Type', 'application/json')
|
|
self.send_header('Content-Length', str(len(body)))
|
|
self.send_header('Cache-Control', 'no-store')
|
|
self.end_headers()
|
|
self.wfile.write(body)
|
|
return
|
|
if self.path not in {'/health', '/messages'}:
|
|
self.send_error(404)
|
|
return
|
|
with LOCK:
|
|
body = json.dumps({'messages': MESSAGES} if self.path == '/messages'
|
|
else {'status': 'fixture-ready'}).encode()
|
|
self.send_response(200)
|
|
self.send_header('Content-Type', 'application/json')
|
|
self.send_header('Content-Length', str(len(body)))
|
|
self.send_header('Cache-Control', 'no-store')
|
|
self.end_headers()
|
|
self.wfile.write(body)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
if not __debug__ or {p.name for p in Path('/sys/class/net').iterdir()} != {'lo'}:
|
|
raise SystemExit('Fixture loopback boundary required')
|
|
with SMTPServer(('127.0.0.1', 2525), SMTP) as smtp:
|
|
thread = threading.Thread(target=smtp.serve_forever, daemon=True)
|
|
thread.start()
|
|
ThreadingHTTPServer(('127.0.0.1', 8088), Mailbox).serve_forever()
|