fix(mitmproxy): Wildcard routing fallback and JSON output corruption

- Skip wildcard domains (starting with '.') in sync-routes to allow
  unknown subdomains to show "WAF SAYS NO" 404 page instead of blog
- Fix log_info() to output to stderr to prevent JSON corruption in
  sync-routes when log messages mixed with JSON fragments
- Escape CSS curly braces in NOT_FOUND_HTML for Python .format()
  compatibility (fixes KeyError: 'box-sizing')

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-02-28 08:45:44 +01:00
parent 356e2814ca
commit 0389f93667
2 changed files with 47 additions and 37 deletions

View File

@ -75,7 +75,7 @@ EOF
require_root() { [ "$(id -u)" -eq 0 ] || { echo "Root required" >&2; exit 1; }; } require_root() { [ "$(id -u)" -eq 0 ] || { echo "Root required" >&2; exit 1; }; }
log_info() { echo "[INFO] $*"; } log_info() { echo "[INFO] $*" >&2; }
log_warn() { echo "[WARN] $*" >&2; } log_warn() { echo "[WARN] $*" >&2; }
log_error() { echo "[ERROR] $*" >&2; } log_error() { echo "[ERROR] $*" >&2; }
@ -1594,6 +1594,15 @@ cmd_sync_routes() {
local domain=$(uci -q get haproxy.$vhost.domain) local domain=$(uci -q get haproxy.$vhost.domain)
local backend=$(uci -q get haproxy.$vhost.backend) local backend=$(uci -q get haproxy.$vhost.backend)
# Skip wildcard domains (starting with '.') - they should fall through
# to the "WAF SAYS NO" 404 page for unknown subdomains, not route to a fallback
case "$domain" in
.*)
log_info " Skipping wildcard domain: $domain (no route = 404 error page)"
continue
;;
esac
# If currently using mitmproxy_inspector, use the stored original backend # If currently using mitmproxy_inspector, use the stored original backend
if [ "$backend" = "mitmproxy_inspector" ]; then if [ "$backend" = "mitmproxy_inspector" ]; then
backend=$(uci -q get haproxy.$vhost.original_backend) backend=$(uci -q get haproxy.$vhost.original_backend)

View File

