diff --git a/config.py b/config.py index 167c6a5..8db88df 100644 --- a/config.py +++ b/config.py @@ -255,3 +255,18 @@ TRUSTED_DEFAULT_URLS = [ # This MUST BE THE LAST! if os.path.isfile(os.path.join(BASE_DIR, "config_local.py")): from config_local import * # noqa # flake8: noqa # NOQA # pragma: no cover + +def map_legacy_config(trusted_url): + """ + For backward compability with the legacy configuration + for trusting URLs. Adapts them to fit the new config. + """ + if isinstance(trusted_url, str): + return { + "url_prefix": trusted_url + } + + return trusted_url + +# Backward compability for legacy behavior +TRUSTED_DEFAULT_URLS = list(map(map_legacy_config, TRUSTED_DEFAULT_URLS)) diff --git a/ivatar/test_utils.py b/ivatar/test_utils.py index 909a512..5ecf362 100644 --- a/ivatar/test_utils.py +++ b/ivatar/test_utils.py @@ -47,7 +47,7 @@ class Tester(TestCase): self.assertEqual(openid_variations(openid3)[3], openid3) def test_is_trusted_url(self): - test1 = is_trusted_url("https://gravatar.com/avatar/63a75a80e6b1f4adfdb04c1ca02e596c", [ + test_gravatar_true = is_trusted_url("https://gravatar.com/avatar/63a75a80e6b1f4adfdb04c1ca02e596c", [ { "schemes": [ "http", @@ -57,9 +57,9 @@ class Tester(TestCase): "path_prefix": "/avatar/" } ]) - self.assertTrue(test1) + self.assertTrue(test_gravatar_true) - test2 = is_trusted_url("https://gravatar.com.example.org/avatar/63a75a80e6b1f4adfdb04c1ca02e596c", [ + test_gravatar_false = is_trusted_url("https://gravatar.com.example.org/avatar/63a75a80e6b1f4adfdb04c1ca02e596c", [ { "schemes": [ "http", @@ -69,10 +69,9 @@ class Tester(TestCase): "path_prefix": "/avatar/" } ]) - self.assertFalse(test2) + self.assertFalse(test_gravatar_false) - # Test against open redirect with valid URL in query params - test3 = is_trusted_url("https://github.com/SethFalco/?boop=https://secure.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50", [ + test_open_redirect = is_trusted_url("https://github.com/SethFalco/?boop=https://secure.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50", [ { "schemes": [ "http", @@ -82,9 +81,9 @@ class Tester(TestCase): "path_prefix": "/avatar/" } ]) - self.assertFalse(test3) + self.assertFalse(test_open_redirect) - test4 = is_trusted_url("https://ui-avatars.com/api/blah", [ + test_multiple_filters = is_trusted_url("https://ui-avatars.com/api/blah", [ { "schemes": [ "https" @@ -101,4 +100,18 @@ class Tester(TestCase): "path_prefix": "/avatar/" } ]) - self.assertTrue(test4) + self.assertTrue(test_multiple_filters) + + test_url_prefix_true = is_trusted_url("https://ui-avatars.com/api/blah", [ + { + "url_prefix": "https://ui-avatars.com/api/" + } + ]) + self.assertTrue(test_url_prefix_true) + + test_url_prefix_false = is_trusted_url("https://ui-avatars.com/api/blah", [ + { + "url_prefix": "https://gravatar.com/avatar/" + } + ]) + self.assertFalse(test_url_prefix_false) diff --git a/ivatar/utils.py b/ivatar/utils.py index dc91b46..aea0920 100644 --- a/ivatar/utils.py +++ b/ivatar/utils.py @@ -149,6 +149,12 @@ def is_trusted_url(url, url_filters): if not path.startswith(path_prefix): continue + if "url_prefix" in filter: + url_prefix = filter["url_prefix"] + + if not url.startswith(url_prefix): + continue + return True return False diff --git a/ivatar/views.py b/ivatar/views.py index e5e24cd..2569e61 100644 --- a/ivatar/views.py +++ b/ivatar/views.py @@ -141,18 +141,19 @@ class AvatarImageView(TemplateView): if "default" in request.GET: default = request.GET["default"] - # Check if default starts with an URL scheme and if it does, - # check if it's trusted - # Check for :// (schema) - if default is not None and default.find("://") > 0: - # Check if it's trusted, if not, reset to None - trusted_url = is_trusted_url(default, TRUSTED_DEFAULT_URLS) - - if not trusted_url: - print( - "Default URL is not in trusted URLs: '%s' ; Kicking it!" % default - ) + if default is not None: + if TRUSTED_DEFAULT_URLS is None: + print("Query parameter `default` is disabled.") default = None + elif default.find("://") > 0: + # Check if it's trusted, if not, reset to None + trusted_url = is_trusted_url(default, TRUSTED_DEFAULT_URLS) + + if not trusted_url: + print( + "Default URL is not in trusted URLs: '%s' ; Kicking it!" % default + ) + default = None if "f" in request.GET: if request.GET["f"] == "y":