Skip to content

Commit 7deb69c

Browse files
fix(security): prevent XSS in proxy endpoint via json.dumps()
CodeQL alert #84: target_origin was inserted into JavaScript without proper escaping. Fix uses json.dumps() to safely encode the origin string for JavaScript context, preventing XSS even if the origin contained special characters. - Add json import - Use json.dumps(target_origin) for safe JS string encoding - Update test to match double-quote format from json.dumps() 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 359b8e1 commit 7deb69c

2 files changed

Lines changed: 13 additions & 5 deletions

File tree

api/routers/proxy.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""HTML proxy endpoint for interactive plots with size reporting."""
22

3+
import json
34
from urllib.parse import urlparse
45

56
import httpx
@@ -14,7 +15,13 @@
1415

1516

1617
def get_size_reporter_script(target_origin: str) -> str:
17-
"""Generate size reporter script with specified target origin."""
18+
"""Generate size reporter script with specified target origin.
19+
20+
Uses json.dumps() to safely encode the origin string for JavaScript context,
21+
preventing XSS even if the origin contained special characters.
22+
"""
23+
# Safely encode for JavaScript string context (includes quotes)
24+
safe_origin = json.dumps(target_origin)
1825
return f"""
1926
<script>
2027
(function() {{
@@ -41,7 +48,7 @@ def get_size_reporter_script(target_origin: str) -> str:
4148
type: 'pyplots-size',
4249
width: Math.ceil(width),
4350
height: Math.ceil(height)
44-
}}, '{target_origin}');
51+
}}, {safe_origin});
4552
}}
4653
}} catch (e) {{
4754
// Silently fail if postMessage is blocked

tests/unit/api/test_proxy.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,12 +257,13 @@ class TestSizeReporterScript:
257257
def test_script_uses_specific_origin(self):
258258
"""Script should use specific origin, not wildcard."""
259259
assert "'*'" not in SIZE_REPORTER_SCRIPT
260+
assert '"*"' not in SIZE_REPORTER_SCRIPT
260261
# Use regex to verify postMessage uses specific origin, not substring check
261262
# This avoids CodeQL's "incomplete URL substring sanitization" false positive
262-
# Pattern matches: }, 'https://pyplots.ai') - the end of the postMessage call
263-
pattern = r"\},\s*'https://pyplots\.ai'\)"
263+
# Pattern matches: }, "https://pyplots.ai") - json.dumps() produces double quotes
264+
pattern = r'\},\s*"https://pyplots\.ai"\)'
264265
assert re.search(pattern, SIZE_REPORTER_SCRIPT), (
265-
"postMessage must use specific origin 'https://pyplots.ai', not '*'"
266+
'postMessage must use specific origin "https://pyplots.ai", not "*"'
266267
)
267268

268269
def test_script_sends_pyplots_size_message(self):

0 commit comments

Comments
 (0)