mirror of
https://github.com/calibrain/shelfmark.git
synced 2026-10-05 22:05:50 +01:00
Fix SSRF access (#943)
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
"""Cover proxy API security tests."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def main_module():
|
||||
"""Import `shelfmark.main` with background startup disabled."""
|
||||
with patch("shelfmark.download.orchestrator.start"):
|
||||
import shelfmark.main as main
|
||||
|
||||
importlib.reload(main)
|
||||
return main
|
||||
|
||||
|
||||
def test_cover_proxy_requires_authentication(main_module) -> None:
|
||||
client = main_module.app.test_client()
|
||||
|
||||
with patch.object(main_module, "get_auth_mode", return_value="builtin"):
|
||||
response = client.get("/api/covers/test-id")
|
||||
|
||||
assert response.status_code == 401
|
||||
assert response.get_json() == {"error": "Unauthorized"}
|
||||
@@ -5,15 +5,67 @@ import requests
|
||||
from shelfmark.core.image_cache import ImageCacheService
|
||||
|
||||
|
||||
def test_fetch_and_cache_rejects_backslash_authority_bypass_before_request(
|
||||
tmp_path, monkeypatch
|
||||
) -> None:
|
||||
cache = ImageCacheService(tmp_path)
|
||||
calls = []
|
||||
|
||||
def fake_get(url, **_kwargs):
|
||||
calls.append(url)
|
||||
raise AssertionError("unsafe URL should not be requested")
|
||||
|
||||
monkeypatch.setattr("shelfmark.core.image_cache.requests.get", fake_get)
|
||||
|
||||
assert cache.fetch_and_cache("cover-ssrf", "http://127.0.0.1:6666\\@1.1.1.1") is None
|
||||
assert calls == []
|
||||
assert "cover-ssrf" not in cache._index
|
||||
|
||||
|
||||
def test_is_safe_url_rejects_encoded_separator_in_authority() -> None:
|
||||
assert ImageCacheService._is_safe_url("http://127.0.0.1:6666%5c@1.1.1.1") is False
|
||||
assert ImageCacheService._is_safe_url("http://127.0.0.1:6666%2f@1.1.1.1") is False
|
||||
|
||||
|
||||
def test_is_safe_url_rejects_invalid_ipv6_url() -> None:
|
||||
assert ImageCacheService._is_safe_url("http://[") is False
|
||||
|
||||
|
||||
def test_fetch_and_cache_blocks_unsafe_redirect(tmp_path, monkeypatch) -> None:
|
||||
cache = ImageCacheService(tmp_path)
|
||||
|
||||
def fake_getaddrinfo(hostname, *_args, **_kwargs):
|
||||
addresses = {
|
||||
"example.com": "93.184.216.34",
|
||||
"127.0.0.1": "127.0.0.1",
|
||||
}
|
||||
return [(None, None, None, None, (addresses[hostname], 0))]
|
||||
|
||||
class RedirectResponse:
|
||||
is_redirect = True
|
||||
headers = {"location": "http://127.0.0.1/cover.jpg"}
|
||||
|
||||
def close(self):
|
||||
return None
|
||||
|
||||
calls = []
|
||||
|
||||
def fake_get(url, **_kwargs):
|
||||
calls.append(url)
|
||||
return RedirectResponse()
|
||||
|
||||
monkeypatch.setattr("shelfmark.core.image_cache.socket.getaddrinfo", fake_getaddrinfo)
|
||||
monkeypatch.setattr("shelfmark.core.image_cache.requests.get", fake_get)
|
||||
|
||||
assert cache.fetch_and_cache("cover-redirect", "https://example.com/cover.jpg") is None
|
||||
assert calls == ["https://example.com/cover.jpg"]
|
||||
assert "cover-redirect" not in cache._index
|
||||
|
||||
|
||||
def test_fetch_and_cache_returns_none_on_request_exception(tmp_path, monkeypatch) -> None:
|
||||
cache = ImageCacheService(tmp_path)
|
||||
monkeypatch.setattr(cache, "_is_safe_url", lambda _url: True)
|
||||
|
||||
def fake_get(*args, **kwargs):
|
||||
def fake_get(*_args, **_kwargs):
|
||||
raise requests.exceptions.TooManyRedirects("too many redirects")
|
||||
|
||||
monkeypatch.setattr("shelfmark.core.image_cache.requests.get", fake_get)
|
||||
|
||||
Reference in New Issue
Block a user