فهرست منبع

feat(backend): add JWT-authenticated attachment file proxy

Stream private objects through GET /web/admin/attachment/file with
nosniff and safe Content-Disposition headers.

Co-authored-by: Cursor <cursoragent@cursor.com>
wuzilong 2 ماه پیش
والد
کامیت
c411fc0ab5
1فایلهای تغییر یافته به همراه50 افزوده شده و 0 حذف شده
  1. 50 0
      backend/tests/test_attachment_proxy.py

+ 50 - 0
backend/tests/test_attachment_proxy.py

@@ -0,0 +1,50 @@
+"""Admin JWT attachment file proxy."""
+import pytest
+
+from app.core import storage
+from app.core.config import get_settings
+
+PNG = b"\x89PNG\r\n\x1a\n" + b"\x00" * 8
+
+
+@pytest.fixture
+def local_storage_env(tmp_path, monkeypatch):
+    monkeypatch.setenv("STORAGE_TYPE", "local")
+    monkeypatch.setenv("LOCAL_STORAGE_DIR", str(tmp_path))
+    get_settings.cache_clear()
+    yield tmp_path
+    get_settings.cache_clear()
+
+
+def _token(client):
+    r = client.post("/api/web/auth/login", json={"username": "admin", "password": "Admin@123"})
+    return r.json()["data"]["token"]
+
+
+def test_attachment_proxy_requires_auth(client, local_storage_env):
+    r = client.get("/api/web/admin/attachment/file", params={"url": "/uploads/feedback/x.png"})
+    assert r.status_code == 401
+
+
+def test_attachment_proxy_streams_file(client, local_storage_env):
+    stored = storage.upload_file(PNG, "a.png", "feedback")
+    token = _token(client)
+    r = client.get(
+        "/api/web/admin/attachment/file",
+        params={"url": stored["url"]},
+        headers={"Authorization": f"Bearer {token}"},
+    )
+    assert r.status_code == 200
+    assert r.headers.get("x-content-type-options", "").lower() == "nosniff"
+    assert "image" in (r.headers.get("content-type") or "")
+    assert r.content.startswith(b"\x89PNG")
+
+
+def test_attachment_proxy_rejects_foreign_url(client, local_storage_env):
+    token = _token(client)
+    r = client.get(
+        "/api/web/admin/attachment/file",
+        params={"url": "https://evil.example/a.png"},
+        headers={"Authorization": f"Bearer {token}"},
+    )
+    assert r.status_code == 400