@ -17,6 +17,7 @@ ROUTES_FILE = "/data/haproxy-routes.json"
# 404 page HTML - shown when no route is found # 404 page HTML - shown when no route is found
# NEVER fallback to LuCI - return proper 404 instead # NEVER fallback to LuCI - return proper 404 instead
# Note: CSS curly braces must be doubled for Python .format() compatibility
NOT_FOUND_HTML = """<!DOCTYPE html> NOT_FOUND_HTML = """<!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
@ -24,8 +25,8 @@ NOT_FOUND_HTML = """<!DOCTYPE html>
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WAF Says NO - SecuBox</title> <title>WAF Says NO - SecuBox</title>
<style> <style>
* { box-sizing: border-box; } * {{ box-sizing: border-box; }}
body { body {{
font-family: 'Courier New', monospace; font-family: 'Courier New', monospace;
background: linear-gradient(135deg, #0a0a0a 0%, #1a0a1a 50%, #0a1a0a 100%); background: linear-gradient(135deg, #0a0a0a 0%, #1a0a1a 50%, #0a1a0a 100%);
color: #0f0; color: #0f0;
@ -35,8 +36,8 @@ NOT_FOUND_HTML = """<!DOCTYPE html>
min-height: 100vh; min-height: 100vh;
margin: 0; margin: 0;
overflow: hidden; overflow: hidden;
} }}
.matrix-bg { .matrix-bg {{
position: fixed; position: fixed;
top: 0; left: 0; right: 0; bottom: 0; top: 0; left: 0; right: 0; bottom: 0;
background: repeating-linear-gradient( background: repeating-linear-gradient(
@ -48,41 +49,41 @@ NOT_FOUND_HTML = """<!DOCTYPE html>
); );
pointer-events: none; pointer-events: none;
animation: scan 8s linear infinite; animation: scan 8s linear infinite;
} }}
@keyframes scan { from { background-position: 0 0; } to { background-position: 0 100vh; } } @keyframes scan {{ from {{ background-position: 0 0; }} to {{ background-position: 0 100vh; }} }}
.container { .container {{
text-align: center; text-align: center;
padding: 2rem; padding: 2rem;
max-width: 700px; max-width: 700px;
position: relative; position: relative;
z-index: 10; z-index: 10;
} }}
.skull { .skull {{
font-size: 5rem; font-size: 5rem;
animation: pulse 2s ease-in-out infinite; animation: pulse 2s ease-in-out infinite;
text-shadow: 0 0 20px #f00, 0 0 40px #f00; text-shadow: 0 0 20px #f00, 0 0 40px #f00;
} }}
@keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.1); } } @keyframes pulse {{ 0%, 100% {{ transform: scale(1); }} 50% {{ transform: scale(1.1); }} }}
h1 { h1 {{
font-size: 3rem; font-size: 3rem;
margin: 0.5rem 0; margin: 0.5rem 0;
color: #f00; color: #f00;
text-shadow: 0 0 10px #f00, 0 0 20px #800; text-shadow: 0 0 10px #f00, 0 0 20px #800;
animation: glitch 0.5s infinite; animation: glitch 0.5s infinite;
} }}
@keyframes glitch { @keyframes glitch {{
0%, 90%, 100% { transform: translateX(0); } 0%, 90%, 100% {{ transform: translateX(0); }}
92% { transform: translateX(-2px); } 92% {{ transform: translateX(-2px); }}
94% { transform: translateX(2px); } 94% {{ transform: translateX(2px); }}
96% { transform: translateX(-1px); } 96% {{ transform: translateX(-1px); }}
98% { transform: translateX(1px); } 98% {{ transform: translateX(1px); }}
} }}
h2 { h2 {{
font-size: 1.2rem; font-size: 1.2rem;
color: #0f0; color: #0f0;
margin: 1rem 0; margin: 1rem 0;
} }}
.domain { .domain {{
background: rgba(0, 255, 0, 0.1); background: rgba(0, 255, 0, 0.1);
border: 1px solid #0f0; border: 1px solid #0f0;
padding: 0.8rem 1.5rem; padding: 0.8rem 1.5rem;
@ -92,42 +93,42 @@ NOT_FOUND_HTML = """<!DOCTYPE html>
display: inline-block; display: inline-block;
margin: 1rem 0; margin: 1rem 0;
color: #ff0; color: #ff0;
} }}
.waf-layers { .waf-layers {{
display: flex; display: flex;
justify-content: center; justify-content: center;
gap: 1rem; gap: 1rem;
margin: 1.5rem 0; margin: 1.5rem 0;
flex-wrap: wrap; flex-wrap: wrap;
} }}
.layer { .layer {{
background: rgba(255, 0, 0, 0.2); background: rgba(255, 0, 0, 0.2);
border: 1px solid #f00; border: 1px solid #f00;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
border-radius: 20px; border-radius: 20px;
font-size: 0.8rem; font-size: 0.8rem;
color: #f88; color: #f88;
} }}
.message { .message {{
color: #888; color: #888;
line-height: 1.8; line-height: 1.8;
margin: 1rem 0; margin: 1rem 0;
} }}
.quote { .quote {{
font-style: italic; font-style: italic;
color: #0f0; color: #0f0;
margin: 1.5rem 0; margin: 1.5rem 0;
padding: 1rem; padding: 1rem;
border-left: 3px solid #0f0; border-left: 3px solid #0f0;
text-align: left; text-align: left;
} }}
.footer { .footer {{
margin-top: 2rem; margin-top: 2rem;
font-size: 0.8rem; font-size: 0.8rem;
color: #444; color: #444;
} }}
a { color: #0f0; text-decoration: none; } a {{ color: #0f0; text-decoration: none; }}
a:hover { text-decoration: underline; color: #0ff; } a:hover {{ text-decoration: underline; color: #0ff; }}
</style> </style>
</head> </head>
<body> <body>