diff --git a/changedetectionio/processors/__init__.py b/changedetectionio/processors/__init__.py index 2dcc9730..adf08a18 100644 --- a/changedetectionio/processors/__init__.py +++ b/changedetectionio/processors/__init__.py @@ -33,8 +33,8 @@ class difference_detection_processor(): url = self.watch.link - # Protect against file://, file:/ access, check the real "link" without any meta "source:" etc prepended. - if re.search(r'^file:/', url.strip(), re.IGNORECASE): + # Protect against file:, file:/, file:// access, check the real "link" without any meta "source:" etc prepended. + if re.search(r'^file:', url.strip(), re.IGNORECASE): if not strtobool(os.getenv('ALLOW_FILE_URI', 'false')): raise Exception( "file:// type access is denied for security reasons." diff --git a/changedetectionio/tests/test_security.py b/changedetectionio/tests/test_security.py index 0dfbdcba..2ff4beec 100644 --- a/changedetectionio/tests/test_security.py +++ b/changedetectionio/tests/test_security.py @@ -1,9 +1,7 @@ import os from flask import url_for -from .util import set_original_response, set_modified_response, live_server_setup, wait_for_all_checks -import time - +from .util import live_server_setup, wait_for_all_checks from .. import strtobool @@ -61,15 +59,11 @@ def test_bad_access(client, live_server, measure_memory_usage): assert b'Watch protocol is not permitted by SAFE_PROTOCOL_REGEX' in res.data -def test_file_slashslash_access(client, live_server, measure_memory_usage): - #live_server_setup(live_server) - - test_file_path = os.path.abspath(__file__) +def _runner_test_various_file_slash(client, file_uri): - # file:// is permitted by default, but it will be caught by ALLOW_FILE_URI client.post( url_for("form_quick_watch_add"), - data={"url": f"file://{test_file_path}", "tags": ''}, + data={"url": file_uri, "tags": ''}, follow_redirects=True ) wait_for_all_checks(client) @@ -77,38 +71,25 @@ def test_file_slashslash_access(client, live_server, measure_memory_usage): # If it is enabled at test time if strtobool(os.getenv('ALLOW_FILE_URI', 'false')): - res = client.get( - url_for("preview_page", uuid="first"), - follow_redirects=True - ) - - assert b"test_file_slashslash_access" in res.data + # So it should permit it, but it should fall back to the 'requests' library giving an error + # (but means it gets passed to playwright etc) + assert b"URLs with hostname components are not permitted" in res.data + assert b"_runner_test_various_file_slash" in res.data # Can read this file OK else: # Default should be here assert b'file:// type access is denied for security reasons.' in res.data + res = client.get(url_for("form_delete", uuid="all"), follow_redirects=True) + assert b'Deleted' in res.data + def test_file_slash_access(client, live_server, measure_memory_usage): #live_server_setup(live_server) + # file: is permitted by default, but it will be caught by ALLOW_FILE_URI test_file_path = os.path.abspath(__file__) - - # file:// is permitted by default, but it will be caught by ALLOW_FILE_URI - client.post( - url_for("form_quick_watch_add"), - data={"url": f"file:/{test_file_path}", "tags": ''}, - follow_redirects=True - ) - wait_for_all_checks(client) - res = client.get(url_for("index")) - - # If it is enabled at test time - if strtobool(os.getenv('ALLOW_FILE_URI', 'false')): - # So it should permit it, but it should fall back to the 'requests' library giving an error - # (but means it gets passed to playwright etc) - assert b"URLs with hostname components are not permitted" in res.data - else: - # Default should be here - assert b'file:// type access is denied for security reasons.' in res.data + _runner_test_various_file_slash(client, file_uri=f"file://{test_file_path}") + _runner_test_various_file_slash(client, file_uri=f"file:/{test_file_path}") + _runner_test_various_file_slash(client, file_uri=f"file:{test_file_path}") # CVE-2024-56509 def test_xss(client, live_server, measure_memory_usage): #live_server_setup(live_server)