From 4a686851a1c64ca78c5d38df5159accc2d283475 Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Sat, 28 Mar 2026 10:31:40 +0100 Subject: [PATCH] URL-encode tool ID in shed_tool_static image paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tool IDs with special characters (e.g., "EMBOSS: isochore47") were embedded into URL paths without percent-encoding. The space would be lost in transit through RST→HTML→browser, causing the server to reconstruct a GUID that didn't match any tool. Apply urllib.parse.quote(safe="/") to the entire route_to_images path so special characters are properly percent-encoded. The WSGI layer auto-decodes PATH_INFO before routing, so the controller receives the correct values. Fixes https://github.com/galaxyproject/galaxy/issues/22126 --- lib/galaxy/tool_shed/util/shed_util_common.py | 6 +- test/unit/tool_shed/test_shed_util_common.py | 56 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 test/unit/tool_shed/test_shed_util_common.py diff --git a/lib/galaxy/tool_shed/util/shed_util_common.py b/lib/galaxy/tool_shed/util/shed_util_common.py index 648b9de4eaf..90b8079f8a0 100644 --- a/lib/galaxy/tool_shed/util/shed_util_common.py +++ b/lib/galaxy/tool_shed/util/shed_util_common.py @@ -1,5 +1,6 @@ import logging import re +from urllib.parse import quote from galaxy import util from galaxy.tool_shed.util import repository_util @@ -149,7 +150,10 @@ def set_image_paths(app, text, encoded_repository_id=None, tool_shed_repository= # We're in the tool shed. route_to_images = f"/repository/static/images/{encoded_repository_id}" elif tool_shed_repository and tool_id and tool_version: - route_to_images = f"shed_tool_static/{tool_shed_repository.tool_shed}/{tool_shed_repository.owner}/{tool_shed_repository.name}/{tool_id}/{tool_version}" + route_to_images = quote( + f"shed_tool_static/{tool_shed_repository.tool_shed}/{tool_shed_repository.owner}/{tool_shed_repository.name}/{tool_id}/{tool_version}", + safe="/", + ) else: raise Exception( "encoded_repository_id or tool_shed_repository and tool_id and tool_version must be provided" diff --git a/test/unit/tool_shed/test_shed_util_common.py b/test/unit/tool_shed/test_shed_util_common.py new file mode 100644 index 00000000000..3b49dfbec9b --- /dev/null +++ b/test/unit/tool_shed/test_shed_util_common.py @@ -0,0 +1,56 @@ +from types import SimpleNamespace + +from galaxy.tool_shed.util.shed_util_common import set_image_paths + + +def test_set_image_paths_encodes_special_characters_in_tool_id(): + tool_shed_repository = SimpleNamespace( + tool_shed="toolshed.g2.bx.psu.edu", + owner="devteam", + name="emboss_5", + ) + text = ".. image:: static/images/isochore.png" + result = set_image_paths( + app=None, + text=text, + tool_shed_repository=tool_shed_repository, + tool_id="EMBOSS: isochore47", + tool_version="5.0.0.1", + ) + assert "EMBOSS%3A%20isochore47" in result + assert "EMBOSS: isochore47" not in result + + +def test_set_image_paths_preserves_slashes_in_route(): + tool_shed_repository = SimpleNamespace( + tool_shed="toolshed.g2.bx.psu.edu", + owner="devteam", + name="emboss_5", + ) + text = ".. image:: isochore.png" + result = set_image_paths( + app=None, + text=text, + tool_shed_repository=tool_shed_repository, + tool_id="isochore", + tool_version="5.0.0", + ) + assert "shed_tool_static/toolshed.g2.bx.psu.edu/devteam/emboss_5/isochore/5.0.0/" in result + + +def test_set_image_paths_does_not_modify_http_urls(): + tool_shed_repository = SimpleNamespace( + tool_shed="toolshed.g2.bx.psu.edu", + owner="devteam", + name="emboss_5", + ) + text = ".. image:: https://example.com/image.png" + result = set_image_paths( + app=None, + text=text, + tool_shed_repository=tool_shed_repository, + tool_id="mytool", + tool_version="1.0", + ) + assert ".. image:: https://example.com/image.png" in result + assert "shed_tool_static" not in result