Fix /media/verify apostrophe false-positive #60

Merged
sascha merged 3 commits from fix/media-verify-apostrophe-escaping into main 2026-09-17 21:55:06 +02:00
Showing only changes of commit f5c650fe3d - Show all commits

View file

@ -1,6 +1,7 @@
import os import os
import asyncio import asyncio
import json import json
import shlex
import time import time
from datetime import datetime, timedelta, timezone 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 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): def test_media_verify_rejects_shell_metacharacters_before_ssh(monkeypatch):
monkeypatch.setattr(app, "_ssh", lambda *_args, **_kwargs: (_ for _ in ()).throw(AssertionError("must not SSH"))) monkeypatch.setattr(app, "_ssh", lambda *_args, **_kwargs: (_ for _ in ()).throw(AssertionError("must not SSH")))
with TestClient(app.app) as client: with TestClient(app.app) as client:
response = client.post( response = client.post(
"/media/verify", "/media/verify",
headers={"Authorization": "Bearer test-token"}, 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 assert response.status_code == 400