fix: escape HTML in workstation browsing history to prevent stored XSS (CWE-79)#846
Open
sebastiondev wants to merge 1 commit intoQwenLM:mainfrom
Open
fix: escape HTML in workstation browsing history to prevent stored XSS (CWE-79)#846sebastiondev wants to merge 1 commit intoQwenLM:mainfrom
sebastiondev wants to merge 1 commit intoQwenLM:mainfrom
Conversation
…d XSS (CWE-79) The update_browser_list() function interpolated title and url values from meta_data.jsonl directly into an HTML string without escaping. An attacker who controls a browsing entry title (via the browser extension or by writing to the meta_data file) could inject arbitrary HTML/JavaScript into the Gradio web UI. Fix: apply html.escape() to both title and url before interpolation into the HTML template string.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Vulnerability Summary
CWE-79: Stored Cross-Site Scripting (XSS) in
qwen_server/workstation_server.pySeverity: High (stored XSS, no authentication required)
Data Flow
urlandtitlevalues are written tometa_data.jsonlvia:database_server.cache_page()— unauthenticated POST to/endpoint(port 7866)workstation_server.add_file()— Gradio file uploadupdate_browser_list()(line ~162) interpolates these values directly into an HTML string using.format(), which is then rendered by Gradio'sgr.HTMLcomponent.Additionally,
get_basename_from_url()URL-decodes percent-encoded characters, so a URL likehttp://evil.com/%3Cimg%20src=x%20onerror=alert(1)%3Eproduces the title<img src=x onerror=alert(1)>.Exploit Scenarios
Vector 1 — Chrome Extension (social engineering):
database_server, which URL-decodes it intometa_data.jsonlgr.HTML→ XSS firesVector 2 — Direct POST (network access required):
Fix Description
Change: Added
html.escape()calls for bothurl(withquote=Truefor attribute context) andtitlebefore HTML interpolation inupdate_browser_list().Rationale:
html.escape()is the standard Python library function for neutralizing HTML metacharacters (<,>,&,",')quote=Trueon the URL ensures theid="ck-..."andhref="..."attribute contexts are also safeDiff: 1 file changed, 5 insertions, 2 deletions.
Test Results Summary
The fix was verified by tracing the code path:
x[0]andx[1]frommeta_data.jsonlare interpolated directly into HTML →<img src=x onerror=alert(1)>renders as live HTMLhtml.escape(x[0], quote=True)andhtml.escape(x[1])convert<→<,>→>,"→"→ payload renders as inert textDisprove Analysis
We systematically attempted to invalidate this finding:
api_keyis for DashScope (LLM backend), not for user auth. Neither the Gradio nor FastAPI server authenticates clients.127.0.0.1, but0.0.0.0is documented and encouraged for multi-machine setups. Chrome extension path bypasses localhost restriction entirely.<script>tags don't execute via innerHTML per HTML5 spec, but event handlers (onerror,onload) DO execute.sanitize,escape,validate,clean, orallowlistcalls existed in the original code path.gradio_utils.py— confirming this is an acknowledged vulnerability class in the project.SECURITY.mdfound.Verdict: CONFIRMED VALID (high confidence)
No existing mitigation fully prevents exploitation. The Chrome extension attack vector bypasses both localhost binding and CORS restrictions, requires no special network access, and needs only a single click from the victim.
Known Limitations (for follow-up)
javascript:URI scheme:html.escape()does not preventjavascript:URIs in thehrefattribute. A URL-scheme allowlist would be a stronger defense.id="ck-..."attribute uses the escaped URL, which may differ from the raw URL used elsewhere. This is a pre-existing design concern, not introduced by this fix.qwen_agent/gui/gradio_utils.py— that file is not addressed by this PR.