Skip to content

Commit 20471f3

Browse files
matthew29tangcopybara-github
authored andcommitted
chore: Escape params for display_link util function
PiperOrigin-RevId: 930686244
1 parent 14a2265 commit 20471f3

3 files changed

Lines changed: 44 additions & 11 deletions

File tree

google/cloud/aiplatform/utils/_ipython_utils.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
# limitations under the License.
1616
#
1717

18+
import html
19+
import json
1820
import sys
1921
import typing
2022
import urllib
@@ -119,28 +121,31 @@ def display_link(text: str, url: str, icon: Optional[str] = "open_in_new") -> No
119121

120122
button_id = f"view-vertex-resource-{str(uuid4())}"
121123

124+
# Safe encodings
125+
safe_href = html.escape(url, quote=True)
126+
safe_text = html.escape(text)
127+
safe_icon = html.escape(icon) if icon else ""
128+
safe_url_js = json.dumps(url)
129+
122130
# Add the markup for the CSS and link component
123-
html = f"""
131+
html_out = f"""
124132
{_get_styles()}
125-
<a class="view-vertex-resource" id="{button_id}" href="#view-{button_id}">
126-
<span class="material-icons view-vertex-icon">{icon}</span>
127-
<span>{text}</span>
133+
<a class="view-vertex-resource" id="{button_id}" href="{safe_href}" target="_blank">
134+
<span class="material-icons view-vertex-icon">{safe_icon}</span>
135+
<span>{safe_text}</span>
128136
</a>
129137
"""
130138

131139
# Add the click handler for the link
132-
html += f"""
140+
html_out += f"""
133141
<script>
134142
(function () {{
135143
const link = document.getElementById('{button_id}');
136144
link.addEventListener('click', (e) => {{
137145
if (window.google?.colab?.openUrl) {{
138-
window.google.colab.openUrl('{url}');
139-
}} else {{
140-
window.open('{url}', '_blank');
146+
window.google.colab.openUrl({safe_url_js});
147+
e.preventDefault();
141148
}}
142-
e.stopPropagation();
143-
e.preventDefault();
144149
}});
145150
}})();
146151
</script>
@@ -149,7 +154,7 @@ def display_link(text: str, url: str, icon: Optional[str] = "open_in_new") -> No
149154
from IPython.display import display
150155
from IPython.display import HTML
151156

152-
display(HTML(html))
157+
display(HTML(html_out))
153158

154159

155160
def display_experiment_button(experiment: "experiment_resources.Experiment") -> None:

tests/unit/aiplatform/test_utils.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from google.cloud.aiplatform.compat.types import pipeline_failure_policy
3939
from google.cloud.aiplatform import datasets
4040
from google.cloud.aiplatform.utils import (
41+
_ipython_utils,
4142
column_transformations_utils,
4243
gcs_utils,
4344
pipeline_utils,
@@ -1137,3 +1138,29 @@ def test_load_yaml_from_invalid_uri(self, uri: str):
11371138
)
11381139
with pytest.raises(ValueError, match=message):
11391140
yaml_utils.load_yaml(uri)
1141+
1142+
1143+
class TestIpythonUtils:
1144+
"""Tests for IPython utility functions."""
1145+
1146+
def test_display_link_raises_value_error_for_invalid_url(self):
1147+
with pytest.raises(ValueError, match="Only urls starting with"):
1148+
_ipython_utils.display_link("bad", "https://example.com")
1149+
1150+
def test_display_link_success_and_sanitizes(self):
1151+
mock_display_module = mock.MagicMock()
1152+
with mock.patch.dict("sys.modules", {"IPython.display": mock_display_module, "IPython": mock.MagicMock()}):
1153+
_ipython_utils.display_link(
1154+
text="<script>alert('xss')</script>",
1155+
url="https://console.cloud.google.com/test?param=1&another=2",
1156+
icon="<icon>",
1157+
)
1158+
mock_display_module.HTML.assert_called_once()
1159+
html_arg = mock_display_module.HTML.call_args[0][0]
1160+
1161+
assert "&lt;script&gt;alert(&#x27;xss&#x27;)&lt;/script&gt;" in html_arg
1162+
assert "href=\"https://console.cloud.google.com/test?param=1&amp;another=2\"" in html_arg
1163+
assert "window.google.colab.openUrl(\"https://console.cloud.google.com/test?param=1&another=2\")" in html_arg
1164+
assert "&lt;icon&gt;" in html_arg
1165+
1166+
mock_display_module.display.assert_called_once_with(mock_display_module.HTML.return_value)

tests/unit/architecture/test_vertexai_import.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ def test_vertexai_import():
3333
import google.api_core.operations_v1 as _ # noqa: F811
3434
import google.api_core.rest_streaming as _ # noqa: F811
3535
import google.cloud.storage as _ # noqa: F811
36+
import html as _ # noqa: F811
3637

3738
try:
3839
# Needed for Python 3.8

0 commit comments

Comments
 (0)