The previous attempt set ``demuxer_lavf_o`` as an init kwarg with a comma-laden ``protocol_whitelist=file,http,https,tls,tcp`` value. mpv rejected it with -7 OPT_FORMAT because python-mpv's init path goes through ``mpv_set_option_string``, which routes through mpv's keyvalue list parser — that parser splits on ``,`` to find entries, shredding the protocol list into orphan tokens. Backslash-escaping ``\,`` did not unescape on this code path either. Splits the option set into two helpers: - ``build_mpv_kwargs`` — init kwargs only (ytdl=no, load_scripts=no, POSIX input_conf null, all the existing playback/audio/network tuning). The lavf option is intentionally absent. - ``lavf_options`` — a dict applied post-construction via the python-mpv property API, which uses the node API and accepts dict values for keyvalue-list options without splitting on commas inside the value. Tests cover both paths: that ``demuxer_lavf_o`` is NOT in the init kwargs (regression guard), and that ``lavf_options`` returns the expected protocol set. Audit-Ref: SECURITY_AUDIT.md finding #2 Severity: High
89 lines
3.2 KiB
Python
89 lines
3.2 KiB
Python
"""Tests for the pure mpv kwargs builder.
|
|
|
|
Pure Python. No Qt, no mpv, no network. The helper is importable
|
|
from the CI environment that installs only httpx + Pillow + pytest.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from booru_viewer.gui.media._mpv_options import (
|
|
LAVF_PROTOCOL_WHITELIST,
|
|
build_mpv_kwargs,
|
|
lavf_options,
|
|
)
|
|
|
|
|
|
def test_ytdl_disabled():
|
|
"""Finding #2 — mpv must not delegate URLs to yt-dlp."""
|
|
kwargs = build_mpv_kwargs(is_windows=False)
|
|
assert kwargs["ytdl"] == "no"
|
|
|
|
|
|
def test_load_scripts_disabled():
|
|
"""Finding #2 — no auto-loading of ~/.config/mpv/scripts."""
|
|
kwargs = build_mpv_kwargs(is_windows=False)
|
|
assert kwargs["load_scripts"] == "no"
|
|
|
|
|
|
def test_protocol_whitelist_not_in_init_kwargs():
|
|
"""Finding #2 — the lavf protocol whitelist must NOT be in the
|
|
init kwargs dict. python-mpv's init path uses
|
|
``mpv_set_option_string``, which trips on the comma-laden value
|
|
with -7 OPT_FORMAT. The whitelist is applied separately via the
|
|
property API in ``mpv_gl.py`` (see ``lavf_options``)."""
|
|
kwargs = build_mpv_kwargs(is_windows=False)
|
|
assert "demuxer_lavf_o" not in kwargs
|
|
assert "demuxer-lavf-o" not in kwargs
|
|
|
|
|
|
def test_lavf_options_protocol_whitelist():
|
|
"""Finding #2 — lavf demuxer must only accept file + HTTP(S) + TLS/TCP.
|
|
|
|
Returned as a dict so callers can pass it through the python-mpv
|
|
property API (which uses the node API and handles comma-laden
|
|
values cleanly).
|
|
"""
|
|
opts = lavf_options()
|
|
assert opts.keys() == {"protocol_whitelist"}
|
|
allowed = set(opts["protocol_whitelist"].split(","))
|
|
# `file` must be present — cached local clips and .part files use it.
|
|
assert "file" in allowed
|
|
# HTTP(S) + supporting protocols for network videos.
|
|
assert "http" in allowed
|
|
assert "https" in allowed
|
|
assert "tls" in allowed
|
|
assert "tcp" in allowed
|
|
# Dangerous protocols must NOT appear.
|
|
for banned in ("concat", "subfile", "data", "udp", "rtp", "crypto"):
|
|
assert banned not in allowed
|
|
# The constant and the helper return the same value.
|
|
assert opts["protocol_whitelist"] == LAVF_PROTOCOL_WHITELIST
|
|
|
|
|
|
def test_input_conf_nulled_on_posix():
|
|
"""Finding #2 — on POSIX, skip loading ~/.config/mpv/input.conf."""
|
|
kwargs = build_mpv_kwargs(is_windows=False)
|
|
assert kwargs["input_conf"] == "/dev/null"
|
|
|
|
|
|
def test_input_conf_skipped_on_windows():
|
|
"""Finding #2 — input_conf gate is POSIX-only; Windows omits the key."""
|
|
kwargs = build_mpv_kwargs(is_windows=True)
|
|
assert "input_conf" not in kwargs
|
|
|
|
|
|
def test_existing_options_preserved():
|
|
"""Regression: pre-audit playback/audio tuning must remain."""
|
|
kwargs = build_mpv_kwargs(is_windows=False)
|
|
# Discord screen-share audio fix (see mpv_gl.py comment).
|
|
assert kwargs["ao"] == "pulse,wasapi,"
|
|
assert kwargs["audio_client_name"] == "booru-viewer"
|
|
# Network tuning from the uncached-video fast path.
|
|
assert kwargs["cache"] == "yes"
|
|
assert kwargs["cache_pause"] == "no"
|
|
assert kwargs["demuxer_max_bytes"] == "50MiB"
|
|
assert kwargs["network_timeout"] == "10"
|
|
# Existing input lockdown (primary — input_conf is defense-in-depth).
|
|
assert kwargs["input_default_bindings"] is False
|
|
assert kwargs["input_vo_keyboard"] is False
|