Initial release: cloud browser with auth and one-click deploy on port 32450
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
import ipaddress
|
||||
import os
|
||||
import re
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
||||
BLOCKED_HOSTNAMES = {
|
||||
"localhost",
|
||||
"localhost.localdomain",
|
||||
"metadata.google.internal",
|
||||
}
|
||||
|
||||
PRIVATE_NETWORKS = [
|
||||
ipaddress.ip_network("0.0.0.0/8"),
|
||||
ipaddress.ip_network("10.0.0.0/8"),
|
||||
ipaddress.ip_network("127.0.0.0/8"),
|
||||
ipaddress.ip_network("169.254.0.0/16"),
|
||||
ipaddress.ip_network("172.16.0.0/12"),
|
||||
ipaddress.ip_network("192.168.0.0/16"),
|
||||
ipaddress.ip_network("::1/128"),
|
||||
ipaddress.ip_network("fc00::/7"),
|
||||
ipaddress.ip_network("fe80::/10"),
|
||||
]
|
||||
|
||||
ALLOWED_SCHEMES = {"http", "https"}
|
||||
|
||||
|
||||
class SecurityError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
def _normalize_url(raw_url: str) -> str:
|
||||
url = raw_url.strip()
|
||||
if not url:
|
||||
raise SecurityError("URL 不能为空")
|
||||
|
||||
if not re.match(r"^[a-zA-Z][a-zA-Z0-9+.-]*://", url):
|
||||
url = f"https://{url}"
|
||||
|
||||
parsed = urlparse(url)
|
||||
if parsed.scheme not in ALLOWED_SCHEMES:
|
||||
raise SecurityError("仅允许 http/https 协议")
|
||||
|
||||
if not parsed.netloc:
|
||||
raise SecurityError("URL 格式无效")
|
||||
|
||||
if parsed.username or parsed.password:
|
||||
raise SecurityError("URL 中不允许包含用户名或密码")
|
||||
|
||||
hostname = parsed.hostname
|
||||
if not hostname:
|
||||
raise SecurityError("无法解析主机名")
|
||||
|
||||
hostname_lower = hostname.lower()
|
||||
if hostname_lower in BLOCKED_HOSTNAMES:
|
||||
raise SecurityError("不允许访问该主机")
|
||||
|
||||
if hostname_lower.endswith(".local") or hostname_lower.endswith(".internal"):
|
||||
raise SecurityError("不允许访问内网域名")
|
||||
|
||||
try:
|
||||
ip = ipaddress.ip_address(hostname)
|
||||
except ValueError:
|
||||
return url
|
||||
|
||||
for network in PRIVATE_NETWORKS:
|
||||
if ip in network:
|
||||
raise SecurityError("不允许访问内网或本地地址")
|
||||
|
||||
return url
|
||||
|
||||
|
||||
def validate_url(raw_url: str) -> str:
|
||||
return _normalize_url(raw_url)
|
||||
|
||||
|
||||
def get_max_sessions() -> int:
|
||||
return max(1, int(os.getenv("MAX_SESSIONS", "1")))
|
||||
|
||||
|
||||
def get_idle_timeout() -> int:
|
||||
return max(60, int(os.getenv("SESSION_IDLE_TIMEOUT", "1800")))
|
||||
|
||||
|
||||
def get_viewport_size() -> tuple[int, int]:
|
||||
width = max(800, int(os.getenv("VIEWPORT_WIDTH", "1280")))
|
||||
height = max(600, int(os.getenv("VIEWPORT_HEIGHT", "720")))
|
||||
return width, height
|
||||
|
||||
|
||||
def get_screencast_quality() -> int:
|
||||
quality = int(os.getenv("SCREENCAST_QUALITY", "80"))
|
||||
return min(100, max(10, quality))
|
||||
Reference in New Issue
Block a user