# Craft CMS: DOM XSS via GitHub issue title in CraftSupport widget

GitHub issue title with an img onerror payload rendered via jQuery html: in CraftSupport's feedback search executes XSS in a Craft admin's control panel session.

- Payout: Research
- Severity: high
- Program: Craft CMS
- Found by: Crypto-Cat
- Disclosed: 2026-07-06
- CVE: CVE-2026-55790
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-24x4-j6x9-rfw5
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-24x4-j6x9-rfw5
- Tags: area:web domain:web impact:ato impact:privesc kind:research platform:ghsa sev:high technique:js-analysis technique:source-review vuln:xss

**Target:** CraftSupport widget feedback search screen (CraftSupportWidget.js / FeedbackScreen)
**Root cause:** FeedbackScreen.getSearchResultText returns the GitHub API issue title verbatim, and CraftSupportWidget.js passes it into jQuery's `html:` option, which sets innerHTML. The GitHub API returns titles unencoded and the widget queries it directly from the browser with no sanitization.
**Steps:**
1. Attacker creates a GitHub issue on craftcms/cms with title `<img src=x onerror=alert(document.domain)> cannot upload files`.
2. Victim admin opens the control panel dashboard and the CraftSupport widget, clicks "Give feedback".
3. Victim types `cannot upload files` in the search box.
4. The poisoned issue is returned by the GitHub API and rendered via jQuery `html:`, firing `alert(document.domain)` in the admin session.
**How found:** Reporter compared the two help widgets: HelpScreen (Stack Exchange API) HTML-encodes titles, while FeedbackScreen (GitHub API) returns raw titles; tracing getSearchResultText into the jQuery `html:` sink revealed the innerHTML injection.
**Exploitation:** Attacker needs only a GitHub account to plant the payload in an issue title. When an admin searches a matching term in the feedback screen, the title is inserted as innerHTML and the onerror handler executes same-origin in the control panel, giving access to `Craft.csrfTokenName`/`Craft.csrfTokenValue` to send authenticated action requests as the admin.
**Escalation:** Payload runs in admin session with CSRF token access, enabling same-origin privileged action requests without further interaction.
**Insight:** When a widget aggregates third-party APIs, compare sibling data sources: an API that HTML-encodes responses masks the sink, while a raw-JSON API (GitHub) exposes it. Grep for jQuery `html:`/innerHTML sinks fed by external API fields like titles.

## Proof of concept (copied exactly from the original report)

**Malicious GitHub issue title payload**

```
<img src=x onerror=alert(document.domain)> cannot upload files
```

**Vulnerable jQuery html option in widget**

```
$('<a>', {
  href: this.getSearchResultUrl(results[i]),
  target: '_blank',
  html:
    '<span class="status ' +
    this.getSearchResultStatus(results[i]) +
    '"></span>' +
    this.getSearchResultText(results[i]),
})
```

