diff --git a/backend/llm/orchestrator.py b/backend/llm/orchestrator.py index 30dba5a..1aeb4fd 100644 --- a/backend/llm/orchestrator.py +++ b/backend/llm/orchestrator.py @@ -2695,29 +2695,12 @@ def _runtime_progress_root() -> Path: return progress_root -def _legacy_orchestration_progress_file_path(run_id: str) -> Path: - safe_run_id = re.sub(r"[^A-Za-z0-9._-]", "_", str(run_id or "unknown")) - return _runtime_progress_root() / f"{safe_run_id}.json" - - def _orchestration_progress_file_path(run_id: str) -> Path: - normalized_run_id = str(run_id or "unknown") - safe_run_id = re.sub(r"[^A-Za-z0-9_.-]", "_", normalized_run_id).strip("._-") - if not safe_run_id: - safe_run_id = "unknown" + normalized_run_id = str(run_id if run_id is not None else "unknown") + if normalized_run_id == "": + normalized_run_id = "unknown" runtime_root = _runtime_progress_root().resolve() - legacy_path = _legacy_orchestration_progress_file_path(safe_run_id) - try: - resolved_legacy_path = legacy_path.resolve() - if resolved_legacy_path.exists() and resolved_legacy_path.is_relative_to(runtime_root): - return resolved_legacy_path - except Exception: - logger.warning( - "Ignoring unsafe legacy orchestration progress path for run_id=%s", - safe_run_id, - exc_info=True, - ) - file_name = f"{hashlib.sha256(safe_run_id.encode('utf-8')).hexdigest()}.json" + file_name = f"{hashlib.sha256(normalized_run_id.encode('utf-8')).hexdigest()}.json" return runtime_root / file_name diff --git a/run_profiler_backend.py b/run_profiler_backend.py index 19a6b96..55c66ab 100644 --- a/run_profiler_backend.py +++ b/run_profiler_backend.py @@ -1,5 +1,6 @@ from __future__ import annotations +import ipaddress import logging import os import socket @@ -32,8 +33,35 @@ def _is_container_runtime() -> bool: def _default_profiler_host() -> str: - if _is_container_runtime(): - return "0.0.0.0" + return "127.0.0.1" + + +def _resolve_profiler_host() -> str: + requested_host = (os.getenv("BACKEND_PROFILER_HOST") or _default_profiler_host()).strip() + allow_remote = (os.getenv("BACKEND_PROFILER_ALLOW_REMOTE", "") or "").strip().lower() in {"1", "true", "yes", "on"} + if requested_host == "localhost": + try: + infos = socket.getaddrinfo("localhost", None) + if infos and all(ipaddress.ip_address(info[4][0]).is_loopback for info in infos): + return requested_host + except Exception: + logger.warning("[WARN] failed to resolve localhost loopback addresses", exc_info=True) + logger.warning("[WARN] localhost does not resolve to loopback only; fallback to 127.0.0.1") + return "127.0.0.1" + if requested_host in {"127.0.0.1", "::1"}: + return requested_host + try: + requested_ip = ipaddress.ip_address(requested_host) + except (TypeError, ValueError): + logger.warning("[WARN] hostname profiler host=%s is not allowed; fallback to 127.0.0.1", requested_host) + return "127.0.0.1" + if requested_ip.is_loopback: + return requested_host + if allow_remote: + if requested_ip.is_unspecified: + logger.warning("[WARN] profiler backend is binding to all interfaces (host=%s)", requested_host) + return requested_host + logger.warning("[WARN] remote profiler host=%s blocked; set BACKEND_PROFILER_ALLOW_REMOTE=true to allow", requested_host) return "127.0.0.1" @@ -68,7 +96,7 @@ def _resolve_bind_port(host: str, requested_port: int, max_attempts: int = 20) - def main() -> None: import uvicorn - host = os.getenv("BACKEND_PROFILER_HOST", _default_profiler_host()) + host = _resolve_profiler_host() port = _resolve_bind_port(host, int(os.getenv("BACKEND_PROFILER_PORT", "8000"))) logger.info("[OK] profiler backend bind target: http://%s:%s", host, port) uvicorn.run(app, host=host, port=port, reload=False)