Skip to content

Add <img> to safe-outputs HTML tag allowlist #29009

@sg650

Description

@sg650

Feature: Allow <img> tags in safe-output content

Context

In #17298 (fixed by #17346, shipped in v0.47.6), the convertXmlTags() allowlist was expanded to include GFM inline tags (abbr, del, ins, kbd, mark, s, span). The same logic should be extended to <img>.

Problem

<img> tags in safe-output bodies (e.g. create-issue, update-issue) are converted to parentheses:

<img align="right" width="120" src="https://example.com/image.png" alt="Mascot" />

becomes:

(img align="right" width="120" src="https://example.com/image.png" alt="Mascot" /)

The only workaround is standard markdown ![alt](url), which doesn't support alignment or sizing.

Why <img> is safe

stripDangerousAttributes() already strips on* event handlers and style from every allowed tag. Adding <img> to allowedTags would produce:

Input Output Safe?
<img src="url" alt="text" width="120" align="right"> Preserved as-is Yes — no dangerous attributes
<img src=x onerror="alert(1)"> <img src=x> Yes — onerror stripped by existing logic
<img src=x onload="steal()"> <img src=x> Yes — onload stripped
<img style="position:fixed" src="url"> <img src="url"> Yes — style stripped

The src URL is separately validated by sanitizeUrlDomains(), which already redacts URLs from domains not in allowed-domains. So even src is covered.

The existing XSS test (<img src=x onerror="alert(1)">) would continue to pass — the tag is preserved but the dangerous attribute is gone.

Use case: image alignment in dashboard issues

Agentic workflows that maintain living dashboard issues (status reports, watchtowers, audit summaries) benefit from layout control. The most common pattern is floating a small image to the right of a heading or blockquote:

<img align="right" width="120" src="https://example.com/mascot.png" alt="Dashboard mascot" />

> *Status summary text flows to the left of the image...*

This is a well-established GitHub Markdown pattern — used across READMEs, issue templates, and profile pages. It works when a human edits the issue manually but breaks when an agent writes the same content through safe-outputs.

![alt](url) can't replicate this — markdown images don't support align, width, or height.

Suggested change

Add "img" to the allowedTags array in convertXmlTags() (sanitize_content_core.cjs, ~line 630):

const allowedTags = [
  "abbr",
  "b",
  "blockquote",
  "br",
  "code",
  "del",
  "details",
  "em",
  "h1", "h2", "h3", "h4", "h5", "h6",
  "hr",
  "i",
  "img",   // <-- add
  "ins",
  "kbd",
  // ...
];

Update the existing XSS test to verify onerror/onload are stripped while src, alt, width, height, and align are preserved.

Broader consideration

The same argument applies to other GitHub-supported media/layout tags: <picture>, <source>, <video>, <audio>. But <img> is by far the most common and impactful — happy to keep this scoped to just that.

Environment

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions