T09 · Insecure Skill Coding Practices
- Location
scripts/generate-report.mjs:127- Finding
Stored HTML Injection Through Unescaped GitHub Metadata
- Content
View full analysis
Vulnerability Details
File Location:
scripts/generate-report.mjs:127, 169, 191-201, 297-318
Vulnerability Type: Stored HTML/script injection caused by unescaped external content
Risk Level: MediumVulnerable Code
js const issues = filtered .filter(e => e.type === 'IssuesEvent' && e.payload.action === 'closed') .map(e => ({ title: e.payload.issue.title, repo: e.repo.name, url: e.payload.issue.html_url, labels: e.payload.issue.labels.map(l => l.name) })); // ... return prs .filter(pr => pr.merged_at && new Date(pr.merged_at) >= start && new Date(pr.merged_at) <= end) .map(pr => ({ title: pr.title, repo: `${owner}/${name}`, url: pr.html_url, mergedAt: pr.merged_at, number: pr.number })); // ... lines.push(`- ${issue.title} \`${issue.repo}\` #${issue.url.split('/').pop()}`); // ... lines.push(`- ${status} ${pr.title} \`${pr.repo}\` #${pr.number}`); // ... <body> ${mdToHTML(md)} </body> </html>`; } function mdToHTML(md) { return md .replace(/^# (.*)$/gm, '<h1>$1</h1>') .replace(/^## (.*)$/gm, '<h2>$1</h2>') .replace(/^### (.*)$/gm, '<h3>$1</h3>') .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>') .replace(/`([^`]+)`/g, '<code>$1</code>') .replace(/^- (.*)$/gm, '<li>$1</li>') .replace(/(<li>.*<\/li>)/s, '<ul>$1</ul>') .replace(/^> (.*)$/gm, '<blockquote>$1</blockquote>') .replace(/^---$/gm, '<hr>') .replace(/\n\n/g, '</p><p>') .replace(/\|(.+)\|/g, (m) => { const cells = m.split('|').filter(c => c.trim()); if (cells.some(c => c.includes('---'))) return ''; return '<tr>' + cells.map(c => `<td>${c.trim()}</td>`).join('') + '</tr>'; }); }Technical Analysis
Issue and pull-request titles obtained from the GitHub API are externally controlled values. They are interpolated directly into the generated Markdown and subsequently converted to HTML by
mdToHTML().The converter performs regular-expression substitutions ...[truncated 2348 chars]
- Remediation
View remediation
Remediation Suggestions
- Encode all untrusted values before inserting them into HTML. At minimum, convert
&,<,>,", and'to their corresponding HTML entities. - Prefer a maintained Markdown rendering library configured to reject or escape raw HTML.
- Apply a well-maintained HTML sanitizer after Markdown conversion. Use a strict allowlist of required tags and attributes, and prohibit event-handler attributes, scripts, unsafe URLs, embedded objects, and active content.
- Keep data and markup separate where possible. Construct DOM elements using text nodes rather than assembling HTML strings.
- Treat all GitHub-derived fields as untrusted, including titles, commit messages, repository names, labels, and URLs.
- Add regression tests with payloads containing
<script>tags, event-handler attributes, malformed tags, quotes, encoded markup, and dangerous URL schemes. - Consider applying a restrictive Content Security Policy to generated HTML as defense in depth, while not relying on CSP as a substitute for contextual output encoding.
- Document that GitHub API access leaves the local host despite local report processing, and recommend fine-grained, read-only GitHub tokens with access limited to required repositories.
- Encode all untrusted values before inserting them into HTML. At minimum, convert
