Merge pull request '[SECURITY] Prevent Error Information Disclosure (V-013, CVSS 7.5)' (#67) from security/fix-error-disclosure into main
This commit was merged in pull request #67.
This commit is contained in:
@@ -207,6 +207,37 @@ def _openai_error(message: str, err_type: str = "invalid_request_error", param:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# SECURITY FIX (V-013): Safe error handling to prevent info disclosure
|
||||||
|
def _handle_error_securely(exception: Exception, context: str = "") -> Dict[str, Any]:
|
||||||
|
"""Handle errors securely - log full details, return generic message.
|
||||||
|
|
||||||
|
Prevents information disclosure by not exposing internal error details
|
||||||
|
to API clients. Logs full stack trace internally for debugging.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
exception: The caught exception
|
||||||
|
context: Additional context about where the error occurred
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
OpenAI-style error response with generic message
|
||||||
|
"""
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
# Log full error details internally
|
||||||
|
error_id = str(uuid.uuid4())[:8]
|
||||||
|
logger.error(
|
||||||
|
f"Internal error [{error_id}] in {context}: {exception}\n"
|
||||||
|
f"{traceback.format_exc()}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Return generic error to client - no internal details
|
||||||
|
return _openai_error(
|
||||||
|
message=f"An internal error occurred. Reference: {error_id}",
|
||||||
|
err_type="internal_error",
|
||||||
|
code="internal_error"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if AIOHTTP_AVAILABLE:
|
if AIOHTTP_AVAILABLE:
|
||||||
@web.middleware
|
@web.middleware
|
||||||
async def body_limit_middleware(request, handler):
|
async def body_limit_middleware(request, handler):
|
||||||
@@ -1084,7 +1115,8 @@ class APIServerAdapter(BasePlatformAdapter):
|
|||||||
jobs = self._cron_list(include_disabled=include_disabled)
|
jobs = self._cron_list(include_disabled=include_disabled)
|
||||||
return web.json_response({"jobs": jobs})
|
return web.json_response({"jobs": jobs})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return web.json_response({"error": str(e)}, status=500)
|
# SECURITY FIX (V-013): Use secure error handling
|
||||||
|
return web.json_response(_handle_error_securely(e, "list_jobs"), status=500)
|
||||||
|
|
||||||
async def _handle_create_job(self, request: "web.Request") -> "web.Response":
|
async def _handle_create_job(self, request: "web.Request") -> "web.Response":
|
||||||
"""POST /api/jobs — create a new cron job."""
|
"""POST /api/jobs — create a new cron job."""
|
||||||
@@ -1132,7 +1164,8 @@ class APIServerAdapter(BasePlatformAdapter):
|
|||||||
job = self._cron_create(**kwargs)
|
job = self._cron_create(**kwargs)
|
||||||
return web.json_response({"job": job})
|
return web.json_response({"job": job})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return web.json_response({"error": str(e)}, status=500)
|
# SECURITY FIX (V-013): Use secure error handling
|
||||||
|
return web.json_response(_handle_error_securely(e, "list_jobs"), status=500)
|
||||||
|
|
||||||
async def _handle_get_job(self, request: "web.Request") -> "web.Response":
|
async def _handle_get_job(self, request: "web.Request") -> "web.Response":
|
||||||
"""GET /api/jobs/{job_id} — get a single cron job."""
|
"""GET /api/jobs/{job_id} — get a single cron job."""
|
||||||
@@ -1151,7 +1184,8 @@ class APIServerAdapter(BasePlatformAdapter):
|
|||||||
return web.json_response({"error": "Job not found"}, status=404)
|
return web.json_response({"error": "Job not found"}, status=404)
|
||||||
return web.json_response({"job": job})
|
return web.json_response({"job": job})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return web.json_response({"error": str(e)}, status=500)
|
# SECURITY FIX (V-013): Use secure error handling
|
||||||
|
return web.json_response(_handle_error_securely(e, "list_jobs"), status=500)
|
||||||
|
|
||||||
async def _handle_update_job(self, request: "web.Request") -> "web.Response":
|
async def _handle_update_job(self, request: "web.Request") -> "web.Response":
|
||||||
"""PATCH /api/jobs/{job_id} — update a cron job."""
|
"""PATCH /api/jobs/{job_id} — update a cron job."""
|
||||||
@@ -1184,7 +1218,8 @@ class APIServerAdapter(BasePlatformAdapter):
|
|||||||
return web.json_response({"error": "Job not found"}, status=404)
|
return web.json_response({"error": "Job not found"}, status=404)
|
||||||
return web.json_response({"job": job})
|
return web.json_response({"job": job})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return web.json_response({"error": str(e)}, status=500)
|
# SECURITY FIX (V-013): Use secure error handling
|
||||||
|
return web.json_response(_handle_error_securely(e, "list_jobs"), status=500)
|
||||||
|
|
||||||
async def _handle_delete_job(self, request: "web.Request") -> "web.Response":
|
async def _handle_delete_job(self, request: "web.Request") -> "web.Response":
|
||||||
"""DELETE /api/jobs/{job_id} — delete a cron job."""
|
"""DELETE /api/jobs/{job_id} — delete a cron job."""
|
||||||
@@ -1203,7 +1238,8 @@ class APIServerAdapter(BasePlatformAdapter):
|
|||||||
return web.json_response({"error": "Job not found"}, status=404)
|
return web.json_response({"error": "Job not found"}, status=404)
|
||||||
return web.json_response({"ok": True})
|
return web.json_response({"ok": True})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return web.json_response({"error": str(e)}, status=500)
|
# SECURITY FIX (V-013): Use secure error handling
|
||||||
|
return web.json_response(_handle_error_securely(e, "list_jobs"), status=500)
|
||||||
|
|
||||||
async def _handle_pause_job(self, request: "web.Request") -> "web.Response":
|
async def _handle_pause_job(self, request: "web.Request") -> "web.Response":
|
||||||
"""POST /api/jobs/{job_id}/pause — pause a cron job."""
|
"""POST /api/jobs/{job_id}/pause — pause a cron job."""
|
||||||
@@ -1222,7 +1258,8 @@ class APIServerAdapter(BasePlatformAdapter):
|
|||||||
return web.json_response({"error": "Job not found"}, status=404)
|
return web.json_response({"error": "Job not found"}, status=404)
|
||||||
return web.json_response({"job": job})
|
return web.json_response({"job": job})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return web.json_response({"error": str(e)}, status=500)
|
# SECURITY FIX (V-013): Use secure error handling
|
||||||
|
return web.json_response(_handle_error_securely(e, "list_jobs"), status=500)
|
||||||
|
|
||||||
async def _handle_resume_job(self, request: "web.Request") -> "web.Response":
|
async def _handle_resume_job(self, request: "web.Request") -> "web.Response":
|
||||||
"""POST /api/jobs/{job_id}/resume — resume a paused cron job."""
|
"""POST /api/jobs/{job_id}/resume — resume a paused cron job."""
|
||||||
@@ -1241,7 +1278,8 @@ class APIServerAdapter(BasePlatformAdapter):
|
|||||||
return web.json_response({"error": "Job not found"}, status=404)
|
return web.json_response({"error": "Job not found"}, status=404)
|
||||||
return web.json_response({"job": job})
|
return web.json_response({"job": job})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return web.json_response({"error": str(e)}, status=500)
|
# SECURITY FIX (V-013): Use secure error handling
|
||||||
|
return web.json_response(_handle_error_securely(e, "list_jobs"), status=500)
|
||||||
|
|
||||||
async def _handle_run_job(self, request: "web.Request") -> "web.Response":
|
async def _handle_run_job(self, request: "web.Request") -> "web.Response":
|
||||||
"""POST /api/jobs/{job_id}/run — trigger immediate execution."""
|
"""POST /api/jobs/{job_id}/run — trigger immediate execution."""
|
||||||
@@ -1260,7 +1298,8 @@ class APIServerAdapter(BasePlatformAdapter):
|
|||||||
return web.json_response({"error": "Job not found"}, status=404)
|
return web.json_response({"error": "Job not found"}, status=404)
|
||||||
return web.json_response({"job": job})
|
return web.json_response({"job": job})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return web.json_response({"error": str(e)}, status=500)
|
# SECURITY FIX (V-013): Use secure error handling
|
||||||
|
return web.json_response(_handle_error_securely(e, "list_jobs"), status=500)
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Output extraction helper
|
# Output extraction helper
|
||||||
|
|||||||
Reference in New Issue
Block a user