From aa7e703ee1baf412ad05c9079bdd4721dc655c5a Mon Sep 17 00:00:00 2001 From: Amit Oren Date: Tue, 10 Mar 2026 09:51:02 +0200 Subject: [PATCH] fix: stop exposing internal error details in API responses Replace f-string error details with generic messages in HTTP exceptions to avoid leaking stack traces and internal state to clients. Errors are still logged server-side with exc_info=True. Signed-off-by: Amit Oren --- src/neuralnav/api/routes/configuration.py | 29 +++++++++++++---------- src/neuralnav/api/routes/database.py | 13 +++++----- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/neuralnav/api/routes/configuration.py b/src/neuralnav/api/routes/configuration.py index 619fecb..0c87c4f 100644 --- a/src/neuralnav/api/routes/configuration.py +++ b/src/neuralnav/api/routes/configuration.py @@ -104,10 +104,10 @@ async def deploy_model(request: DeploymentRequest): yaml_validator.validate_all(result["files"]) logger.info(f"All YAML files validated for deployment: {result['deployment_id']}") except Exception as e: - logger.error(f"YAML validation failed: {e}") + logger.error(f"YAML validation failed: {e}", exc_info=True) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Generated YAML validation failed: {str(e)}", + detail="Generated YAML validation failed", ) from e return DeploymentResponse( @@ -118,11 +118,13 @@ async def deploy_model(request: DeploymentRequest): message=f"Deployment files generated successfully for {result['deployment_id']}", ) + except HTTPException: + raise except Exception as e: logger.error(f"Failed to generate deployment: {e}", exc_info=True) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to generate deployment: {str(e)}", + detail="Failed to generate deployment", ) from e @@ -249,10 +251,10 @@ async def deploy_to_cluster(request: DeploymentRequest): yaml_validator.validate_all(files) logger.info(f"YAML validation passed for: {deployment_id}") except Exception as e: - logger.error(f"YAML validation failed: {e}") + logger.error(f"YAML validation failed: {e}", exc_info=True) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Generated YAML validation failed: {str(e)}", + detail="Generated YAML validation failed", ) from e # Step 3: Deploy to cluster @@ -264,7 +266,7 @@ async def deploy_to_cluster(request: DeploymentRequest): logger.error(f"Deployment failed: {deployment_result['errors']}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Deployment failed: {deployment_result['errors']}", + detail="Deployment to cluster failed", ) logger.info(f"Successfully deployed {deployment_id} to cluster") @@ -284,7 +286,7 @@ async def deploy_to_cluster(request: DeploymentRequest): logger.error(f"Failed to deploy to cluster: {e}", exc_info=True) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to deploy to cluster: {str(e)}", + detail="Failed to deploy to cluster", ) from e @@ -309,7 +311,7 @@ async def get_cluster_status(): } except Exception as e: logger.error(f"Failed to query cluster status: {e}") - return {"accessible": False, "error": str(e)} + return {"accessible": False, "error": "Failed to connect to cluster"} @router.get("/deployments/{deployment_id}/k8s-status") @@ -343,7 +345,7 @@ async def get_k8s_deployment_status(deployment_id: str): logger.error(f"Failed to get K8s deployment status: {e}", exc_info=True) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to get deployment status: {str(e)}", + detail="Failed to get deployment status", ) from e @@ -384,7 +386,7 @@ async def get_deployment_yaml(deployment_id: str): logger.error(f"Failed to retrieve YAML files: {e}", exc_info=True) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to retrieve YAML files: {str(e)}", + detail="Failed to retrieve YAML files", ) from e @@ -408,9 +410,10 @@ async def delete_deployment(deployment_id: str): result = manager.delete_inferenceservice(deployment_id) if not result["success"]: + logger.error(f"Failed to delete deployment: {result.get('error', 'Unknown error')}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to delete deployment: {result.get('error', 'Unknown error')}", + detail="Failed to delete deployment", ) return result @@ -421,7 +424,7 @@ async def delete_deployment(deployment_id: str): logger.error(f"Failed to delete deployment: {e}", exc_info=True) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to delete deployment: {str(e)}", + detail="Failed to delete deployment", ) from e @@ -459,5 +462,5 @@ async def list_all_deployments(): logger.error(f"Failed to list deployments: {e}", exc_info=True) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to list deployments: {str(e)}", + detail="Failed to list deployments", ) from e diff --git a/src/neuralnav/api/routes/database.py b/src/neuralnav/api/routes/database.py index d6c9609..fa880b2 100644 --- a/src/neuralnav/api/routes/database.py +++ b/src/neuralnav/api/routes/database.py @@ -41,9 +41,10 @@ async def db_status(): finally: conn.close() except Exception as e: - logger.error(f"Failed to get DB status: {e}") + logger.error(f"Failed to get DB status: {e}", exc_info=True) raise HTTPException( - status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"Database not accessible: {e}" + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, + detail="Database not accessible", ) from e @@ -95,10 +96,10 @@ async def upload_benchmarks(file: UploadFile = File(...)): finally: conn.close() except Exception as e: - logger.error(f"Failed to load benchmarks: {e}") + logger.error(f"Failed to load benchmarks: {e}", exc_info=True) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to load benchmarks: {e}", + detail="Failed to load benchmarks", ) from e @@ -126,8 +127,8 @@ async def reset_database(): finally: conn.close() except Exception as e: - logger.error(f"Failed to reset database: {e}") + logger.error(f"Failed to reset database: {e}", exc_info=True) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to reset database: {e}", + detail="Failed to reset database", ) from e