mirror of
https://github.com/alexta69/metube.git
synced 2026-09-21 13:35:01 +00:00
fix: enforce download-dir containment at the resolved-path chokepoint
The per-download chapter_template was validated only against literal ".." in the template string, but yt-dlp expands %(section_title)s (and every other field) from attacker-controlled metadata at download time. On POSIX hosts yt-dlp does not neutralise a ".." path component, so a chapter titled ".." turns a guard-passing template like "%(section_title)s/%(section_title)s/x.%(ext)s" into "../../x.mp4" and writes outside DOWNLOAD_DIR. The same class of escape applies to any multi-segment output template (default/playlist/channel) whose fields resolve to "..". The template string can never see the "..": it only exists after expansion. So move the check to the one point every output path flows through — YoutubeDL.prepare_filename — via a _ConfinedYoutubeDL subclass that refuses any resolved path outside the download/temp roots (fail closed). This covers the main file, split-chapter files, thumbnails and subtitles in one place. With the chokepoint authoritative, the scattered ingress string checks (chapter_template, custom_name_prefix) and the weaker _output_dir_escapes literal-prefix check are removed. Tests move from the ingress layer to the chokepoint, exercising the real metadata-resolution vector. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -138,25 +138,6 @@ async def test_add_invalid_subtitle_language(mock_dqueue):
|
||||
await main.add(req)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_custom_name_prefix_path_traversal(mock_dqueue):
|
||||
req = _json_request(_valid_video_add_body(custom_name_prefix="../evil"))
|
||||
with pytest.raises(web.HTTPBadRequest):
|
||||
await main.add(req)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_chapter_template_path_traversal(mock_dqueue):
|
||||
req = _json_request(
|
||||
_valid_video_add_body(
|
||||
split_by_chapters=True,
|
||||
chapter_template="/etc/passwd%(title)s",
|
||||
)
|
||||
)
|
||||
with pytest.raises(web.HTTPBadRequest):
|
||||
await main.add(req)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_invalid_json_body(mock_dqueue):
|
||||
req = MagicMock(spec=web.Request)
|
||||
|
||||
Reference in New Issue
Block a user