T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/convert.py:401
- Finding
- Unbounded buffering and storage of remote conversion responses<![CDATA[ ## Vulnerability Details **File Location**: `scripts/convert.py:401-441` **Vulnerability Type**: Unrestricted resource consumption from a remote response **Risk Level**: Medium ### Complete Code Snippet ```python response = session.get(download_url, timeout=120) if response.status_code != 200: raise Exception(f"下载失败,状态码: {response.status_code}") # 获取目标文件扩展名 target_ext = '.pdf' if conversion_type: # 从转换类型推断目标扩展名 type_to_ext = { 'wordTOpdf': '.pdf', 'excelTOpdf': '.pdf', 'pptTOpdf': '.pdf', 'htmlTOpdf': '.pdf', 'pdfTOdocx': '.docx', 'pdf2docx': '.docx', 'pdfTOxlsx': '.xlsx', 'pdfTOpptx': '.pptx', 'pdfTOhtml': '.html', 'pdfTOjpg': '.jpg', 'wordTOjpg': '.jpg', 'excelTOjpg': '.jpg', 'pptTOjpg': '.jpg', 'wordTOexcel': '.xlsx', 'excelTOword': '.docx', 'imageTOpng': '.png', 'imageTOjpg': '.jpg', 'imageTObmp': '.bmp', 'imageToPdf': '.pdf', 'imageToWord': '.docx', 'imageToTxt': '.txt', 'imageToExcel': '.xlsx', } target_ext = type_to_ext.get(conversion_type, '.pdf') # 生成新文件名格式: 原文件名_目标格式.扩展名 if original_file_name and conversion_type: # 获取原文件名(不含扩展名) name_without_ext = os.path.splitext(original_file_name)[0] # 从转换类型提取目标格式(如 wordTOpdf -> pdf, pdfTOdocx -> docx) if 'TO' in conversion_type: type_suffix = conversion_type.split('TO')[-1].lower() else: type_suffix = conversion_type.lower() file_name = f"{name_without_ext}_{type_suffix}{target_ext}" else: # 备用:使用docId file_name = f"converted_{doc_id}{target_ext}" # 确保输出目录存在 os.makedirs(output_dir, exist_ok=True) output_path = os.path.join(output_dir, file_name) with open(output_path, 'wb') as f: f.write(response.content) ``` ### Technical Analysis The download request does not use streaming and does not impose a maximum response size. Accessing `response.content` causes the `requests` library to buffer the complete response body in process memory before it is written to disk. The so ...[truncated 1753 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Request the result as a streamed response: ```python response = session.get(download_url, timeout=120, stream=True) response.raise_for_status() ``` 2. Define a maximum converted-file size appropriate for the service. 3. Reject a declared `Content-Length` that exceeds the limit, while still enforcing the limit during streaming because that header can be missing or inaccurate. 4. Count downloaded bytes and stop before writing data beyond the limit: ```python MAX_DOWNLOAD_BYTES = 100 * 1024 * 1024 downloaded = 0 with open(output_path, "xb") as output: for chunk in response.iter_content(chunk_size=64 * 1024): if not chunk: continue downloaded += len(chunk) if downloaded > MAX_DOWNLOAD_BYTES: raise ValueError("Converted file exceeds the download limit") output.write(chunk) ``` 5. Download into a temporary file in the destination directory and atomically rename it only after successful validation. 6. Delete partial files when a timeout, size violation, or other exception occurs. 7. Validate the downloaded file's expected media type and file signature where practical. ]]>
