T09 · Insecure Skill Coding Practices
- Location
scripts/render.mjs:44- Finding
Unescaped Markdown and title content executes as active HTML in an unsandboxed browser
- Content
View full analysis
' + cells.map(c => `<${tag}>${formatCell(c)}`).join('') + ''; if (!headerDone) headerDone = false; // will be set by separator } else { if (inTable) { html += ''; inTable = false; headerDone = false; } // Handle headers const hMatch = trimmed.match(/^(#{1,6})\s+(.+)/); if (hMatch) { const level = hMatch[1].length; html += `${hMatch[2]}`; } else if (trimmed === '') { html += '
'; } else { html += `${formatCell(trimmed)}
`; } } function formatCell(text) { return text .replace(/\*\*(.+?)\*\*/g, '$1') .replace(/`(.+?)`/g, '$1') .replace(/~~(.+?)~~/g, '$1'); } ``` The resulting HTML also directly embeds the untrusted title and is loaded into Chrome with JavaScript enabled and the browser sandbox disabled: ```js ${values.title ? `${values.title}` : ''} ${mdToHtml(md)} `; const browser = await puppeteer.launch({ executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', headless: 'new', args: ['--no-sandbox', '--disable-setuid-sandbox'], }); const page = await browser.newPage(); await page.setViewport({ width: parseInt(values.width), height: 100 }); await page.setContent(htmlContent, { waitUntil: 'networkidle0' }); ``` ### Technical Analysis Markdown and title values are inserted into an HTML document without contextual HTML escaping or sanitization. The `formatCell()` function only replaces a few Markdown patterns; it does not encode characters such as `<`, `>`, `&`, `"`, or `'`. Several input paths are affected: - Table cells pass through `formatCell()` and are then ...[truncated 2575 chars]- Remediation
View remediation
`, `"`, and `'` before inserting Markdown cells, paragraphs, headings, or titles into templates. - Apply Markdown emphasis transformations only through a parser that safely emits permitted elements. 2. **Reject or sanitize raw HTML** - Use a maintained Markdown renderer configured with raw HTML disabled. - If limited HTML is required, sanitize it using a strict allowlist that excludes scripts, event-handler attributes, frames, embedded objects, external-resource tags, and dangerous URL schemes. 3. **Restore browser isolation** - Remove `--no-sandbox` and `--disable-setuid-sandbox`. - Run Chrome under a dedicated, minimally privileged operating-system account or inside an additional container or sandbox. 4. **Disable unnecessary active capabilities** - Disable JavaScript for this static rendering use case with `page.setJavaScriptEnabled(false)`. - Add request interception and reject all network requests unless a narrowly defined local resource is explicitly required. - Apply a restrictive Content Security Policy, such as one denying scripts, frames, plugins, connections, and remote media. 5. **Add resource controls** - Impose maximum input length, table size, viewport width, and render duration. - Validate `--width` as a finite integer within a safe range. - Use explicit timeouts and ensure the browser is closed in a `finally` block. 6. **Add security regression tests** - Verify that inputs containing `
