security: harden code against command injection and path traversal
Core changes: * enforce HTTPS URLs and remove shell usage in generate_release.py * replace os.system calls with subprocess across the codebase * validate external inputs (playlist names, video IDs) Improvements and fixes: * settings.py: fix typo (node.lineno → line_number); use isinstance() over type() * youtube/get_app_version: improve git detection using subprocess.DEVNULL * youtube/util.py: add cleanup helpers; use shutil.which for binary resolution YouTube modules: * watch.py: detect and flag HLS streams; remove unused audio_track_sources * comments.py: return early when comments are disabled; add error handling * local_playlist.py: validate playlist names to prevent path traversal * subscriptions.py: replace asserts with proper error handling; validate video IDs Cleanup: * remove unused imports across modules (playlist, search, channel) * reorganize package imports in youtube/**init**.py * simplify test imports and fix cleanup_func in tests Tests: * tests/test_shorts.py: simplify imports * tests/test_util.py: fix cleanup_func definition
This commit is contained in:
@@ -1,27 +1,26 @@
|
||||
import json
|
||||
import logging
|
||||
import math
|
||||
import os
|
||||
import re
|
||||
import traceback
|
||||
import urllib
|
||||
from math import ceil
|
||||
from types import SimpleNamespace
|
||||
from urllib.parse import parse_qs, urlencode
|
||||
|
||||
import flask
|
||||
import gevent
|
||||
import urllib3.exceptions
|
||||
from flask import request
|
||||
|
||||
import youtube
|
||||
from youtube import yt_app
|
||||
from youtube import util, comments, local_playlist, yt_data_extract
|
||||
from youtube.util import time_utc_isoformat
|
||||
import settings
|
||||
|
||||
from flask import request
|
||||
import flask
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
import json
|
||||
import gevent
|
||||
import os
|
||||
import math
|
||||
import traceback
|
||||
import urllib
|
||||
import re
|
||||
import urllib3.exceptions
|
||||
from urllib.parse import parse_qs, urlencode
|
||||
from types import SimpleNamespace
|
||||
from math import ceil
|
||||
|
||||
|
||||
try:
|
||||
with open(os.path.join(settings.data_dir, 'decrypt_function_cache.json'), 'r') as f:
|
||||
@@ -62,7 +61,9 @@ def get_video_sources(info, target_resolution):
|
||||
continue
|
||||
if not (fmt['init_range'] and fmt['index_range']):
|
||||
# Allow HLS-backed audio tracks (served locally, no init/index needed)
|
||||
if not fmt.get('url', '').startswith('http://127.') and not '/ytl-api/' in fmt.get('url', ''):
|
||||
url_value = fmt.get('url', '')
|
||||
if (not url_value.startswith('http://127.')
|
||||
and '/ytl-api/' not in url_value):
|
||||
continue
|
||||
# Mark as HLS for frontend
|
||||
fmt['is_hls'] = True
|
||||
@@ -222,7 +223,7 @@ def lang_in(lang, sequence):
|
||||
if lang is None:
|
||||
return False
|
||||
lang = lang[0:2]
|
||||
return lang in (l[0:2] for l in sequence)
|
||||
return lang in (item[0:2] for item in sequence)
|
||||
|
||||
|
||||
def lang_eq(lang1, lang2):
|
||||
@@ -238,9 +239,9 @@ def equiv_lang_in(lang, sequence):
|
||||
e.g. if lang is en, extracts en-GB from sequence.
|
||||
Necessary because if only a specific variant like en-GB is available, can't ask YouTube for simply en. Need to get the available variant.'''
|
||||
lang = lang[0:2]
|
||||
for l in sequence:
|
||||
if l[0:2] == lang:
|
||||
return l
|
||||
for item in sequence:
|
||||
if item[0:2] == lang:
|
||||
return item
|
||||
return None
|
||||
|
||||
|
||||
@@ -310,7 +311,15 @@ def get_subtitle_sources(info):
|
||||
sources[-1]['on'] = True
|
||||
|
||||
if len(sources) == 0:
|
||||
assert len(info['automatic_caption_languages']) == 0 and len(info['manual_caption_languages']) == 0
|
||||
# Invariant: with no caption sources there should be no languages
|
||||
# either. Don't rely on `assert` which is stripped under `python -O`.
|
||||
if (len(info['automatic_caption_languages']) != 0
|
||||
or len(info['manual_caption_languages']) != 0):
|
||||
logger.warning(
|
||||
'Unexpected state: no subtitle sources but %d auto / %d manual languages',
|
||||
len(info['automatic_caption_languages']),
|
||||
len(info['manual_caption_languages']),
|
||||
)
|
||||
|
||||
return sources
|
||||
|
||||
@@ -669,7 +678,6 @@ def format_bytes(bytes):
|
||||
@yt_app.route('/ytl-api/audio-track-proxy')
|
||||
def audio_track_proxy():
|
||||
"""Proxy for DASH audio tracks to avoid throttling."""
|
||||
cache_key = request.args.get('id', '')
|
||||
audio_url = request.args.get('url', '')
|
||||
|
||||
if not audio_url:
|
||||
@@ -692,7 +700,7 @@ def audio_track_proxy():
|
||||
@yt_app.route('/ytl-api/audio-track')
|
||||
def get_audio_track():
|
||||
"""Proxy HLS audio/video: playlist or individual segment."""
|
||||
from youtube.hls_cache import get_hls_url, _tracks
|
||||
from youtube.hls_cache import get_hls_url
|
||||
|
||||
cache_key = request.args.get('id', '')
|
||||
seg_url = request.args.get('seg', '')
|
||||
@@ -916,7 +924,7 @@ def get_hls_manifest():
|
||||
flask.abort(404, 'HLS manifest not found')
|
||||
|
||||
try:
|
||||
print(f'[hls-manifest] Fetching HLS manifest...')
|
||||
print('[hls-manifest] Fetching HLS manifest...')
|
||||
manifest = util.fetch_url(hls_url,
|
||||
headers=(('User-Agent', 'Mozilla/5.0'),),
|
||||
debug_name='hls_manifest').decode('utf-8')
|
||||
@@ -1018,7 +1026,8 @@ def get_storyboard_vtt():
|
||||
for i, board in enumerate(boards):
|
||||
*t, _, sigh = board.split("#")
|
||||
width, height, count, width_cnt, height_cnt, interval = map(int, t)
|
||||
if height != wanted_height: continue
|
||||
if height != wanted_height:
|
||||
continue
|
||||
q['sigh'] = [sigh]
|
||||
url = f"{base_url}?{urlencode(q, doseq=True)}"
|
||||
storyboard = SimpleNamespace(
|
||||
@@ -1182,7 +1191,6 @@ def get_watch_page(video_id=None):
|
||||
uni_sources = video_sources['uni_sources']
|
||||
pair_sources = video_sources['pair_sources']
|
||||
pair_idx = video_sources['pair_idx']
|
||||
audio_track_sources = video_sources['audio_track_sources']
|
||||
|
||||
# Build audio tracks list from HLS
|
||||
audio_tracks = []
|
||||
|
||||
Reference in New Issue
Block a user