mirror of
https://github.com/calibrain/shelfmark.git
synced 2026-11-14 17:18:26 +00:00
A signed-in user picks the release fields of POST /api/releases/download, and a record id with the "oceanofpdf:" prefix reached the provider without handles() or is_enabled(). The provider then fetched source_url, POSTed to the form action found on that page and fetched the meta-refresh target, with no check on any of them. The provider now refuses to run when it is switched off, and the book page, the form action and every POST redirect hop must be on the configured mirror (same scheme, host and port). The signed file link may sit on another host but must be http(s). network.ensure_public_destination refuses a host that resolves to a loopback, private, link-local or otherwise non-global address (IPv4-mapped IPv6 judged by its IPv4 address). Hosts the operator configured as mirrors and names that do not resolve pass as before. html_get_page and download_url call it on their first URL and check each redirect hop through a response hook. The image cache uses the shared address check too. Co-authored-by: CaliBrain <calibrain@l4n.xyz>
115 lines
3.5 KiB
Python
115 lines
3.5 KiB
Python
"""
|
|
Pytest configuration and shared fixtures.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
|
|
# Set environment variables BEFORE importing the application
|
|
# These override the defaults that try to use system paths like /var/log
|
|
_temp_base = tempfile.mkdtemp(prefix="cwabd_test_")
|
|
|
|
# LOG_ROOT is the base - LOG_DIR is computed as LOG_ROOT / "shelfmark"
|
|
# So we set LOG_ROOT to our temp directory to get LOG_DIR = _temp_base/shelfmark
|
|
os.environ["LOG_ROOT"] = _temp_base
|
|
os.environ["CONFIG_DIR"] = os.path.join(_temp_base, "config")
|
|
os.environ["INGEST_DIR"] = os.path.join(_temp_base, "ingest")
|
|
os.environ["TMP_DIR"] = os.path.join(_temp_base, "tmp")
|
|
|
|
# Create the directories that will be used
|
|
os.makedirs(os.path.join(_temp_base, "shelfmark"), exist_ok=True) # LOG_DIR
|
|
os.makedirs(os.path.join(_temp_base, "config"), exist_ok=True)
|
|
os.makedirs(os.path.join(_temp_base, "ingest"), exist_ok=True)
|
|
os.makedirs(os.path.join(_temp_base, "tmp"), exist_ok=True)
|
|
|
|
# Add the project root to Python path
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _clear_torrent_fetch_cache():
|
|
"""Keep the shared torrent fetch cache from leaking between tests."""
|
|
from shelfmark.download.clients.torrent_utils import clear_torrent_fetch_cache
|
|
|
|
clear_torrent_fetch_cache()
|
|
yield
|
|
clear_torrent_fetch_cache()
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _public_destination_dns(monkeypatch):
|
|
"""Resolve every host to a public address for the destination guard.
|
|
|
|
The guard looks hosts up before a request. Tests use made-up and real hostnames and
|
|
must not depend on the DNS of the machine running them (a filtering resolver answers
|
|
``::`` for some mirrors), so the guard's resolver is stubbed. Tests of the guard
|
|
itself replace it again.
|
|
"""
|
|
from shelfmark.download import network
|
|
|
|
monkeypatch.setattr(
|
|
network,
|
|
"_system_getaddrinfo",
|
|
lambda *_args, **_kwargs: [(2, 1, 6, "", ("93.184.216.34", 0))],
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_prowlarr_result():
|
|
"""Sample Prowlarr API search result."""
|
|
return {
|
|
"guid": "abc123-guid",
|
|
"title": "The Great Book by Author Name.epub",
|
|
"indexer": "MyIndexer",
|
|
"protocol": "torrent",
|
|
"size": 5242880, # 5 MB
|
|
"downloadUrl": "magnet:?xt=urn:btih:abc123",
|
|
"infoUrl": "https://example.com/book/123",
|
|
"seeders": 10,
|
|
"leechers": 2,
|
|
"publishDate": "2024-01-15T12:00:00Z",
|
|
"categories": [{"id": 7020, "name": "Books/EBook"}],
|
|
"indexerId": 1,
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_nzb_result():
|
|
"""Sample Prowlarr API NZB result."""
|
|
return {
|
|
"guid": "nzb456-guid",
|
|
"title": "Another Book [PDF] by Writer",
|
|
"indexer": "NZBIndexer",
|
|
"protocol": "usenet",
|
|
"size": 10485760, # 10 MB
|
|
"downloadUrl": "https://example.com/download.nzb",
|
|
"infoUrl": "https://example.com/nzb/456",
|
|
"grabs": 50,
|
|
"publishDate": "2024-02-20T10:30:00Z",
|
|
"categories": [{"id": 7020, "name": "Books/EBook"}],
|
|
"indexerId": 2,
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_config(monkeypatch):
|
|
"""Fixture to mock config values."""
|
|
config_values = {}
|
|
|
|
def mock_get(key, default=""):
|
|
return config_values.get(key, default)
|
|
|
|
def set_config(key, value):
|
|
config_values[key] = value
|
|
|
|
# Create a mock config module
|
|
class MockConfig:
|
|
get = staticmethod(mock_get)
|
|
set = staticmethod(set_config)
|
|
_values = config_values
|
|
|
|
return MockConfig
|