Skip to content

Commit

Permalink
m3u: Ignore paths outside the playlist_dir
Browse files Browse the repository at this point in the history
  • Loading branch information
jodal committed Sep 17, 2018
1 parent 0d258f1 commit 6ac759e
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 0 deletions.
23 changes: 23 additions & 0 deletions mopidy/m3u/playlists.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import tempfile

from mopidy import backend
from mopidy.internal import path

from . import Extension, translator

Expand Down Expand Up @@ -89,6 +90,9 @@ def create(self, name):

def delete(self, uri):
path = translator.uri_to_path(uri)
if not self._is_in_basedir(path):
logger.debug('Ignoring path outside playlist dir: %s', uri)
return False
try:
os.remove(self._abspath(path))
except EnvironmentError as e:
Expand All @@ -99,6 +103,9 @@ def delete(self, uri):

def get_items(self, uri):
path = translator.uri_to_path(uri)
if not self._is_in_basedir(path):
logger.debug('Ignoring path outside playlist dir: %s', uri)
return None
try:
with self._open(path, 'r') as fp:
items = translator.load_items(fp, self._base_dir)
Expand All @@ -109,6 +116,9 @@ def get_items(self, uri):

def lookup(self, uri):
path = translator.uri_to_path(uri)
if not self._is_in_basedir(path):
logger.debug('Ignoring path outside playlist dir: %s', uri)
return None
try:
with self._open(path, 'r') as fp:
items = translator.load_items(fp, self._base_dir)
Expand All @@ -123,6 +133,10 @@ def refresh(self):

def save(self, playlist):
path = translator.uri_to_path(playlist.uri)
if not self._is_in_basedir(path):
logger.debug(
'Ignoring path outside playlist dir: %s', playlist.uri)
return None
name = translator.name_from_path(path)
try:
with self._open(path, 'w') as fp:
Expand All @@ -140,13 +154,22 @@ def save(self, playlist):
def _abspath(self, path):
return os.path.join(self._playlists_dir, path)

def _is_in_basedir(self, local_path):
if not os.path.isabs(local_path):
local_path = os.path.join(self._playlists_dir, local_path)
return path.is_path_inside_base_dir(local_path, self._playlists_dir)

def _open(self, path, mode='r'):
if path.endswith(b'.m3u8'):
encoding = 'utf-8'
else:
encoding = self._default_encoding
if not os.path.isabs(path):
path = os.path.join(self._playlists_dir, path)
if not self._is_in_basedir(path):
raise Exception(
'Path (%s) is not inside playlist_dir (%s)'
% (path, self._playlists_dir))
if 'w' in mode:
return replace(path, mode, encoding=encoding, errors='replace')
else:
Expand Down
19 changes: 19 additions & 0 deletions tests/m3u/test_playlists.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ def test_deleted_playlist_is_removed(self):
self.assertTrue(success)
self.assertFalse(os.path.exists(path))

def test_delete_on_path_outside_playlist_dir_returns_none(self):
success = self.core.playlists.delete('m3u:///etc/passwd')

self.assertFalse(success)

def test_playlist_contents_is_written_to_disk(self):
track = Track(uri=generate_song(1))
playlist = self.core.playlists.create('test')
Expand Down Expand Up @@ -217,6 +222,11 @@ def test_lookup_finds_playlist_by_uri(self):

self.assertEqual(original_playlist, looked_up_playlist)

def test_lookup_on_path_outside_playlist_dir_returns_none(self):
result = self.core.playlists.lookup('m3u:///etc/passwd')

self.assertIsNone(result)

def test_refresh(self):
playlist = self.core.playlists.create('test')
self.assertEqual(playlist, self.core.playlists.lookup(playlist.uri))
Expand Down Expand Up @@ -252,6 +262,10 @@ def test_save_playlist_with_new_uri(self):
path = os.path.join(self.playlists_dir, b'test.m3u')
self.assertTrue(os.path.exists(path))

def test_save_on_path_outside_playlist_dir_returns_none(self):
result = self.core.playlists.save(Playlist(uri='m3u:///tmp/test.m3u'))
self.assertIsNone(result)

def test_playlist_with_unknown_track(self):
track = Track(uri='file:///dev/null')
playlist = self.core.playlists.create('test')
Expand Down Expand Up @@ -331,6 +345,11 @@ def test_get_items_of_unknown_playlist_returns_none(self):

self.assertIsNone(item_refs)

def test_get_items_from_file_outside_playlist_dir_returns_none(self):
item_refs = self.core.playlists.get_items('m3u:///etc/passwd')

self.assertIsNone(item_refs)


class M3UPlaylistsProviderBaseDirectoryTest(M3UPlaylistsProviderTest):

Expand Down

0 comments on commit 6ac759e

Please sign in to comment.