summaryrefslogtreecommitdiffstats
path: root/deluge/ui
diff options
context:
space:
mode:
Diffstat (limited to 'deluge/ui')
-rw-r--r--deluge/ui/gtk3/new_release_dialog.py2
-rw-r--r--deluge/ui/web/auth.py10
-rw-r--r--deluge/ui/web/js/deluge-all/AboutWindow.js2
-rw-r--r--deluge/ui/web/server.py40
4 files changed, 41 insertions, 13 deletions
diff --git a/deluge/ui/gtk3/new_release_dialog.py b/deluge/ui/gtk3/new_release_dialog.py
index a635bd2..7246994 100644
--- a/deluge/ui/gtk3/new_release_dialog.py
+++ b/deluge/ui/gtk3/new_release_dialog.py
@@ -61,7 +61,7 @@ class NewReleaseDialog:
self.dialog.show()
def _on_button_goto_downloads(self, widget):
- deluge.common.open_url_in_browser('http://deluge-torrent.org')
+ deluge.common.open_url_in_browser('https://deluge-torrent.org')
self.config['show_new_releases'] = not self.chk_not_show_dialog.get_active()
self.dialog.destroy()
diff --git a/deluge/ui/web/auth.py b/deluge/ui/web/auth.py
index eacbbf5..a0c3a91 100644
--- a/deluge/ui/web/auth.py
+++ b/deluge/ui/web/auth.py
@@ -100,9 +100,9 @@ class Auth(JSONComponent):
checksum = str(make_checksum(session_id))
request.addCookie(
- b'_session_id',
+ '_session_id',
session_id + checksum,
- path=request.base + b'json',
+ path=request.base,
expires=expires_str,
)
@@ -160,10 +160,10 @@ class Auth(JSONComponent):
_session_id = request.getCookie(b'_session_id')
request.addCookie(
- b'_session_id',
+ '_session_id',
_session_id,
- path=request.base + b'json',
- expires=expires_str.encode('utf8'),
+ path=request.base,
+ expires=expires_str,
)
if method:
diff --git a/deluge/ui/web/js/deluge-all/AboutWindow.js b/deluge/ui/web/js/deluge-all/AboutWindow.js
index cfae7a8..4ab6599 100644
--- a/deluge/ui/web/js/deluge-all/AboutWindow.js
+++ b/deluge/ui/web/js/deluge-all/AboutWindow.js
@@ -99,7 +99,7 @@ Deluge.about.AboutWindow = Ext.extend(Ext.Window, {
{
xtype: 'label',
style: 'padding-top: 10px; font-size: 10px;',
- text: _('Copyright 2007-2018 Deluge Team'),
+ text: _('Copyright 2007-2025 Deluge Team'),
},
{
xtype: 'label',
diff --git a/deluge/ui/web/server.py b/deluge/ui/web/server.py
index 5aba8aa..e93d4f6 100644
--- a/deluge/ui/web/server.py
+++ b/deluge/ui/web/server.py
@@ -20,8 +20,9 @@ from twisted.web import http, resource, server, static
from twisted.web.resource import EncodingResourceWrapper
from deluge import common, component, configmanager
-from deluge.common import is_ipv6
+from deluge.common import AUTH_LEVEL_DEFAULT, is_ipv6
from deluge.crypto_utils import check_ssl_keys, get_context_factory
+from deluge.error import NotAuthorizedError
from deluge.i18n import set_language, setup_translation
from deluge.ui.tracker_icons import TrackerIcons
from deluge.ui.web.auth import Auth
@@ -182,8 +183,9 @@ class Tracker(resource.Resource):
except KeyError:
self.tracker_icons = TrackerIcons()
- def getChild(self, path, request): # NOQA: N802
- request.tracker_name = path
+ def getChild(self, path: bytes, request): # NOQA: N802
+ # Ensure tracker name only to prevent path traversal.
+ request.tracker_name = os.path.basename(path.decode())
return self
def on_got_icon(self, icon, request):
@@ -200,8 +202,22 @@ class Tracker(resource.Resource):
request.finish()
def render(self, request):
- d = self.tracker_icons.fetch(request.tracker_name.decode())
- d.addCallback(self.on_got_icon, request)
+ tracker_icon = self.tracker_icons.get(request.tracker_name)
+ if tracker_icon:
+ self.on_got_icon(tracker_icon, request)
+ return server.NOT_DONE_YET
+
+ # Tracker endpoint is secured to avoid exploits when downloading icons.
+ try:
+ component.get('Auth').check_request(request, level=AUTH_LEVEL_DEFAULT)
+ except NotAuthorizedError:
+ log.warning('Auth required to download tracker icon.')
+ request.setResponseCode(http.UNAUTHORIZED)
+ request.finish()
+ else:
+ self.tracker_icons.fetch(request.tracker_name).addCallback(
+ self.on_got_icon, request
+ )
return server.NOT_DONE_YET
@@ -211,7 +227,9 @@ class Flag(resource.Resource):
return self
def render(self, request):
- flag = request.country.decode().lower() + '.png'
+ country = request.country.decode().lower()
+ # Ensure filename only, to prevent path traversal.
+ flag = os.path.basename(f'{country}.png')
path = ('ui', 'data', 'pixmaps', 'flags', flag)
filename = common.resource_filename('deluge', os.path.join(*path))
if os.path.exists(filename):
@@ -433,8 +451,18 @@ class ScriptResource(resource.Resource, component.Component):
filepath = filepath[0]
path = filepath + lookup_path[len(pattern) :]
+ path = os.path.abspath(path)
+
+ if not os.path.commonpath([path, filepath]) == filepath:
+ log.warning(
+ 'Script path %s traverses out of common dir %s',
+ path,
+ filepath,
+ )
+ continue
if not os.path.isfile(path):
+ log.warning('Unable to serve script which does not exist: %s', path)
continue
log.debug('Serving path: %s', path)