diff --git a/client/src/entry/analysis/modules/CenterFrame.vue b/client/src/entry/analysis/modules/CenterFrame.vue
index 20e6a0cb080..7159891d883 100644
--- a/client/src/entry/analysis/modules/CenterFrame.vue
+++ b/client/src/entry/analysis/modules/CenterFrame.vue
@@ -1,46 +1,99 @@
-
-
-
-
+
+
+
+ {{ sanitizedMessage }}
+
+
+ Review Allowlist
+ if outputs of {{ sanitizedToolId }} are trusted and should be shown as HTML.
+
+
+
Loading ...
+
+
+
diff --git a/client/src/entry/analysis/router.js b/client/src/entry/analysis/router.js
index 033b70136cb..ba7da642ada 100644
--- a/client/src/entry/analysis/router.js
+++ b/client/src/entry/analysis/router.js
@@ -238,6 +238,7 @@ export function getRouter(Galaxy) {
component: CenterFrame,
props: (route) => ({
src: `/datasets/${route.params.datasetId}/display/?preview=True`,
+ isPreview: true,
}),
},
{
diff --git a/lib/galaxy/datatypes/data.py b/lib/galaxy/datatypes/data.py
index 66462f51740..5ff2da26b13 100644
--- a/lib/galaxy/datatypes/data.py
+++ b/lib/galaxy/datatypes/data.py
@@ -60,7 +60,6 @@ from galaxy.util.markdown import (
indicate_data_truncated,
literal_via_fence,
)
-from galaxy.util.sanitize_html import sanitize_html
from galaxy.util.zipstream import ZipstreamWrapper
from . import (
dataproviders as p_dataproviders,
@@ -635,21 +634,18 @@ class Data(metaclass=DataMeta):
return result
def _yield_user_file_content(self, trans, from_dataset: HasCreatingJob, filename: str, headers: Headers) -> IO:
- """This method is responsible for sanitizing the HTML if needed."""
+ """This method sets the content type header to text/plain if we don't trust html content."""
if trans.app.config.sanitize_all_html and headers.get("content-type", None) == "text/html":
- # Sanitize anytime we respond with plain text/html content.
# Check to see if this dataset's parent job is allowlisted
# We cannot currently trust imported datasets for rendering.
- if not from_dataset.creating_job.imported and from_dataset.creating_job.tool_id.startswith(
- tuple(trans.app.config.sanitize_allowlist)
- ):
- return open(filename, mode="rb")
-
- # This is returning to the browser, it needs to be encoded.
- # TODO Ideally this happens a layer higher, but this is a bad
- # issue affecting many tools
- with open(filename) as f:
- return sanitize_html(f.read()).encode("utf-8")
+ content_type = "text/html"
+ if from_dataset.creating_job.imported:
+ content_type = "text/plain"
+ headers["x-sanitized-job-imported"] = True
+ if not from_dataset.creating_job.tool_id.startswith(tuple(trans.app.config.sanitize_allowlist)):
+ content_type = "text/plain"
+ headers["x-sanitized-tool-id"] = from_dataset.creating_job.tool_id
+ headers["content-type"] = content_type
return open(filename, mode="rb")
diff --git a/lib/galaxy/selenium/has_driver.py b/lib/galaxy/selenium/has_driver.py
index 9fae270ca7b..d0ab5ed98ca 100644
--- a/lib/galaxy/selenium/has_driver.py
+++ b/lib/galaxy/selenium/has_driver.py
@@ -116,6 +116,9 @@ class HasDriver:
def element_absent(self, selector_template: Target) -> bool:
return len(self.find_elements(selector_template)) == 0
+ def switch_to_frame(self, name: str = "frame"):
+ return self._wait_on(ec.frame_to_be_available_and_switch_to_it((By.NAME, name)))
+
def wait_for_xpath(self, xpath: str, **kwds) -> WebElement:
element = self._wait_on(
ec.presence_of_element_located((By.XPATH, xpath)), f"XPATH selector [{xpath}] to become present", **kwds
diff --git a/test/functional/tools/html_output.xml b/test/functional/tools/html_output.xml
new file mode 100644
index 00000000000..7558bca44ec
--- /dev/null
+++ b/test/functional/tools/html_output.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+ Hello World
+
+
+
+ Hello, World!
+
+
+
+ ]]>
+
+
+
+
+
diff --git a/test/functional/tools/sample_tool_conf.xml b/test/functional/tools/sample_tool_conf.xml
index bb9d7568f60..3e0b6fb3729 100644
--- a/test/functional/tools/sample_tool_conf.xml
+++ b/test/functional/tools/sample_tool_conf.xml
@@ -243,6 +243,7 @@
+
diff --git a/test/integration_selenium/test_allowlist_sanitization.py b/test/integration_selenium/test_allowlist_sanitization.py
new file mode 100644
index 00000000000..109862158cb
--- /dev/null
+++ b/test/integration_selenium/test_allowlist_sanitization.py
@@ -0,0 +1,52 @@
+from .framework import (
+ selenium_test,
+ SeleniumIntegrationTestCase,
+)
+
+
+class TestAllowListSanitization(SeleniumIntegrationTestCase):
+ run_as_admin = True
+ axe_skip = True # skip testing iframe contents
+
+ @classmethod
+ def handle_galaxy_config_kwds(cls, config):
+ super().handle_galaxy_config_kwds(config)
+ config["sanitize_all_html"] = True
+
+ def run_html_output(self):
+ history_id = self.dataset_populator.new_history()
+ run_response = self.dataset_populator.run_tool("html_output", {}, history_id=history_id)
+ hda_id = run_response["outputs"][0]["id"]
+ self.dataset_populator.wait_for_dataset(history_id, dataset_id=hda_id)
+ return hda_id
+
+ @selenium_test
+ def test_html_output_sanitized(self):
+ self.login()
+ hda_id = self.run_html_output()
+ self.get(f"datasets/{hda_id}/preview")
+ self.wait_for_selector_visible("[data-description='sanitization warning']")
+ self.assert_selector_absent("[data-description='allowlist link']")
+ self.screenshot("sanitization warning")
+ assert self.switch_to_frame()
+ self.assert_selector_absent("[data-description='hello-world']")
+
+ @selenium_test
+ def test_html_output_sanitized_admin(self):
+ self.admin_login()
+ hda_id = self.run_html_output()
+ self.get(f"datasets/{hda_id}/preview")
+ self.wait_for_selector_visible("[data-description='sanitization warning']")
+ self.wait_for_selector_visible("[data-description='allowlist link']")
+ self.screenshot("sanitization warning admin")
+ try:
+ self._put(
+ "/api/sanitize_allow?tool_id=html_output", data={"params": {"tool_id": "html_output"}}, admin=True
+ ).raise_for_status()
+ self.driver.refresh()
+ self.assert_selector_absent("[data-description='sanitization warning']")
+ self.assert_selector_absent("[data-description='allowlist link']")
+ assert self.switch_to_frame()
+ self.wait_for_selector("[data-description='hello-world']")
+ finally:
+ self._delete("/api/sanitize_allow?tool_id=html_output", admin=True)