fix(security): harden uploads and wecom binding
This commit is contained in:
@@ -1,7 +1,13 @@
|
||||
from fastapi import HTTPException
|
||||
import asyncio
|
||||
from io import BytesIO
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
from app.api.upload import build_object_key, is_owned_upload_key, validate_image_upload
|
||||
from fastapi import HTTPException
|
||||
from PIL import Image
|
||||
|
||||
from app.api.upload import build_object_key, is_owned_upload_key, normalize_image, validate_image_upload
|
||||
from app.config import settings, validate_security_settings
|
||||
from app.services.wecom import consume_bind_token, store_bind_token
|
||||
|
||||
|
||||
USER_ID = "a2d4447f-7b67-4304-ad9a-953007e75ef2"
|
||||
@@ -32,6 +38,46 @@ def test_upload_rejects_an_invalid_filename_extension():
|
||||
raise AssertionError("invalid extensions must be rejected")
|
||||
|
||||
|
||||
def test_image_content_is_normalized_to_jpeg():
|
||||
source = BytesIO()
|
||||
Image.new("RGBA", (8, 8), color=(255, 0, 0, 128)).save(source, format="PNG")
|
||||
|
||||
normalized = normalize_image(source.getvalue())
|
||||
|
||||
with Image.open(BytesIO(normalized)) as image:
|
||||
assert image.format == "JPEG"
|
||||
assert image.mode == "RGB"
|
||||
|
||||
|
||||
def test_image_content_rejects_non_images():
|
||||
try:
|
||||
normalize_image(b"not an image")
|
||||
except HTTPException as exc:
|
||||
assert exc.status_code == 400
|
||||
else:
|
||||
raise AssertionError("non-image bytes must be rejected")
|
||||
|
||||
|
||||
def test_wecom_binding_tokens_are_persisted_and_consumed_atomically():
|
||||
session = MagicMock()
|
||||
session.execute = AsyncMock()
|
||||
session.commit = AsyncMock()
|
||||
|
||||
token = asyncio.run(store_bind_token(session, "wecom-user"))
|
||||
|
||||
assert len(token) == 32
|
||||
assert session.add.call_args.args[0].wecom_userid == "wecom-user"
|
||||
session.commit.assert_awaited_once()
|
||||
|
||||
result = MagicMock()
|
||||
result.scalar_one_or_none.return_value = "wecom-user"
|
||||
session.execute = AsyncMock(return_value=result)
|
||||
session.commit = AsyncMock()
|
||||
|
||||
assert asyncio.run(consume_bind_token(session, token)) == "wecom-user"
|
||||
session.commit.assert_awaited_once()
|
||||
|
||||
|
||||
def test_production_rejects_placeholder_database_configuration(monkeypatch):
|
||||
monkeypatch.setattr(settings, "ENVIRONMENT", "production")
|
||||
monkeypatch.setattr(settings, "SECRET_KEY", "a" * 32)
|
||||
|
||||
Reference in New Issue
Block a user