diff --git a/src/google/adk/cli/adk_web_server.py b/src/google/adk/cli/adk_web_server.py
index ef83dcd45a..6ba508640c 100644
--- a/src/google/adk/cli/adk_web_server.py
+++ b/src/google/adk/cli/adk_web_server.py
@@ -15,6 +15,7 @@
from __future__ import annotations
import asyncio
+import base64
from contextlib import asynccontextmanager
import importlib
import json
@@ -1673,6 +1674,32 @@ async def list_metrics_info(app_name: str) -> ListMetricsInfoResponse:
"/apps/{app_name}/users/{user_id}/sessions/{session_id}/artifacts/{artifact_name}",
response_model_exclude_none=True,
)
+def _sanitize_svg_content(svg_str: str) -> str:
+ """Remove XSS vectors from SVG content.
+
+ Removes foreignObject, script tags, and event handlers.
+ """
+ if not svg_str:
+ return svg_str
+
+ # Remove foreignObject elements
+ svg_str = re.sub(r']*>.*?', '', svg_str,
+ flags=re.IGNORECASE | re.DOTALL)
+ # Remove script tags
+ svg_str = re.sub(r'', '', svg_str,
+ flags=re.IGNORECASE | re.DOTALL)
+ # Remove event handler attributes
+ handlers = ['onerror', 'onload', 'onclick', 'onmouseover', 'onmouseout',
+ 'onkeydown', 'onkeyup', 'onchange', 'onsubmit', 'onfocus', 'onblur']
+ for handler in handlers:
+ pattern = handler + r'\s*=\s*["']?[^"'> ]*["']?'
+ svg_str = re.sub(pattern, '', svg_str, flags=re.IGNORECASE)
+ # Remove javascript: URLs
+ svg_str = re.sub(r'javascript:', '', svg_str, flags=re.IGNORECASE)
+
+ return svg_str
+
+
async def load_artifact(
app_name: str,
user_id: str,
@@ -1689,6 +1716,30 @@ async def load_artifact(
)
if not artifact:
raise HTTPException(status_code=404, detail="Artifact not found")
+
+ # Sanitize SVG content to prevent XSS
+ try:
+ if artifact and hasattr(artifact, 'inline_data') and artifact.inline_data:
+ inline_data = artifact.inline_data
+ mime_type = getattr(inline_data, 'mime_type', '')
+
+ if mime_type == 'image/svg+xml':
+ data = getattr(inline_data, 'data', None)
+ if data:
+ # Decode base64
+ if isinstance(data, bytes):
+ svg_str = data.decode('utf-8')
+ else:
+ svg_str = base64.b64decode(data).decode('utf-8')
+
+ # Sanitize
+ clean_svg = _sanitize_svg_content(svg_str)
+
+ # Re-encode
+ inline_data.data = base64.b64encode(clean_svg.encode()).decode()
+ except Exception as e:
+ logger.warning(f"SVG sanitization skipped: {e}")
+
return artifact
@app.get(
diff --git a/tests/unittests/cli/test_svg_sanitization.py b/tests/unittests/cli/test_svg_sanitization.py
new file mode 100644
index 0000000000..a9f3c43261
--- /dev/null
+++ b/tests/unittests/cli/test_svg_sanitization.py
@@ -0,0 +1,137 @@
+# Copyright 2026 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Tests for SVG sanitization to prevent Stored XSS."""
+
+import pytest
+from google.adk.cli.adk_web_server import _sanitize_svg_content
+
+
+class TestSvgSanitization:
+ """Test SVG XSS sanitization."""
+
+ def test_remove_foreignobject(self) -> None:
+ """foreignObject elements are removed."""
+ svg = ''
+ result = _sanitize_svg_content(svg)
+ assert ' None:
+ """Script tags are removed."""
+ svg = ''
+ result = _sanitize_svg_content(svg)
+ assert ''
+ result = _sanitize_svg_content(svg)
+ assert '' in result
+ assert 'Safe content' in result
+ assert '