diff --git a/backend/app/component/environment.py b/backend/app/component/environment.py index 5b3a350f..ebed2b7c 100644 --- a/backend/app/component/environment.py +++ b/backend/app/component/environment.py @@ -46,7 +46,7 @@ def set_user_env_path(env_path: str | None = None): delattr(_thread_local, 'env_path') traceroot_logger.info("Reset to default global environment") - if env_path and (not sanitized_path or not os.path.exists(env_path)): + if env_path and (not sanitized_path or not (sanitized_path and sanitized_path.exists())): traceroot_logger.warning( "User environment path does not exist or is invalid, falling back to global", extra={"env_path": env_path} ) diff --git a/server/app/controller/mcp/proxy_controller.py b/server/app/controller/mcp/proxy_controller.py index 8dc97869..790ea606 100644 --- a/server/app/controller/mcp/proxy_controller.py +++ b/server/app/controller/mcp/proxy_controller.py @@ -111,10 +111,21 @@ def google_search(query: str, search_type: str = "web", key: Key = Depends(key_m def _redact_secret(text: str) -> str: redacted = text for secret in secrets_to_redact: - if secret: + if secret and isinstance(redacted, str): redacted = redacted.replace(secret, "[REDACTED]") return redacted + def _redact_obj(obj): + """Recursively redact secrets from all string fields in a dict/list structure.""" + if isinstance(obj, dict): + return {k: _redact_obj(v) for k, v in obj.items()} + elif isinstance(obj, list): + return [_redact_obj(item) for item in obj] + elif isinstance(obj, str): + return _redact_secret(obj) + else: + return obj + # Using the first page start_page_idx = 1 # Different language may get different result @@ -207,14 +218,10 @@ def google_search(query: str, search_type: str = "web", key: Key = Depends(key_m logger.info("Google search completed", extra={"query": query, "search_type": search_type, "result_count": len(responses)}) else: error_info = data.get("error", {}) - sanitized_error = { - "code": error_info.get("code"), - "reason": (error_info.get("errors") or [{}])[0].get("reason"), - "message": _redact_secret(error_info.get("message", "")), - } + sanitized_error = _redact_obj(error_info) logger.error( "Google search API error", - extra={"query": query, "search_type": search_type, "api_error": sanitized_error}, + extra={"query": _redact_secret(query), "search_type": _redact_secret(search_type), "api_error": sanitized_error}, ) raise HTTPException(status_code=500, detail="Internal server error") diff --git a/server/app/controller/oauth/oauth_controller.py b/server/app/controller/oauth/oauth_controller.py index 3522d560..853dcde9 100644 --- a/server/app/controller/oauth/oauth_controller.py +++ b/server/app/controller/oauth/oauth_controller.py @@ -40,9 +40,14 @@ def oauth_login(app: str, request: Request, state: Optional[str] = None): @traceroot.trace() def oauth_callback(app: str, request: Request, code: Optional[str] = None, state: Optional[str] = None): """Handle OAuth provider callback and redirect to client app.""" - if not code: - logger.warning("OAuth callback missing code", extra={"provider": app}) - raise HTTPException(status_code=400, detail="Missing code parameter") + import re + CODE_STATE_REGEX = re.compile(r'^[A-Za-z0-9_\-]+$') + if not code or not CODE_STATE_REGEX.match(code): + logger.warning("OAuth callback missing or invalid code", extra={"provider": app, "code": code}) + raise HTTPException(status_code=400, detail="Missing or invalid code parameter") + if state and not CODE_STATE_REGEX.match(state): + logger.warning("OAuth callback invalid state", extra={"provider": app, "state": state}) + raise HTTPException(status_code=400, detail="Invalid state parameter") logger.info("OAuth callback received", extra={"provider": app, "has_state": state is not None}) diff --git a/server/app/controller/redirect_controller.py b/server/app/controller/redirect_controller.py index 54121c38..8aee8609 100644 --- a/server/app/controller/redirect_controller.py +++ b/server/app/controller/redirect_controller.py @@ -1,3 +1,4 @@ +import re from urllib.parse import urlencode, quote from fastapi import APIRouter, Request from fastapi.responses import RedirectResponse @@ -8,7 +9,9 @@ router = APIRouter(tags=["Redirect"]) @router.get("/redirect/callback") def redirect_callback(code: str, request: Request): - + if not re.match(r'^[A-Za-z0-9_-]+$', code): + # fallback safe redirect without user data + return RedirectResponse("eigent://callback") params = {"code": code} query = urlencode(params, quote_via=quote) redirect_url = f"eigent://callback?{query}"