mirror of
https://github.com/calibrain/shelfmark.git
synced 2026-11-10 12:38:32 +00:00
* fix(torrent): check each redirect hop when fetching a torrent file The torrent file fetch followed redirects by hand without looking at where a hop pointed, so a download link could make the server request a loopback, link-local or LAN address. Each hop (the initial URL and every redirect Location) must now be http or https and resolve only to global addresses. The configured Prowlarr/Newznab origin is exempt, as it usually lives on the LAN. The API key still only goes to that origin. * fix(socketio): only accept websocket handshakes from the app's own origin The Socket.IO server allowed every origin. It now uses the same-origin check python-engineio applies by default (Host, or X-Forwarded-Proto and X-Forwarded-Host behind a proxy). SOCKETIO_ALLOWED_ORIGINS takes a comma separated list of extra origins for proxies that change Host without forwarding it. * fix(custom-script): do not pass a file name that starts with a dash as the first argument In relative path mode the script got the organized file's relative path as its first argument, which many scripts read as an option when the name starts with a dash. Such a name is now passed with a ./ prefix. Absolute mode is unchanged.
63 lines
1.8 KiB
Python
63 lines
1.8 KiB
Python
"""The file name handed to a custom script must not be readable as an option."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from shelfmark.download.postprocess.custom_script import (
|
|
CustomScriptExecution,
|
|
run_custom_script,
|
|
)
|
|
|
|
|
|
def _execution(target_arg: str, mode: str) -> CustomScriptExecution:
|
|
destination = Path("/library")
|
|
return CustomScriptExecution(
|
|
script_path="/scripts/hook.sh",
|
|
target_arg=Path(target_arg),
|
|
target_abs=destination / target_arg,
|
|
destination=destination,
|
|
mode=mode,
|
|
phase="after_move",
|
|
payload_json=None,
|
|
)
|
|
|
|
|
|
def _argv_for(execution: CustomScriptExecution) -> list[str]:
|
|
with patch(
|
|
"shelfmark.download.postprocess.custom_script.run_blocking_io",
|
|
return_value=MagicMock(stdout=""),
|
|
) as run:
|
|
assert run_custom_script(execution, task_id="t1", status_callback=MagicMock())
|
|
return list(run.call_args.args[1])
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("target", "expected"),
|
|
[
|
|
("-rf", "./-rf"),
|
|
("--help.epub", "./--help.epub"),
|
|
("-Author/book.epub", "./-Author/book.epub"),
|
|
("normal.epub", "normal.epub"),
|
|
("Author/-odd.epub", "Author/-odd.epub"),
|
|
],
|
|
)
|
|
def test_relative_mode_target_never_starts_with_a_dash(target, expected):
|
|
assert _argv_for(_execution(target, "relative")) == ["/scripts/hook.sh", expected]
|
|
|
|
|
|
def test_absolute_mode_is_unchanged():
|
|
execution = CustomScriptExecution(
|
|
script_path="/scripts/hook.sh",
|
|
target_arg=Path("/library/-rf"),
|
|
target_abs=Path("/library/-rf"),
|
|
destination=Path("/library"),
|
|
mode="absolute",
|
|
phase="after_move",
|
|
payload_json=None,
|
|
)
|
|
assert _argv_for(execution) == ["/scripts/hook.sh", "/library/-rf"]
|