diff options
23 files changed, 232 insertions, 83 deletions
diff --git a/cli/onionshare_cli/common.py b/cli/onionshare_cli/common.py index 7ec31ec6..dd92eb0b 100644 --- a/cli/onionshare_cli/common.py +++ b/cli/onionshare_cli/common.py @@ -250,7 +250,7 @@ class Common: ) left_spaces = (43 - len(self.version) - 1) // 2 right_spaces = left_spaces - if left_spaces + len(self.version) + right_spaces < 43: + if left_spaces + len(self.version) + 1 + right_spaces < 43: right_spaces += 1 print( Back.MAGENTA diff --git a/cli/onionshare_cli/resources/templates/500.html b/cli/onionshare_cli/resources/templates/500.html new file mode 100644 index 00000000..9f6727d2 --- /dev/null +++ b/cli/onionshare_cli/resources/templates/500.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html> + +<head> + <title>OnionShare: An error occurred</title> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <link href="{{ static_url_path }}/img/favicon.ico" rel="icon" type="image/x-icon"> + <link rel="stylesheet" rel="subresource" type="text/css" href="{{ static_url_path }}/css/style.css" media="all"> +</head> + +<body> + <div class="info-wrapper"> + <div class="info"> + <p><img class="logo" src="{{ static_url_path }}/img/logo_large.png" title="OnionShare"></p> + <p class="info-header">Sorry, an unexpected error seems to have occurred, and your request didn't succeed.</p> + </div> + </div> +</body> + +</html> diff --git a/cli/onionshare_cli/web/chat_mode.py b/cli/onionshare_cli/web/chat_mode.py index e9b573dd..f6dc2d1a 100644 --- a/cli/onionshare_cli/web/chat_mode.py +++ b/cli/onionshare_cli/web/chat_mode.py @@ -39,6 +39,12 @@ class ChatModeWeb: # This tracks the history id self.cur_history_id = 0 + # Whether or not we can send REQUEST_INDIVIDUAL_FILE_STARTED + # and maybe other events when requests come in to this mode + # Chat mode has no concept of individual file requests that + # turn into history widgets in the GUI, so set it to False + self.supports_file_requests = False + self.define_routes() def define_routes(self): @@ -46,7 +52,7 @@ class ChatModeWeb: The web app routes for chatting """ - @self.web.app.route("/") + @self.web.app.route("/", methods=["GET"], provide_automatic_options=False) def index(): history_id = self.cur_history_id self.cur_history_id += 1 @@ -72,7 +78,7 @@ class ChatModeWeb: ) return self.web.add_security_headers(r) - @self.web.app.route("/update-session-username", methods=["POST"]) + @self.web.app.route("/update-session-username", methods=["POST"], provide_automatic_options=False) def update_session_username(): history_id = self.cur_history_id data = request.get_json() diff --git a/cli/onionshare_cli/web/receive_mode.py b/cli/onionshare_cli/web/receive_mode.py index f5aae296..76abb0a8 100644 --- a/cli/onionshare_cli/web/receive_mode.py +++ b/cli/onionshare_cli/web/receive_mode.py @@ -64,6 +64,10 @@ class ReceiveModeWeb: # This tracks the history id self.cur_history_id = 0 + # Whether or not we can send REQUEST_INDIVIDUAL_FILE_STARTED + # and maybe other events when requests come in to this mode + self.supports_file_requests = True + self.define_routes() def define_routes(self): @@ -71,7 +75,7 @@ class ReceiveModeWeb: The web app routes for receiving files """ - @self.web.app.route("/") + @self.web.app.route("/", methods=["GET"], provide_automatic_options=False) def index(): history_id = self.cur_history_id self.cur_history_id += 1 @@ -93,7 +97,7 @@ class ReceiveModeWeb: ) return self.web.add_security_headers(r) - @self.web.app.route("/upload", methods=["POST"]) + @self.web.app.route("/upload", methods=["POST"], provide_automatic_options=False) def upload(ajax=False): """ Handle the upload files POST request, though at this point, the files have @@ -225,7 +229,7 @@ class ReceiveModeWeb: ) return self.web.add_security_headers(r) - @self.web.app.route("/upload-ajax", methods=["POST"]) + @self.web.app.route("/upload-ajax", methods=["POST"], provide_automatic_options=False) def upload_ajax_public(): if not self.can_upload: return self.web.error403() diff --git a/cli/onionshare_cli/web/send_base_mode.py b/cli/onionshare_cli/web/send_base_mode.py index 742f6f75..e448d2dd 100644 --- a/cli/onionshare_cli/web/send_base_mode.py +++ b/cli/onionshare_cli/web/send_base_mode.py @@ -52,6 +52,10 @@ class SendBaseModeWeb: # This tracks the history id self.cur_history_id = 0 + # Whether or not we can send REQUEST_INDIVIDUAL_FILE_STARTED + # and maybe other events when requests come in to this mode + self.supports_file_requests = True + self.define_routes() self.init() @@ -208,10 +212,6 @@ class SendBaseModeWeb: history_id = self.cur_history_id self.cur_history_id += 1 - # Only GET requests are allowed, any other method should fail - if request.method != "GET": - return self.web.error405(history_id) - self.web.add_request( self.web.REQUEST_INDIVIDUAL_FILE_STARTED, path, diff --git a/cli/onionshare_cli/web/share_mode.py b/cli/onionshare_cli/web/share_mode.py index 95aec1ba..51ddd674 100644 --- a/cli/onionshare_cli/web/share_mode.py +++ b/cli/onionshare_cli/web/share_mode.py @@ -134,8 +134,8 @@ class ShareModeWeb(SendBaseModeWeb): The web app routes for sharing files """ - @self.web.app.route("/", defaults={"path": ""}) - @self.web.app.route("/<path:path>") + @self.web.app.route("/", defaults={"path": ""}, methods=["GET"], provide_automatic_options=False) + @self.web.app.route("/<path:path>", methods=["GET"], provide_automatic_options=False) def index(path): """ Render the template for the onionshare landing page. @@ -160,7 +160,7 @@ class ShareModeWeb(SendBaseModeWeb): return self.render_logic(path) - @self.web.app.route("/download") + @self.web.app.route("/download", methods=["GET"], provide_automatic_options=False) def download(): """ Download the zip file. diff --git a/cli/onionshare_cli/web/web.py b/cli/onionshare_cli/web/web.py index d88a7e4e..56e307b4 100644 --- a/cli/onionshare_cli/web/web.py +++ b/cli/onionshare_cli/web/web.py @@ -229,6 +229,20 @@ class Web: mode.cur_history_id += 1 return self.error404(history_id) + @self.app.errorhandler(405) + def method_not_allowed(e): + mode = self.get_mode() + history_id = mode.cur_history_id + mode.cur_history_id += 1 + return self.error405(history_id) + + @self.app.errorhandler(500) + def method_not_allowed(e): + mode = self.get_mode() + history_id = mode.cur_history_id + mode.cur_history_id += 1 + return self.error500(history_id) + @self.app.route("/<password_candidate>/shutdown") def shutdown(password_candidate): """ @@ -280,11 +294,13 @@ class Web: return self.add_security_headers(r) def error404(self, history_id): - self.add_request( - self.REQUEST_INDIVIDUAL_FILE_STARTED, - request.path, - {"id": history_id, "status_code": 404}, - ) + mode = self.get_mode() + if mode.supports_file_requests: + self.add_request( + self.REQUEST_INDIVIDUAL_FILE_STARTED, + request.path, + {"id": history_id, "status_code": 404}, + ) self.add_request(Web.REQUEST_OTHER, request.path) r = make_response( @@ -293,11 +309,13 @@ class Web: return self.add_security_headers(r) def error405(self, history_id): - self.add_request( - self.REQUEST_INDIVIDUAL_FILE_STARTED, - request.path, - {"id": history_id, "status_code": 405}, - ) + mode = self.get_mode() + if mode.supports_file_requests: + self.add_request( + self.REQUEST_INDIVIDUAL_FILE_STARTED, + request.path, + {"id": history_id, "status_code": 405}, + ) self.add_request(Web.REQUEST_OTHER, request.path) r = make_response( @@ -305,6 +323,21 @@ class Web: ) return self.add_security_headers(r) + def error500(self, history_id): + mode = self.get_mode() + if mode.supports_file_requests: + self.add_request( + self.REQUEST_INDIVIDUAL_FILE_STARTED, + request.path, + {"id": history_id, "status_code": 500}, + ) + + self.add_request(Web.REQUEST_OTHER, request.path) + r = make_response( + render_template("500.html", static_url_path=self.static_url_path), 500 + ) + return self.add_security_headers(r) + def add_security_headers(self, r): """ Add security headers to a request diff --git a/cli/onionshare_cli/web/website_mode.py b/cli/onionshare_cli/web/website_mode.py index 6badd399..5ab1b184 100644 --- a/cli/onionshare_cli/web/website_mode.py +++ b/cli/onionshare_cli/web/website_mode.py @@ -37,8 +37,8 @@ class WebsiteModeWeb(SendBaseModeWeb): The web app routes for sharing a website """ - @self.web.app.route("/", defaults={"path": ""}) - @self.web.app.route("/<path:path>") + @self.web.app.route("/", defaults={"path": ""}, methods=["GET"], provide_automatic_options=False) + @self.web.app.route("/<path:path>", methods=["GET"], provide_automatic_options=False) def path_public(path): return path_logic(path) diff --git a/desktop/scripts/get-tor-osx.py b/desktop/scripts/get-tor-osx.py index 310acc27..f53174b2 100755 --- a/desktop/scripts/get-tor-osx.py +++ b/desktop/scripts/get-tor-osx.py @@ -34,7 +34,7 @@ import requests def main(): - dmg_url = "https://archive.torproject.org/tor-package-archive/torbrowser/10.0.16/TorBrowser-10.0.16-osx64_en-US.dmg" + dmg_url = "https://www.torproject.org/dist/torbrowser/10.0.16/TorBrowser-10.0.16-osx64_en-US.dmg" dmg_filename = "TorBrowser-10.0.16-osx64_en-US.dmg" expected_dmg_sha256 = ( "95bf37d642bd05e9ae4337c5ab9706990bbd98cc885e25ee8ae81b07c7653f0a" diff --git a/desktop/scripts/get-tor-windows.py b/desktop/scripts/get-tor-windows.py index a9126e9d..84a3a205 100644 --- a/desktop/scripts/get-tor-windows.py +++ b/desktop/scripts/get-tor-windows.py @@ -33,7 +33,7 @@ import requests def main(): - exe_url = "https://archive.torproject.org/tor-package-archive/torbrowser/10.0.16/torbrowser-install-10.0.16_en-US.exe" + exe_url = "https://www.torproject.org/dist/torbrowser/10.0.16/torbrowser-install-10.0.16_en-US.exe" exe_filename = "torbrowser-install-10.0.16_en-US.exe" expected_exe_sha256 = ( "1f93d756b4aee1b2df7d85c8d58b626b0d38d89c974c0a02f324ff51f5b23ee1" diff --git a/desktop/src/onionshare/gui_common.py b/desktop/src/onionshare/gui_common.py index 1a44a128..441aff25 100644 --- a/desktop/src/onionshare/gui_common.py +++ b/desktop/src/onionshare/gui_common.py @@ -84,10 +84,16 @@ class GuiCommon: header_color = "#4E064F" # purple in light title_color = "#333333" # dark gray color in main window stop_button_color = "#d0011b" # red button color for stopping server + new_tab_button_background = "#ffffff" + new_tab_button_border = "#efeff0" + new_tab_button_text_color = "#4e0d4e" if color_mode == "dark": header_color = "#F2F2F2" title_color = "#F2F2F2" stop_button_color = "#C32F2F" + new_tab_button_background = "#5F5F5F" + new_tab_button_border = "#878787" + new_tab_button_text_color = "#FFFFFF" return { # OnionShareGui styles @@ -261,11 +267,17 @@ class GuiCommon: """, "new_tab_button_text": """ QLabel { - border: 1px solid #efeff0; + border: 1px solid """ + + new_tab_button_border + + """; border-radius: 4px; - background-color: #ffffff; + background-color: """ + + new_tab_button_background + + """; text-align: center; - color: #4e0d4e; + color: """ + + new_tab_button_text_color + + """; } """, "new_tab_title_text": """ diff --git a/desktop/src/onionshare/tab/mode/chat_mode/__init__.py b/desktop/src/onionshare/tab/mode/chat_mode/__init__.py index 7f32aebb..fe3e69f1 100644 --- a/desktop/src/onionshare/tab/mode/chat_mode/__init__.py +++ b/desktop/src/onionshare/tab/mode/chat_mode/__init__.py @@ -24,7 +24,7 @@ from onionshare_cli.web import Web from .. import Mode from .... import strings -from ....widgets import MinimumWidthWidget +from ....widgets import MinimumSizeWidget from ....gui_common import GuiCommon @@ -82,17 +82,16 @@ class ChatMode(Mode): # Top bar top_bar_layout = QtWidgets.QHBoxLayout() - top_bar_layout.addStretch() + # Add space at the top, same height as the toggle history bar in other modes + top_bar_layout.addWidget(MinimumSizeWidget(0, 30)) # Main layout self.main_layout = QtWidgets.QVBoxLayout() self.main_layout.addLayout(top_bar_layout) - self.main_layout.addStretch() self.main_layout.addWidget(header_label) - self.main_layout.addWidget(self.primary_action) + self.main_layout.addWidget(self.primary_action, stretch=1) self.main_layout.addWidget(self.server_status) - self.main_layout.addStretch() - self.main_layout.addWidget(MinimumWidthWidget(700)) + self.main_layout.addWidget(MinimumSizeWidget(700, 0)) # Column layout self.column_layout = QtWidgets.QHBoxLayout() diff --git a/desktop/src/onionshare/tab/mode/file_selection.py b/desktop/src/onionshare/tab/mode/file_selection.py index e9604ec5..302f07b9 100644 --- a/desktop/src/onionshare/tab/mode/file_selection.py +++ b/desktop/src/onionshare/tab/mode/file_selection.py @@ -72,8 +72,8 @@ class DropHereWidget(QtWidgets.QWidget): def resize(self, w, h): self.setGeometry(0, 0, w, h) self.image_label.setGeometry(0, 0, w, h - 100) - self.header_label.setGeometry(0, 310, w, h - 380) - self.text_label.setGeometry(0, 360, w, h - 400) + self.header_label.setGeometry(0, 290, w, h - 360) + self.text_label.setGeometry(0, 340, w, h - 380) class DropCountLabel(QtWidgets.QLabel): diff --git a/desktop/src/onionshare/tab/mode/mode_settings_widget.py b/desktop/src/onionshare/tab/mode/mode_settings_widget.py index 98a6a01a..9f55dbaf 100644 --- a/desktop/src/onionshare/tab/mode/mode_settings_widget.py +++ b/desktop/src/onionshare/tab/mode/mode_settings_widget.py @@ -23,7 +23,7 @@ from PySide2 import QtCore, QtWidgets from ... import strings -class ModeSettingsWidget(QtWidgets.QWidget): +class ModeSettingsWidget(QtWidgets.QScrollArea): """ All of the common settings for each mode are in this widget """ @@ -177,7 +177,15 @@ class ModeSettingsWidget(QtWidgets.QWidget): layout.addWidget(self.public_checkbox) layout.addWidget(self.advanced_widget) layout.addWidget(self.toggle_advanced_button) - self.setLayout(layout) + layout.addStretch() + main_widget = QtWidgets.QWidget() + main_widget.setLayout(layout) + + self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.setWidgetResizable(True) + self.setFrameShape(QtWidgets.QFrame.NoFrame) + self.setWidget(main_widget) self.update_ui() diff --git a/desktop/src/onionshare/tab/mode/receive_mode/__init__.py b/desktop/src/onionshare/tab/mode/receive_mode/__init__.py index 4dd2980c..d07b5ffc 100644 --- a/desktop/src/onionshare/tab/mode/receive_mode/__init__.py +++ b/desktop/src/onionshare/tab/mode/receive_mode/__init__.py @@ -26,7 +26,7 @@ from onionshare_cli.web import Web from ..history import History, ToggleHistory, ReceiveHistoryItem from .. import Mode from .... import strings -from ....widgets import MinimumWidthWidget, Alert +from ....widgets import MinimumSizeWidget, Alert from ....gui_common import GuiCommon @@ -182,8 +182,8 @@ class ReceiveMode(Mode): self.main_layout = QtWidgets.QVBoxLayout() self.main_layout.addWidget(header_label) self.main_layout.addWidget(receive_warning) - self.main_layout.addWidget(self.primary_action) - self.main_layout.addWidget(MinimumWidthWidget(525)) + self.main_layout.addWidget(self.primary_action, stretch=1) + self.main_layout.addWidget(MinimumSizeWidget(525, 0)) # Row layout content_row = QtWidgets.QHBoxLayout() @@ -191,10 +191,8 @@ class ReceiveMode(Mode): content_row.addWidget(self.image) row_layout = QtWidgets.QVBoxLayout() row_layout.addLayout(top_bar_layout) - row_layout.addStretch() - row_layout.addLayout(content_row) + row_layout.addLayout(content_row, stretch=1) row_layout.addWidget(self.server_status) - row_layout.addStretch() # Column layout self.column_layout = QtWidgets.QHBoxLayout() diff --git a/desktop/src/onionshare/tab/mode/share_mode/__init__.py b/desktop/src/onionshare/tab/mode/share_mode/__init__.py index 676d34af..4056d92e 100644 --- a/desktop/src/onionshare/tab/mode/share_mode/__init__.py +++ b/desktop/src/onionshare/tab/mode/share_mode/__init__.py @@ -29,7 +29,7 @@ from .. import Mode from ..file_selection import FileSelection from ..history import History, ToggleHistory, ShareHistoryItem from .... import strings -from ....widgets import MinimumWidthWidget +from ....widgets import MinimumSizeWidget from ....gui_common import GuiCommon @@ -160,9 +160,9 @@ class ShareMode(Mode): self.main_layout = QtWidgets.QVBoxLayout() self.main_layout.addLayout(top_bar_layout) self.main_layout.addLayout(self.file_selection) - self.main_layout.addWidget(self.primary_action) + self.main_layout.addWidget(self.primary_action, stretch=1) self.main_layout.addWidget(self.server_status) - self.main_layout.addWidget(MinimumWidthWidget(700)) + self.main_layout.addWidget(MinimumSizeWidget(700, 0)) # Column layout self.column_layout = QtWidgets.QHBoxLayout() diff --git a/desktop/src/onionshare/tab/mode/website_mode/__init__.py b/desktop/src/onionshare/tab/mode/website_mode/__init__.py index 10caff51..577ea28e 100644 --- a/desktop/src/onionshare/tab/mode/website_mode/__init__.py +++ b/desktop/src/onionshare/tab/mode/website_mode/__init__.py @@ -29,7 +29,7 @@ from .. import Mode from ..file_selection import FileSelection from ..history import History, ToggleHistory from .... import strings -from ....widgets import MinimumWidthWidget +from ....widgets import MinimumSizeWidget from ....gui_common import GuiCommon @@ -158,9 +158,9 @@ class WebsiteMode(Mode): self.main_layout = QtWidgets.QVBoxLayout() self.main_layout.addLayout(top_bar_layout) self.main_layout.addLayout(self.file_selection) - self.main_layout.addWidget(self.primary_action) + self.main_layout.addWidget(self.primary_action, stretch=1) self.main_layout.addWidget(self.server_status) - self.main_layout.addWidget(MinimumWidthWidget(700)) + self.main_layout.addWidget(MinimumSizeWidget(700, 0)) # Column layout self.column_layout = QtWidgets.QHBoxLayout() diff --git a/desktop/src/onionshare/widgets.py b/desktop/src/onionshare/widgets.py index a1880a2e..c239d03a 100644 --- a/desktop/src/onionshare/widgets.py +++ b/desktop/src/onionshare/widgets.py @@ -84,14 +84,15 @@ class AddFileDialog(QtWidgets.QFileDialog): QtWidgets.QDialog.accept(self) -class MinimumWidthWidget(QtWidgets.QWidget): +class MinimumSizeWidget(QtWidgets.QWidget): """ - An empty widget with a minimum width, just to force layouts to behave + An empty widget with a minimum width and height, just to force layouts to behave """ - def __init__(self, width): - super(MinimumWidthWidget, self).__init__() + def __init__(self, width, height): + super(MinimumSizeWidget, self).__init__() self.setMinimumWidth(width) + self.setMinimumHeight(height) class Image(qrcode.image.base.BaseImage): diff --git a/desktop/tests/gui_base_test.py b/desktop/tests/gui_base_test.py index 3a38ff8e..acaa9739 100644 --- a/desktop/tests/gui_base_test.py +++ b/desktop/tests/gui_base_test.py @@ -465,6 +465,20 @@ class GuiBaseTest(unittest.TestCase): # We should have timed out now self.assertEqual(tab.get_mode().server_status.status, 0) + def hit_405(self, url, expected_resp, data = {}, methods = [] ): + """Test various HTTP methods and the response""" + for method in methods: + if method == "put": + r = requests.put(url, data = data) + if method == "post": + r = requests.post(url, data = data) + if method == "delete": + r = requests.delete(url) + if method == "options": + r = requests.options(url) + self.assertTrue(expected_resp in r.text) + self.assertFalse('Werkzeug' in r.headers) + # Grouped tests follow from here def run_all_common_setup_tests(self): diff --git a/desktop/tests/test_gui_chat.py b/desktop/tests/test_gui_chat.py index 08c619c6..15ecaa44 100644 --- a/desktop/tests/test_gui_chat.py +++ b/desktop/tests/test_gui_chat.py @@ -30,7 +30,7 @@ class TestChat(GuiBaseTest): def change_username(self, tab): """Test that we can change our username""" url = f"http://127.0.0.1:{tab.app.port}/update-session-username" - data = {"username":"oniontest"} + data = {"username": "oniontest"} if tab.settings.get("general", "public"): r = requests.post(url, json=data) else: @@ -47,28 +47,7 @@ class TestChat(GuiBaseTest): self.assertTrue(jsonResponse["success"]) self.assertEqual(jsonResponse["username"], "oniontest") - def change_username_too_long(self, tab): - """Test that we can't set our username to something 128 chars or longer""" - url = f"http://127.0.0.1:{tab.app.port}/update-session-username" - bad_username = "sduBB9yEMkyQpwkMM4A9nUbQwNUbPU2PQuJYN26zCQ4inELpB76J5i5oRUnD3ESVaE9NNE8puAtBj2DiqDaZdVqhV8MonyxSSGHRv87YgM5dzwBYPBxttoQSKZAUkFjo" - data = {"username":bad_username} - if tab.settings.get("general", "public"): - r = requests.post(url, json=data) - else: - r = requests.post( - url, - json=data, - auth=requests.auth.HTTPBasicAuth( - "onionshare", tab.get_mode().server_status.web.password - ), - ) - - QtTest.QTest.qWait(500, self.gui.qtapp) - jsonResponse = r.json() - self.assertFalse(jsonResponse["success"]) - self.assertNotEqual(jsonResponse["username"], bad_username) - - def run_all_chat_mode_tests(self, tab): + def run_all_chat_mode_started_tests(self, tab): """Tests in chat mode after starting a chat""" self.server_working_on_start_button_pressed(tab) self.server_status_indicator_says_starting(tab) @@ -79,9 +58,9 @@ class TestChat(GuiBaseTest): self.have_copy_url_button(tab) self.have_show_qr_code_button(tab) self.server_status_indicator_says_started(tab) - self.view_chat(tab) - self.change_username(tab) - self.change_username_too_long(tab) + + def run_all_chat_mode_stopping_tests(self, tab): + """Tests stopping a chat""" self.server_is_stopped(tab) self.web_server_is_stopped(tab) self.server_status_indicator_says_closed(tab) @@ -93,5 +72,27 @@ class TestChat(GuiBaseTest): Test chat mode """ tab = self.new_chat_tab() - self.run_all_chat_mode_tests(tab) + self.run_all_chat_mode_started_tests(tab) + self.view_chat(tab) + self.change_username(tab) + self.run_all_chat_mode_stopping_tests(tab) + self.close_all_tabs() + + def test_405_page_returned_for_invalid_methods(self): + """ + Our custom 405 page should return for invalid methods + """ + tab = self.new_chat_tab() + + tab.get_mode().mode_settings_widget.public_checkbox.click() + + self.run_all_chat_mode_started_tests(tab) + url = f"http://127.0.0.1:{tab.app.port}/" + self.hit_405( + url, + expected_resp="OnionShare: 405 Method Not Allowed", + data={"foo": "bar"}, + methods=["put", "post", "delete", "options"], + ) + self.run_all_chat_mode_stopping_tests(tab) self.close_all_tabs() diff --git a/desktop/tests/test_gui_receive.py b/desktop/tests/test_gui_receive.py index 6e14ae67..b523b0fa 100644 --- a/desktop/tests/test_gui_receive.py +++ b/desktop/tests/test_gui_receive.py @@ -286,3 +286,22 @@ class TestReceive(GuiBaseTest): self.run_all_upload_non_writable_dir_tests(tab) self.close_all_tabs() + + def test_405_page_returned_for_invalid_methods(self): + """ + Our custom 405 page should return for invalid methods + """ + tab = self.new_receive_tab() + + tab.get_mode().mode_settings_widget.public_checkbox.click() + + self.run_all_common_setup_tests() + self.run_all_receive_mode_setup_tests(tab) + self.upload_file(tab, self.tmpfile_test, "test.txt") + url = f"http://127.0.0.1:{tab.app.port}/" + self.hit_405(url, expected_resp="OnionShare: 405 Method Not Allowed", data = {'foo':'bar'}, methods = ["put", "post", "delete", "options"]) + + self.server_is_stopped(tab) + self.web_server_is_stopped(tab) + self.server_status_indicator_says_closed(tab) + self.close_all_tabs() diff --git a/desktop/tests/test_gui_share.py b/desktop/tests/test_gui_share.py index 380d63f6..531e456f 100644 --- a/desktop/tests/test_gui_share.py +++ b/desktop/tests/test_gui_share.py @@ -608,3 +608,20 @@ class TestShare(GuiBaseTest): self.hit_401(tab) self.close_all_tabs() + + def test_405_page_returned_for_invalid_methods(self): + """ + Our custom 405 page should return for invalid methods + """ + tab = self.new_share_tab() + + tab.get_mode().autostop_sharing_checkbox.click() + tab.get_mode().mode_settings_widget.public_checkbox.click() + + self.run_all_common_setup_tests() + self.run_all_share_mode_setup_tests(tab) + self.run_all_share_mode_started_tests(tab) + url = f"http://127.0.0.1:{tab.app.port}/" + self.hit_405(url, expected_resp="OnionShare: 405 Method Not Allowed", data = {'foo':'bar'}, methods = ["put", "post", "delete", "options"]) + self.history_widgets_present(tab) + self.close_all_tabs() diff --git a/desktop/tests/test_gui_website.py b/desktop/tests/test_gui_website.py index a838cb96..f526756a 100644 --- a/desktop/tests/test_gui_website.py +++ b/desktop/tests/test_gui_website.py @@ -99,3 +99,19 @@ class TestWebsite(GuiBaseTest): tab.get_mode().disable_csp_checkbox.click() self.run_all_website_mode_download_tests(tab) self.close_all_tabs() + + def test_405_page_returned_for_invalid_methods(self): + """ + Our custom 405 page should return for invalid methods + """ + tab = self.new_website_tab() + + tab.get_mode().mode_settings_widget.public_checkbox.click() + + self.run_all_common_setup_tests() + self.run_all_website_mode_setup_tests(tab) + self.run_all_website_mode_started_tests(tab) + url = f"http://127.0.0.1:{tab.app.port}/" + self.hit_405(url, expected_resp="OnionShare: 405 Method Not Allowed", data = {'foo':'bar'}, methods = ["put", "post", "delete", "options"]) + + self.close_all_tabs() |