fix: harden project writes and uploads

This commit is contained in:
nac
2026-07-13 02:21:19 +00:00
parent 546da0ad45
commit 87a26196dd
7 changed files with 221 additions and 10 deletions
+54
View File
@@ -3,6 +3,7 @@ import unittest
import warnings
from pathlib import Path
from tempfile import TemporaryDirectory
from unittest.mock import patch
import httpx
from starlette.applications import Starlette
@@ -103,6 +104,59 @@ class UploadEndpointTests(unittest.IsolatedAsyncioTestCase):
self.assertEqual(temp_paths, [])
self.assertFalse(any(issubclass(warning.category, ResourceWarning) for warning in caught))
async def test_upload_rejects_non_file_form_fields(self) -> None:
http_client = FakeHttpClient()
with TemporaryDirectory() as temp_dir:
async with await self._client_for(http_client, temp_dir) as client:
response = await client.post(
"/uploads",
files={"file": ("quote.pdf", b"pdf", "application/pdf")},
data={"description": "quote"},
)
self.assertEqual(response.status_code, 400)
self.assertEqual(response.json()["error"]["code"], "invalid_upload")
self.assertEqual(http_client.calls, [])
self.assertEqual(list(Path(temp_dir).iterdir()), [])
async def test_upload_rejects_oversized_misreported_chunked_body_before_multipart_parsing(self) -> None:
http_client = FakeHttpClient()
endpoint = UploadEndpoint(http_client, max_bytes=5, temp_dir="/tmp")
parsed = False
async def forbidden_form(self):
nonlocal parsed
parsed = True
self.fail("multipart parsing must not begin for an oversized body")
body = b"x" * (5 + 65537)
scope = {
"type": "http",
"method": "POST",
"path": "/uploads",
"headers": [
(b"content-type", b"multipart/form-data; boundary=test"),
(b"content-length", b"1"),
],
}
messages = [
{"type": "http.request", "body": body[:10], "more_body": True},
{"type": "http.request", "body": body[10:], "more_body": False},
]
async def receive():
return messages.pop(0)
from starlette.requests import Request
starlette_request = Request(scope, receive=receive)
with patch("nocobase_crm_mcp.uploads.Request.form", new=forbidden_form):
response = await endpoint(starlette_request)
self.assertEqual(response.status_code, 413)
self.assertFalse(parsed)
self.assertEqual(http_client.calls, [])
async def test_upload_rejects_disallowed_mime_type_before_calling_nocobase(self) -> None:
http_client = FakeHttpClient()
with TemporaryDirectory() as temp_dir: