Low severityNVD Advisory· Published Mar 19, 2026· Updated Mar 21, 2026
OpenClaw < 2026.2.23 - HTML Injection via Unvalidated Image MIME Type in Data-URL Interpolation
CVE-2026-32040
Description
OpenClaw versions prior to 2026.2.23 contain an html injection vulnerability in the HTML session exporter that allows attackers to execute arbitrary javascript by injecting malicious mimeType values in image content blocks. Attackers can craft session entries with specially crafted mimeType attributes that break out of the img src data-URL context to achieve cross-site scripting when exported HTML is opened.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
openclawnpm | < 2026.2.23 | 2026.2.23 |
Affected products
1Patches
1f3adf142c195fix(security): escape user input in HTML gallery to prevent stored XSS (#16958)
3 files changed · +55 −3
CHANGELOG.md+1 −0 modified@@ -38,6 +38,7 @@ Docs: https://docs.openclaw.ai ### Fixes - Agents/Compaction: count auto-compactions only after a non-retry `auto_compaction_end`, keeping session `compactionCount` aligned to completed compactions. +- Security/Skills: escape user-controlled prompt, filename, and output-path values in `openai-image-gen` HTML gallery generation to prevent stored XSS in generated `index.html` output. (#12538) Thanks @CornBrother0x. - Security/OTEL: redact sensitive values (API keys, tokens, credential fields) from diagnostics-otel log bodies, log attributes, and error/reason span fields before OTLP export. (#12542) Thanks @brandonwise. - Security/CLI: redact sensitive values in `openclaw config get` output before printing config paths, preventing credential leakage to terminal output/history. (#13683) Thanks @SleuthCo. - Install/Discord Voice: make `@discordjs/opus` an optional dependency so `openclaw` install/update no longer hard-fails when native Opus builds fail, while keeping `opusscript` as the runtime fallback decoder for Discord voice flows. (#23737, #23733, #23703) Thanks @jeadland, @Sheetaa, and @Breakyman.
skills/openai-image-gen/scripts/gen.py+4 −3 modified@@ -9,6 +9,7 @@ import sys import urllib.error import urllib.request +from html import escape as html_escape from pathlib import Path @@ -131,8 +132,8 @@ def write_gallery(out_dir: Path, items: list[dict]) -> None: [ f""" <figure> - <a href="{it["file"]}"><img src="{it["file"]}" loading="lazy" /></a> - <figcaption>{it["prompt"]}</figcaption> + <a href="{html_escape(it["file"], quote=True)}"><img src="{html_escape(it["file"], quote=True)}" loading="lazy" /></a> + <figcaption>{html_escape(it["prompt"])}</figcaption> </figure> """.strip() for it in items @@ -152,7 +153,7 @@ def write_gallery(out_dir: Path, items: list[dict]) -> None: code {{ color: #9cd1ff; }} </style> <h1>openai-image-gen</h1> -<p>Output: <code>{out_dir.as_posix()}</code></p> +<p>Output: <code>{html_escape(out_dir.as_posix())}</code></p> <div class="grid"> {thumbs} </div>
skills/openai-image-gen/scripts/test_gen.py+50 −0 added@@ -0,0 +1,50 @@ +"""Tests for write_gallery HTML escaping (fixes #12538 - stored XSS).""" + +import tempfile +from pathlib import Path + +from gen import write_gallery + + +def test_write_gallery_escapes_prompt_xss(): + with tempfile.TemporaryDirectory() as tmpdir: + out = Path(tmpdir) + items = [{"prompt": '<script>alert("xss")</script>', "file": "001-test.png"}] + write_gallery(out, items) + html = (out / "index.html").read_text() + assert "<script>" not in html + assert "<script>" in html + + +def test_write_gallery_escapes_filename(): + with tempfile.TemporaryDirectory() as tmpdir: + out = Path(tmpdir) + items = [{"prompt": "safe prompt", "file": '" onload="alert(1)'}] + write_gallery(out, items) + html = (out / "index.html").read_text() + assert 'onload="alert(1)"' not in html + assert """ in html + + +def test_write_gallery_escapes_ampersand(): + with tempfile.TemporaryDirectory() as tmpdir: + out = Path(tmpdir) + items = [{"prompt": "cats & dogs <3", "file": "001-test.png"}] + write_gallery(out, items) + html = (out / "index.html").read_text() + assert "cats & dogs <3" in html + + +def test_write_gallery_normal_output(): + with tempfile.TemporaryDirectory() as tmpdir: + out = Path(tmpdir) + items = [ + {"prompt": "a lobster astronaut, golden hour", "file": "001-lobster.png"}, + {"prompt": "a cozy reading nook", "file": "002-nook.png"}, + ] + write_gallery(out, items) + html = (out / "index.html").read_text() + assert "a lobster astronaut, golden hour" in html + assert 'src="001-lobster.png"' in html + assert "002-nook.png" in html +
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-2ww6-868g-2c56ghsaADVISORY
- github.com/openclaw/openclaw/security/advisories/GHSA-2ww6-868g-2c56ghsathird-party-advisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2026-32040ghsaADVISORY
- www.vulncheck.com/advisories/openclaw-html-injection-via-unvalidated-image-mime-type-in-data-url-interpolationghsathird-party-advisoryWEB
- github.com/openclaw/openclaw/commit/f3adf142c195000cbde31200626a1d8c8b716df9ghsaWEB
- github.com/openclaw/openclaw/pull/24140ghsaproductWEB
News mentions
0No linked articles in our index yet.