#16958: fix(security): escape user input in HTML gallery to prevent stored XSS
size: M
Cluster:
Plugin Management Enhancements
## Summary
- **Problem:** `write_gallery()` in the OpenAI image gen skill embeds user-provided prompt text, filenames, and output paths directly into HTML without escaping
- **Why it matters:** Stored XSS — an attacker injects `<script>` tags via image prompts that execute when anyone opens the gallery (CVSS 7.5, CWE-79)
- **What changed:** Applied `html.escape()` (Python stdlib) to all three user-controlled values before HTML embedding
- **What did NOT change:** Gallery layout, image generation, file structure unchanged
Closes #12538
## Changes
**`skills/openai-image-gen/scripts/gen.py`**:
- Added `from html import escape as html_escape`
- Escaped prompt text in `<figcaption>`, filename in `href`/`src` attributes, output path in `<code>` tag
**`skills/openai-image-gen/scripts/test_gen.py`** (new):
- 4 tests: XSS payloads, attribute injection, special characters, normal operation
## Security Impact
- No new permissions, network calls, or execution surface changes
- Fixes stored XSS vulnerability in generated HTML output
## Testing
All 4 tests pass. Verified `<script>alert("xss")</script>` in prompt renders as escaped text, not executable JS.
## Compatibility
Backward compatible. Special characters in prompts now display as HTML entities — no visual difference for normal text.
## AI Disclosure
AI-assisted (Claude via OpenClaw). Code reviewed and tested.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR fixes two security vulnerabilities across two skill scripts:
- **Stored XSS in `gen.py`**: Applied `html.escape()` to user-controlled prompt text, filenames, and output paths before embedding them in the generated HTML gallery, preventing script injection via crafted image prompts (CWE-79).
- **Symlink/path traversal in `package_skill.py`**: Added symlink rejection and resolved-path boundary checks during `.skill` file creation, preventing sensitive file exfiltration via symlink attacks or crafted path traversal (zip-slip).
Both fixes are minimal, targeted, and backward compatible. New test files cover the security scenarios for each fix.
<h3>Confidence Score: 4/5</h3>
- This PR is safe to merge — it adds targeted security hardening with no functional behavior changes.
- Both security fixes are correct and well-scoped. The HTML escaping uses Python's stdlib `html.escape()` with appropriate `quote=True` for attribute contexts. The symlink/path traversal protection uses a defense-in-depth approach (symlink check + resolved path boundary check). New tests cover the key attack vectors. Only minor style issues (unused imports in test files) were found.
- No files require special attention. Both fixes are straightforward and well-tested.
<sub>Last reviewed commit: 055d8de</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#2544: fix(security): XSS vulnerability in Canvas Host + Windows CI stability
by Kiwitwitter · 2026-01-27
74.6%
#8075: fix(skills): add --ignore-scripts to all package managers
by yubrew · 2026-02-03
73.7%
#21745: tools: fix image inbound resolution and empty allowBundled
by AIflow-Labs · 2026-02-20
73.6%
#20796: fix(security): OC-22 prevent Zip Slip and symlink following in skil...
by aether-ai-agent · 2026-02-19
73.3%
#16992: fix(gateway): escape XML entities in file.filename to prevent promp...
by AI-Reviewer-QS · 2026-02-15
72.6%
#10705: security: extend skill scanner to detect threats in markdown skill ...
by Alex-Alaniz · 2026-02-06
72.5%
#5273: fix: skill frontmatter fixes + description improvements (35 skills)
by Terwox · 2026-01-31
72.4%
#3933: fix(skills): Make skill scripts executable
by jaysonsantos · 2026-01-29
72.1%
#10514: Security: harden AGENTS.md with gateway, prompt injection, and supp...
by catpilothq · 2026-02-06
71.3%
#22198: fix(skills): treat empty allowBundled array as block-all
by haitao-sjsu · 2026-02-20
71.1%