diff --git a/tests/test_app.py b/tests/test_app.py index ae9cde0..1bc0857 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -1,6 +1,7 @@ import os import asyncio import json +import shlex import time from datetime import datetime, timedelta, timezone @@ -951,13 +952,38 @@ def test_media_verify_rejects_path_outside_data_before_ssh(monkeypatch): assert response.status_code == 400 +def test_media_verify_allows_apostrophe_in_filename_and_quotes_it_safely(monkeypatch): + monkeypatch.setattr(app, "_find_inventory_host", lambda host: {"user": "sascha", "ip": "10.2.1.100"}) + calls = [] + + def fake_ssh(host, command, timeout=30): + calls.append((host, command, timeout)) + return 0, "2967.0\n", "" + + monkeypatch.setattr(app, "_ssh", fake_ssh) + path = "/data/FHD/serien/Star Trek - Strange New Worlds (2022)/Season 04/Once La'An a Time.mkv" + with TestClient(app.app) as client: + response = client.post( + "/media/verify", + headers={"Authorization": "Bearer test-token"}, + json={"host": "arrapps", "path": path, "expected_minutes": 49.5}, + ) + assert response.status_code == 200 + # shlex.quote must produce a command the remote shell parses as ONE argument, + # i.e. no unescaped apostrophe breaks out of quoting. + executed_cmd = calls[0][1] + quoted = shlex.quote(path) + assert quoted in executed_cmd + assert shlex.split(executed_cmd)[-1] == path + + def test_media_verify_rejects_shell_metacharacters_before_ssh(monkeypatch): monkeypatch.setattr(app, "_ssh", lambda *_args, **_kwargs: (_ for _ in ()).throw(AssertionError("must not SSH"))) with TestClient(app.app) as client: response = client.post( "/media/verify", headers={"Authorization": "Bearer test-token"}, - json={"host": "arrapps", "path": "/data/UHD/serien/a'; rm -rf /'.mkv"}, + json={"host": "arrapps", "path": "/data/UHD/serien/a\x00.mkv"}, ) assert response.status_code == 400