T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/md2img.js:7
- Finding
- Active HTML Injection Through Untrusted Model Configuration During Image Rendering<![CDATA[ ## Vulnerability Details **File Location**: `scripts/show-model-config.py:121-138`, `scripts/md2img.js:7-11`, `scripts/md2img.js:30`, and `scripts/md2img.js:68-72` **Vulnerability Type**: Untrusted HTML injection and unsafe document rendering **Risk Level**: Medium ### Vulnerable Code Configuration-derived provider names, model IDs, and aliases are inserted into Markdown without escaping: ```python for pname, pinfo in config.get('models', {}).get('providers', {}).items(): models = pinfo.get('models', []) lines.append(f'### {pname} ({len(models)})') lines.append('| Model ID | Alias | Context | Type |') lines.append('| :--- | :--- | ---: | :---: |') for m in models: mid = m.get('id', '-') ctx = fmt_ctx(m.get('contextWindow', 0)) tag = '**Multimodal**' if 'image' in m.get('input', []) else 'Text' alias = '-' for k, v in defaults_models.items(): if k == f'{pname}/{mid}': alias = v.get('alias', '') or '-' break lines.append(f'| {mid} | `{alias}` | {ctx} | {tag} |') ``` The Markdown renderer explicitly permits raw HTML: ```javascript const md = require('markdown-it')({ html: true, linkify: true, typographer: true }).use(require('markdown-it-emoji').full); ``` The rendered HTML is embedded directly into the document: ```javascript const htmlBody = md.render(content); ``` The resulting document is passed to `wkhtmltoimage` without disabling JavaScript or external resource loading: ```javascript const run = spawnSync( 'wkhtmltoimage', ['--width', '660', '--disable-smart-width', tempHtml, outputPath], { encoding: 'utf-8' } ); ``` ### Technical Analysis The image workflow treats values from `openclaw.json` as trusted presentation data. Provider names, model IDs, and aliases are interpolated directly into Markdown, but Markdown and HTML metacharacters are not escaped. Because `markdown-it` is configured with `html: true`, raw HTM ...[truncated 2610 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Disable raw HTML in `markdown-it`: ```javascript const md = require('markdown-it')({ html: false, linkify: true, typographer: true }).use(require('markdown-it-emoji').full); ``` 2. Escape all configuration-derived values before inserting them into Markdown. This must cover provider names, model IDs, aliases, primary model references, and fallback values. Use a dedicated Markdown-escaping function rather than ad hoc replacements. 3. Apply an allowlist-based HTML sanitizer if HTML support is genuinely required. Remove scripts, event-handler attributes, frames, embedded objects, remote-resource elements, and unsafe URL schemes. 4. Harden the renderer with supported `wkhtmltoimage` options, including disabling JavaScript and local-file access. External network resource loading should also be blocked through renderer configuration, sandboxing, or network isolation. 5. Run image rendering in a restricted subprocess or container with: - No unnecessary network access. - A minimal filesystem view. - A dedicated unprivileged user. - Resource and execution time limits. 6. Validate configuration field types and impose reasonable length limits before generating output. 7. Add regression tests containing payloads in every displayed configuration field, including: - Raw `<script>` elements. - Remote `<img>` elements. - HTML event-handler attributes. - `iframe` and stylesheet references. - Markdown table delimiters and backticks. 8. Update `SKILL.md` so that its sanitization claim accurately reflects the implemented controls. ]]>
