Files
smart-org/apps/api/app/domain/platform/rules.py
T
mjalmoudy94 717e53a7ce نسخهٔ وب مستقل — پلتفرم نیروی کار هوش مصنوعی
مخزن اختصاصی برنامهٔ وب؛ محتوای پوشهٔ web در ریشهٔ مخزن قرار گرفت.
تاریخچهٔ پروژهٔ ربات (agy-bot) در این مخزن وجود ندارد و هیچ وابستگی runtime به آن نیست.
2026-10-13 00:39:28 +03:30

122 lines
4.2 KiB
Python

"""Platform-wide validation rules carried over from the existing infrastructure.
These are pure functions with no I/O, so both the application layer *and* the
adapters can enforce them. Defence in depth: a rule that is only checked in one
place is a rule that will eventually be bypassed.
Reference: ``docs/current-infra-notes.md`` §1.
"""
from __future__ import annotations
import re
DEFAULT_ROOT_DOMAIN = "msa.artacloud.ir"
#: Ports that are never allocatable (system services + data stores).
SYSTEM_RESERVED_PORTS: frozenset[int] = frozenset(
{22, 53, 80, 443, 2019, 3000, 5432, 6379, 8008}
)
#: Ports of internal platform services. A tenant preview must never proxy to
#: these, otherwise a public hostname would expose another component.
SYSTEM_PROTECTED_PORTS: frozenset[int] = (
frozenset({3000, 35555, 35556, 35557}) | SYSTEM_RESERVED_PORTS
)
DEFAULT_PORT_RANGE_START = 8080
DEFAULT_PORT_RANGE_END = 8999
LABEL_MIN = 3
LABEL_MAX = 40
_LABEL_RE = re.compile(r"^[a-z0-9]([a-z0-9-]*[a-z0-9])?$")
_PROXY_TARGET_RE = re.compile(r"^(https?://)?(localhost|127\.0\.0\.1):(\d{1,5})$")
class RuleViolation(ValueError):
"""Raised when a request violates a platform safety rule."""
def validate_subdomain_label(label: str) -> str:
"""Normalise and validate a single DNS label.
Only ``a-z0-9-``, must start and end alphanumeric, 3-40 characters.
"""
clean = (label or "").strip().lower()
if not (LABEL_MIN <= len(clean) <= LABEL_MAX):
raise RuleViolation(
f"نام زیردامنه باید بین {LABEL_MIN} تا {LABEL_MAX} کاراکتر باشد."
)
if not _LABEL_RE.match(clean):
raise RuleViolation("نام زیردامنه فقط می‌تواند شامل حروف کوچک، عدد و خط تیره باشد.")
return clean
def build_host(label: str, root_domain: str = DEFAULT_ROOT_DOMAIN) -> str:
return f"{validate_subdomain_label(label)}.{root_domain.strip().lower()}"
def validate_managed_host(host: str, root_domain: str = DEFAULT_ROOT_DOMAIN) -> str:
"""Validate a fully-qualified host against the managed wildcard domain.
Rejects anything that is not a single label under the root domain — this is
the anti-injection check that keeps a crafted host from breaking out of a
Caddy site block or pointing at an arbitrary host.
"""
clean = (host or "").strip().lower()
suffix = f".{root_domain.strip().lower()}"
if not clean.endswith(suffix):
raise RuleViolation(f"دامنه باید زیرمجموعهٔ «{root_domain}» باشد.")
label = clean[: -len(suffix)]
if "." in label:
raise RuleViolation("فقط یک برچسب (single label) مجاز است.")
validate_subdomain_label(label)
return clean
def validate_proxy_target(target: str) -> tuple[str, int]:
"""Ensure the reverse-proxy target is a loopback address with a numeric port.
Blocks SSRF: a tenant must not be able to proxy a public hostname to an
internal service.
"""
clean = (target or "").strip()
match = _PROXY_TARGET_RE.match(clean)
if not match:
raise RuleViolation(
"مقصد reverse proxy باید localhost یا 127.0.0.1 با پورت عددی باشد."
)
port = int(match.group(3))
if not (1 <= port <= 65535):
raise RuleViolation("شمارهٔ پورت نامعتبر است.")
return match.group(2), port
def is_port_allocatable(
port: int,
*,
start: int = DEFAULT_PORT_RANGE_START,
end: int = DEFAULT_PORT_RANGE_END,
) -> bool:
if port in SYSTEM_RESERVED_PORTS:
return False
return start <= port <= end
def assert_port_allocatable(
port: int,
*,
start: int = DEFAULT_PORT_RANGE_START,
end: int = DEFAULT_PORT_RANGE_END,
) -> int:
if not is_port_allocatable(port, start=start, end=end):
raise RuleViolation(f"پورت {port} قابل تخصیص نیست.")
return port
def assert_not_protected_port(port: int) -> int:
"""A preview domain must never point at an internal platform port."""
if port in SYSTEM_PROTECTED_PORTS:
raise RuleViolation(f"اتصال به پورت محافظت‌شدهٔ {port} ممنوع است.")
return port