diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index c522c3082..d27fd24bf 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -129,6 +129,13 @@ class FlaskDIWrapper: self._reserve_urls(resource.urls) + # enforce our rule that URLs should not contain a trailing slash + for url in resource.urls: + if url.endswith("/"): + raise ValueError( + f"Resource {resource.__name__} has an invalid URL: A URL " + "should not have a trailing slash." + ) dependencies = self._container.resolve_dependencies(resource) self._api.add_resource(resource, *resource.urls, resource_class_args=dependencies) diff --git a/monkey/monkey_island/cc/resources/propagation_credentials.py b/monkey/monkey_island/cc/resources/propagation_credentials.py index a017d2625..1130d50bd 100644 --- a/monkey/monkey_island/cc/resources/propagation_credentials.py +++ b/monkey/monkey_island/cc/resources/propagation_credentials.py @@ -11,7 +11,7 @@ _stolen_collection = "stolen-credentials" class PropagationCredentials(AbstractResource): - urls = ["/api/propagation-credentials/", "/api/propagation-credentials/"] + urls = ["/api/propagation-credentials", "/api/propagation-credentials/"] def __init__(self, credentials_repository: ICredentialsRepository): self._credentials_repository = credentials_repository diff --git a/monkey/tests/unit_tests/monkey_island/cc/resources/test_propagation_credentials.py b/monkey/tests/unit_tests/monkey_island/cc/resources/test_propagation_credentials.py index 5991abec5..0de5c2c91 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/resources/test_propagation_credentials.py +++ b/monkey/tests/unit_tests/monkey_island/cc/resources/test_propagation_credentials.py @@ -22,8 +22,8 @@ from monkey_island.cc.resources.propagation_credentials import ( ) ALL_CREDENTIALS_URL = PropagationCredentials.urls[0] -CONFIGURED_CREDENTIALS_URL = urljoin(ALL_CREDENTIALS_URL, _configured_collection) -STOLEN_CREDENTIALS_URL = urljoin(ALL_CREDENTIALS_URL, _stolen_collection) +CONFIGURED_CREDENTIALS_URL = urljoin(ALL_CREDENTIALS_URL + "/", _configured_collection) +STOLEN_CREDENTIALS_URL = urljoin(ALL_CREDENTIALS_URL + "/", _stolen_collection) @pytest.fixture @@ -105,7 +105,7 @@ def test_all_propagation_credentials_endpoint__put_not_allowed(flask_client): assert resp.status_code == HTTPStatus.METHOD_NOT_ALLOWED -NON_EXISTENT_COLLECTION_URL = urljoin(ALL_CREDENTIALS_URL, "bogus-credentials") +NON_EXISTENT_COLLECTION_URL = urljoin(ALL_CREDENTIALS_URL + "/", "bogus-credentials") def test_propagation_credentials_endpoint__get_not_found(flask_client): diff --git a/monkey/tests/unit_tests/monkey_island/cc/test_app.py b/monkey/tests/unit_tests/monkey_island/cc/test_app.py index 225f87ff8..40e87a32b 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/test_app.py +++ b/monkey/tests/unit_tests/monkey_island/cc/test_app.py @@ -75,9 +75,16 @@ def test_url_check_slash_stripping__trailing_slash(resource_manager): def test_url_check_slash_stripping__path_separation(resource_manager): - resource3 = get_mock_resource("res3", ["/beef/face/"]) + resource3 = get_mock_resource("res3", ["/beef/face"]) resource4 = get_mock_resource("res4", ["/beefface"]) # Following shouldn't raise and exception resource_manager.add_resource(resource3) resource_manager.add_resource(resource4) + + +def test_trailing_slash_enforcement(resource_manager): + bad_endpoint = "/beef/face/" + with pytest.raises(ValueError): + resource3 = get_mock_resource("res3", [f"{bad_endpoint}"]) + resource_manager.add_resource(resource3)