# GHSA-9rcc-pmj8-ffhr: Semantic MediaWiki's Special:FacetedSearch cstate hidden inputs enable reflected XSS (residual of CVE-2025-10354)

> GHSA-9rcc-pmj8-ffhr is a medium-severity vulnerability with a CVSS 3.1 base score of 6.1, classed as Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79), in mediawiki/semantic-media-wiki (Packagist). Affected versions: >= 4.2.0, <= 7.2.0. It is fixed in 7.2.1.

Canonical page: https://cveminer.com/vulnerabilities/GHSA-9rcc-pmj8-ffhr

| Field | Value |
| --- | --- |
| GHSA | GHSA-9rcc-pmj8-ffhr |
| Severity | Medium |
| CVSS 3.1 | 6.1 `CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N` (GitHub Advisory Database) |
| CWE | CWE-79 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') |
| CISA KEV | Not listed |
| Published | 18 Sept 2026 |
| Modified | 18 Sept 2026 |

## Description

Special:FacetedSearch `cstate` hidden inputs enable reflected XSS (residual of CVE-2025-10354)

##### Details

###### Affected versions and vulnerable location

- Confirmed present on latest shipped release tag available in the local clone: `SemanticMediaWiki/SemanticMediaWiki@7.2.0`.
- Confirmed present on default branch `master` at HEAD `18f418b4cdf2875e67a741349179a22c1573f61c`.

Vulnerable sink (default-branch representation):

- `src/MediaWiki/Specials/FacetedSearch/HtmlBuilder.php:131-133`
  - Builds `$hidden` by concatenating unescaped request-controlled `cstate[$key]` values into an HTML attribute context (`value="..."`).
- `templates/FacetedSearch/search.mustache:25`
  - Inserts the constructed fragment via `{{{hidden}}}` (no HTML escaping at this boundary).

###### Reachability trace (verified from source)

1. HTTP entrypoint:
   - `GET` to `Special:FacetedSearch` dispatches into `SMW\MediaWiki\Specials\SpecialFacetedSearch::execute()`.
2. Request decoding boundary:
   - `SpecialFacetedSearch::execute()` constructs `UrlArgs` from `$request->getValues()` and calls `ParametersProcessor::checkRequest($request)`.
3. Checksum gate:
   - `ParametersProcessor::checkRequest()` clears `cstate` only when `filtered != 1` and `getInt('csum', 0) != crc32(getVal('q', ''))`.
4. Decoder -> HTML assembly:
   - `HtmlBuilder::buildHTML()` iterates `foreach ( $urlArgs->getArray( 'cstate' ) as $key => $value )` and concatenates each into `$hidden` without escaping.
   - `HtmlBuilder::buildHTML()` passes `$hidden` into the template variable `hidden`.
5. HTML injection sink:
   - `templates/FacetedSearch/search.mustache` renders `{{{hidden}}}` into the `<form>`, so the concatenated markup is inserted as raw HTML.

##### PoC

###### Reproduction steps (source-derived)

1. Choose a `q` value.
2. Compute `csum` as `crc32(q)`.
3. Send a request that includes:
   - `q=<chosen>`
   - `csum=<crc32(q)>`
   - at least one `cstate[<key>]=<payload>` entry

Example request shape:

```text
/index.php/Special:FacetedSearch?q=Text&csum=<crc32(Text)>&cstate[0]=x%22%20autofocus%20onfocus%3Dalert(1)%20x%22
```

##### Impact

###### Attacker model

- Any remote attacker who can send HTTP requests to `Special:FacetedSearch` (or the localized alias mapped to the same `SpecialFacetedSearch` class) can supply attacker-controlled query parameters.
- Preconditions:
  - The attacker must make `cstate` survive `ParametersProcessor::checkRequest()`, either by setting `csum` to `crc32(q)` (when `filtered != 1`), or by setting `filtered=1`.
  - The attacker must supply `cstate[<key>]` values containing characters that break out of the HTML `value="..."` attribute context (for example an injected `"` to terminate the attribute value).

###### Severity and CVSS reasoning

Proposed severity: MEDIUM.

Proposed CVSS v3.1 vector: `CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N`.

Rationale:

- AV:N: delivered over the network via query parameters.
- AC:L: requires only setting `q`, `csum`, and at least one `cstate` entry.
- PR:N: no authentication required for the request path in this code.
- UI:R: the victim must load the crafted URL.
- S:C: reflected XSS executes in the wiki origin and can affect other users depending on deployment and browser behavior.

