From 666f85b743830bb805171c8367c8c5bdb1bfb5b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Wed, 24 Jun 2026 18:01:18 +0400 Subject: [PATCH] =?UTF-8?q?v1.0.176:=20upload.py=20=E2=80=94=20path=20trav?= =?UTF-8?q?ersal=20+=20size=20limit=20+=20UUID=20+=20data=20loss?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/services/upload.py | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/deploy/services/upload.py b/deploy/services/upload.py index 354fc7f..4e77825 100644 --- a/deploy/services/upload.py +++ b/deploy/services/upload.py @@ -1,12 +1,24 @@ """Upload service — accept file, store to DB, parse.""" -import uuid, base64, json, io, cgi +import uuid, base64, json, io, cgi, os from db import documents, contracts, supplements from .parse import parse_file +MAX_FILE_SIZE = 100 * 1024 * 1024 # 100 MB + def handle_upload(rfile, content_type, content_length): """Parse multipart upload, store in DB, return result dict.""" - body = rfile.read(content_length) + # Validate content_length + try: + cl = int(content_length) + except (TypeError, ValueError): + return {"ok": False, "error": "invalid content-length"} + if cl <= 0: + return {"ok": False, "error": "empty request"} + if cl > MAX_FILE_SIZE: + return {"ok": False, "error": f"file too large (max {MAX_FILE_SIZE // 1024 // 1024}MB)"} + + body = rfile.read(cl) environ = { "REQUEST_METHOD": "POST", @@ -23,13 +35,21 @@ def handle_upload(rfile, content_type, content_length): item = fs["files"] if isinstance(item, list): item = item[0] - filename = item.filename + filename = os.path.basename(item.filename) if item.filename else None + if filename and (".." in filename or "/" in filename or "\\" in filename): + return {"ok": False, "error": "invalid filename"} file_data = item.file.read() if hasattr(item, "file") else item.value if isinstance(file_data, str): file_data = file_data.encode("utf-8") if "contract_id" in fs: contract_id = fs.getfirst("contract_id", "") + # Validate UUID + if contract_id: + try: + uuid.UUID(contract_id) + except (ValueError, AttributeError): + contract_id = "" batch_id = fs.getfirst("batch_id", None) if "batch_id" in fs else None # Validate UUID @@ -46,7 +66,10 @@ def handle_upload(rfile, content_type, content_length): b64 = base64.b64encode(file_data).decode() if contract_id: - supplements.delete_by_document(contract_id, filename) + try: + supplements.delete_by_document(contract_id, filename) + except Exception: + pass # old record may not exist or FK issue — proceed with insert doc = documents.insert(filename, mime, b64, batch_id=batch_id)