High severityNVD Advisory· Published Mar 20, 2025· Updated Jul 15, 2025
Denial of Service (DoS) via Multipart Boundary in zenml-io/zenml
CVE-2024-9340
Description
A Denial of Service (DoS) vulnerability in zenml-io/zenml version 0.66.0 allows unauthenticated attackers to cause excessive resource consumption by sending malformed multipart requests with arbitrary characters appended to the end of multipart boundaries. This flaw in the multipart request boundary processing mechanism leads to an infinite loop, resulting in a complete denial of service for all users. Affected endpoints include /api/v1/login and /api/v1/device_authorization.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
zenmlPyPI | < 0.68.0 | 0.68.0 |
Affected products
1- Range: unspecified
Patches
1cba152eb9ca3Prevent too large requests (#3048)
3 files changed · +95 −2
src/zenml/config/server_config.py+7 −0 modified@@ -29,6 +29,7 @@ DEFAULT_ZENML_SERVER_LOGIN_RATE_LIMIT_DAY, DEFAULT_ZENML_SERVER_LOGIN_RATE_LIMIT_MINUTE, DEFAULT_ZENML_SERVER_MAX_DEVICE_AUTH_ATTEMPTS, + DEFAULT_ZENML_SERVER_MAX_REQUEST_BODY_SIZE_IN_BYTES, DEFAULT_ZENML_SERVER_NAME, DEFAULT_ZENML_SERVER_PIPELINE_RUN_AUTH_WINDOW, DEFAULT_ZENML_SERVER_SECURE_HEADERS_CACHE, @@ -231,6 +232,8 @@ class ServerConfiguration(BaseModel): auto_activate: Whether to automatically activate the server and create a default admin user account with an empty password during the initial deployment. + max_request_body_size_in_bytes: The maximum size of the request body in + bytes. If not specified, the default value of 256 Kb will be used. """ deployment_type: ServerDeploymentType = ServerDeploymentType.OTHER @@ -319,6 +322,10 @@ class ServerConfiguration(BaseModel): thread_pool_size: int = DEFAULT_ZENML_SERVER_THREAD_POOL_SIZE + max_request_body_size_in_bytes: int = ( + DEFAULT_ZENML_SERVER_MAX_REQUEST_BODY_SIZE_IN_BYTES + ) + _deployment_id: Optional[UUID] = None @model_validator(mode="before")
src/zenml/constants.py+1 −0 modified@@ -317,6 +317,7 @@ def handle_int_env_var(var: str, default: int = 0) -> int: DEFAULT_ZENML_SERVER_SECURE_HEADERS_REPORT_TO = "default" DEFAULT_ZENML_SERVER_USE_LEGACY_DASHBOARD = False DEFAULT_ZENML_SERVER_REPORT_USER_ACTIVITY_TO_DB_SECONDS = 30 +DEFAULT_ZENML_SERVER_MAX_REQUEST_BODY_SIZE_IN_BYTES = 256 * 1024 * 1024 # Configurations to decide which resources report their usage and check for # entitlement in the case of a cloud deployment. Expected Format is this:
src/zenml/zen_server/zen_server_api.py+87 −2 modified@@ -24,16 +24,21 @@ from asyncio.log import logger from datetime import datetime, timedelta, timezone from genericpath import isfile -from typing import Any, List +from typing import Any, List, Set from anyio import to_thread from fastapi import FastAPI, HTTPException, Request from fastapi.exceptions import RequestValidationError from fastapi.responses import ORJSONResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates +from starlette.middleware.base import ( + BaseHTTPMiddleware, + RequestResponseEndpoint, +) from starlette.middleware.cors import CORSMiddleware -from starlette.responses import FileResponse +from starlette.responses import FileResponse, JSONResponse, Response +from starlette.types import ASGIApp import zenml from zenml.analytics import source_context @@ -143,6 +148,79 @@ def validation_exception_handler( return ORJSONResponse(error_detail(exc, ValueError), status_code=422) +class RequestBodyLimit(BaseHTTPMiddleware): + """Limits the size of the request body.""" + + def __init__(self, app: ASGIApp, max_bytes: int) -> None: + """Limits the size of the request body. + + Args: + app: The FastAPI app. + max_bytes: The maximum size of the request body. + """ + super().__init__(app) + self.max_bytes = max_bytes + + async def dispatch( + self, request: Request, call_next: RequestResponseEndpoint + ) -> Response: + """Limits the size of the request body. + + Args: + request: The incoming request. + call_next: The next function to be called. + + Returns: + The response to the request. + """ + if content_length := request.headers.get("content-length"): + if int(content_length) > self.max_bytes: + return Response(status_code=413) # Request Entity Too Large + return await call_next(request) + + +class RestrictFileUploadsMiddleware(BaseHTTPMiddleware): + """Restrict file uploads to certain paths.""" + + def __init__(self, app: FastAPI, allowed_paths: Set[str]): + """Restrict file uploads to certain paths. + + Args: + app: The FastAPI app. + allowed_paths: The allowed paths. + """ + super().__init__(app) + self.allowed_paths = allowed_paths + + async def dispatch( + self, request: Request, call_next: RequestResponseEndpoint + ) -> Response: + """Restrict file uploads to certain paths. + + Args: + request: The incoming request. + call_next: The next function to be called. + + Returns: + The response to the request. + """ + if request.method == "POST": + content_type = request.headers.get("content-type", "") + if ( + "multipart/form-data" in content_type + and request.url.path not in self.allowed_paths + ): + return JSONResponse( + status_code=403, + content={ + "detail": "File uploads are not allowed on this endpoint." + }, + ) + return await call_next(request) + + +ALLOWED_FOR_FILE_UPLOAD: Set[str] = set() + app.add_middleware( CORSMiddleware, allow_origins=server_config().cors_allow_origins, @@ -151,6 +229,13 @@ def validation_exception_handler( allow_headers=["*"], ) +app.add_middleware( + RequestBodyLimit, max_bytes=server_config().max_request_body_size_in_bytes +) +app.add_middleware( + RestrictFileUploadsMiddleware, allowed_paths=ALLOWED_FOR_FILE_UPLOAD +) + @app.middleware("http") async def set_secure_headers(request: Request, call_next: Any) -> Any:
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-6gmf-2369-c76cghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-9340ghsaADVISORY
- github.com/pypa/advisory-database/tree/main/vulns/zenml/PYSEC-2025-57.yamlghsaWEB
- github.com/zenml-io/zenml/commit/cba152eb9ca3071c8372b0b91c02d9d3351de48dghsaWEB
- huntr.com/bounties/c9200654-7dc0-4c1d-8573-ab79a87fb4f6ghsaWEB
News mentions
0No linked articles in our index yet.