##### Why this is a residual of CVE-2025-10354

- The CVE-2025-10354 hardening shipped by escaping the `q` parameter before emitting it into the `value="{{q}}"` attribute.
- Commit `3d675ce` updates only the `q` rendering to use `htmlspecialchars( $urlArgs->get( 'q', '' ) )` and does not touch the adjacent `cstate` -> `$hidden` construction loop.
- As a result, `cstate` remains an unescaped input source that flows into the same raw template injection point (`{{{hidden}}}`), creating a distinct reflected-XSS lane.

##### Output (from code inspection)

Given the payload idea where `cstate[0]` starts with `x" ... x"`, `HtmlBuilder.php` constructs the hidden fragment by concatenation:

```html
<input name="cstate[0]" type="hidden" value="x" autofocus onfocus=alert(1) x">
```

Because `search.mustache` injects the fragment via `{{{hidden}}}`, the attacker-controlled markup participates in normal HTML parsing in the response body.

##### Suggested fix

- Escape both the `cstate` key and value when constructing `$hidden`.
- Minimal code change in `src/MediaWiki/Specials/FacetedSearch/HtmlBuilder.php`:

```php
foreach ( $urlArgs->getArray( 'cstate' ) as $key => $value ) {
	$safeKey = htmlspecialchars( (string)$key, ENT_QUOTES, 'UTF-8' );
	$safeValue = htmlspecialchars( (string)$value, ENT_QUOTES, 'UTF-8' );
	$hidden .= '<input name="cstate[' . $safeKey . ']" type="hidden" value="' . $safeValue . '">';
}
```

This keeps the raw `{{{hidden}}}` template insertion safe by ensuring the concatenated HTML fragment itself is attribute-escaped.

##### How I found it and a note on tooling

I anchored on the published CVE-2025-10354 patch by verifying in the checked-out repository that commit `3d675ce` changes only the `q` rendering in `HtmlBuilder.php` to use `htmlspecialchars`.

Then I traced the reachable request path from `SpecialFacetedSearch::execute()` through `ParametersProcessor::checkRequest()` (checksum gate for whether `cstate` survives) into `HtmlBuilder::buildHTML()` where `$hidden` is constructed from `cstate` without escaping and injected into `templates/FacetedSearch/search.mustache` via `{{{hidden}}}`.

(End of file)

##### AI tooling

I used AI assistance for the code audit and for drafting this report. I manually verified the finding against the project's source at the location cited above before reporting it, and the severity and impact assessment are my own.

## Affected packages

| Package | Ecosystem | PURL | Vulnerable versions | Fixed in |
| --- | --- | --- | --- | --- |
| mediawiki/semantic-media-wiki | Packagist | `pkg:composer/mediawiki/semantic-media-wiki` | >= 4.2.0, <= 7.2.0 | 7.2.1 |

## Questions

**Does GHSA-9rcc-pmj8-ffhr have a CVE ID?**

No CVE ID has been assigned to GHSA-9rcc-pmj8-ffhr yet. It is tracked only as a GitHub advisory.

**What is the CVSS score of GHSA-9rcc-pmj8-ffhr?**

GHSA-9rcc-pmj8-ffhr has a CVSS 3.1 base score of 6.1 (Medium), vector CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N.

**Which versions are affected by GHSA-9rcc-pmj8-ffhr?**

mediawiki/semantic-media-wiki (Packagist): >= 4.2.0, <= 7.2.0.

**How do I fix GHSA-9rcc-pmj8-ffhr?**

Upgrade to 7.2.1 or later.

**What is the Package URL (PURL) for GHSA-9rcc-pmj8-ffhr?**

The affected package's PURL is pkg:composer/mediawiki/semantic-media-wiki.

## References

- https://github.com/SemanticMediaWiki/SemanticMediaWiki/security/advisories/GHSA-9rcc-pmj8-ffhr (advisory)
- https://github.com/advisories/GHSA-9rcc-pmj8-ffhr (advisory)
- https://github.com/SemanticMediaWiki/SemanticMediaWiki (package)
- https://github.com/SemanticMediaWiki/SemanticMediaWiki/releases/tag/7.2.1

---

Source: CVEminer (https://cveminer.com/vulnerabilities/GHSA-9rcc-pmj8-ffhr), compiled from OSV (GitHub advisory), GitHub Advisory Database. OSV and GitHub Advisory Database content is licensed CC BY 4.0.
