GitHub Advisoryadvisory · Packagist
GHSA-xc7j-3g8q-9vh4 YesWiki has stored XSS in Bazar form-field templates via unescaped field.label / field.hint (|raw('html'))
MEDIUM
Official page ↗
- Aliases
- CVE-2026-52772
- Ecosystem
- Packagist
- Packages
- yeswiki/yeswiki
- Severity
- Moderate
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:N
- Weaknesses
- CWE-116, CWE-79
- Reviewed
- 2026-07-09
Affected packages
• Packagist yeswiki/yeswiki: < 4.6.6 → fixed in 4.6.6
Description
Bazar form-field templates still apply `|raw('html')` to `field.label` / `field.hint` in attribute and label-body contexts — stored XSS in form renders (sibling class of commit `e6b66aa`)
CWE: CWE-79 (Improper Neutralization of Input During Web Page Generation, "Cross-site Scripting") via CWE-116 (Improper Encoding or Escaping of Output) — same class as the partial fix at commit `e6b66aa`
CVSS v3.1: `CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:N` → 4.7 (Medium)
(Privileges Required = High because writing the field definitions requires `saisie_formulaire`, which `tools/bazar/services/Guard.php:58-61` grants only to admins by default; Scope = Changed because the XSS payload set by a form-editor admin executes in the origin context of arbitrary viewers, including unauthenticated visitors.)
Summary
Commit `e6b66aa` ("fix(bazar): leave the twig escape placeholder as is", 2026-05-19) recognised that emitting `field.label` through Twig's `raw('html')` filter into an HTML attribute is unsafe — Twig's `raw` marker suppresses the attribute auto-escape, `striptags` removes `` tags but not `"`, so a label containing `"` can break out of the attribute and inject event-handler attributes. The commit fixed `tools/bazar/templates/inputs/text.twig:19` and `tools/bazar/templates/inputs/textarea.twig:3`.
At least seven additional templates have the same pattern and were not touched by the fix:
• `tools/bazar/templates/inputs/range.twig:19` — `placeholder="{{ field.label|raw('html')|striptags }}"`
• `tools/bazar/templates/inputs/email.twig:13` — `placeholder="{{ field.label|raw('html')|striptags }}"`
• `tools/bazar/templates/layouts/input.twig:7` — `title="{{ field.hint|raw('html') }}" alt="{{ field.hint|raw('html') }}"`
• `tools/bazar/templates/inputs/textarea.twig:14` — same `title=`/`alt=` pattern (the commit only fixed line 3, line 14 remains)
• `tools/bazar/templates/inputs/user.twig:41, 55` — same
• `tools/bazar/templates/inputs/bookmarklet.twig:4` — same
• `tools/bazar/templates/layouts/input.twig:9`, `tools/bazar/templates/layouts/field.twig:5`, `tools/bazar/templates/inputs/subscribe.twig:16`, `tools/bazar/templates/inputs/linked-entry.twig:4`, `tools/bazar/templates/inputs/textarea.twig:16`, `tools/bazar/templates/inputs/bookmarklet.twig:6` — `{{ field.label|raw }}` *outside* an attribute (label-body), with no `striptags` at all, so direct tag injection (``) executes
The `layouts/input.twig` and `layouts/field.twig` files are base layouts inherited by every Bazar field type, so a single malicious `field.hint` reaches into every form that uses that field.
Affected
• YesWiki `doryphore` at HEAD `6c653dd` (the audit checkout)
• All releases that ship the listed templates with the `|raw('html')` / `|raw` filter in attribute or label-body context
Vulnerability details
[A] — Source: `field.label` and `field.hint` are populated from form definitions
`tools/bazar/fields/BazarField.php:46-53`:
```php
$this->label = empty($values[self::FIELD_LABEL]) ? '' : html_entity_decode($values[self::FIELD_LABEL]);
$this->size = $values[self::FIELD_SIZE];
$this->maxChars = $values[self::FIELD_MAX_CHARS];
$this->default = $values[self::FIELD_DEFAULT];
$this->required = $values[self::FIELD_REQUIRED] == 1;
$this->searchable = $values[self::FIELD_SEARCHABLE];
$this->hint = $values[self::FIELD_HINT]; // [A] no decoding/escaping
```
`field.label` is `html_entity_decode($values[FIELD_LABEL])` — the decode actively *turns* HTML-entity-encoded payloads (`"`, `"`) back into raw `"`, defeating any entity-encoded mitigation a form author might apply. `field.hint` is the raw string from the form definition. Both flow into the field's `__toString`-like context unchanged. Form definitions are written by users with the `saisie_formulaire` ACL (`tools/bazar/services/Guard.php:45-62` — admins by default; the same ACL the audit team chose to gate `imported-form` POST handling under in commit `fe7244b`).
[B] — Sink class 1: attribute-context `|raw('html')|striptags` (placeholder breakout)
`tools/bazar/templates/inputs/range.twig:19`:
```twig
placeholder="{{ field.label|raw('html')|striptags }}"
```
`tools/bazar/templates/inputs/email.twig:13`:
```twig
placeholder="{{ field.label|raw('html')|striptags }}"
```
`raw('html')` marks the value as a `Markup` object, which causes Twig's HTML auto-escaper to skip it (`Twig\Markup::__toString`). `striptags` removes `` sequences but does not touch `"`, `'`, or `=`. A `field.label` of:
```
hi" onmouseover="alert(document.cookie)" x="
```
passes `striptags` unchanged, is marked safe by `raw('html')`, and lands inside the attribute as:
```html
placeholder="hi" onmouseover="alert(document.cookie)" x=""
```
The injected `onmouseover` fires when a viewer hovers the input. Same vector as the pre-fix `text.twig:19`.
[C] — Sink class 2: attribute-context `|raw('html')` *without* `striptags` (worse)
`tools/bazar/templates/layouts/input.twig:7`:
```twig
{% if field.hint %}
{% endif %}
```
Identical patterns in `tools/bazar/templates/inputs/textarea.twig:14`, `tools/bazar/templates/inputs/user.twig:41`, `tools/bazar/templates/inputs/user.twig:55`, `tools/bazar/templates/inputs/bookmarklet.twig:4`.
There is no `striptags` here at all, so the attacker has the full attribute-injection alphabet plus full HTML if the parser desynchronises. Setting `field.hint = '">alert(1)'` gives:
```html
alert(1)" alt="…" …
```
The `` runs at page parse time. Because `layouts/input.twig` is extended by every field-type template, a single malicious `field.hint` on any field in any form propagates into every form render.
[D] — Sink class 3: label-body `|raw` (direct DOM injection)
`tools/bazar/templates/layouts/input.twig:9`:
```twig
{{ field.label|raw }}
```
`tools/bazar/templates/layouts/field.twig:5`:
```twig
{%- block label -%}{{ field.label|raw }}{%- endblock -%}
```
Plus `subscribe.twig:16`, `linked-entry.twig:4`, `textarea.twig:16`, `bookmarklet.twig:6`.
These are outside any attribute, in the body of a `` element. `raw` suppresses escaping, there is no `striptags`. `field.label = ''` injects an `` tag straight into the label DOM; the `onerror` fires the moment the page renders, with no user interaction.
Why the fix at `e6b66aa` is incomplete
The fix correctly replaced `field.label | raw('html') | striptags` with `field.label | striptags | trim` (no `raw`) in `text.twig`'s placeholder and `textarea.twig`'s textarea placeholder. The fix is the right pattern — drop the `raw` so Twig's attribute-context autoescaper does its job — but it was applied at two specific call sites instead of being treated as a class-wide replacement. The siblings above use the same `|raw('html')|striptags` or `|raw('html')` idiom and are all currently exploitable.
Proof of concept
Setup
1. Install YesWiki and log in as admin (or as any user with the `saisie_formulaire` ACL).
2. Navigate to *Bazar → Formulaires → Nouveau formulaire* and create a form. Add any field of type `range`, `email`, or any other field type (every field type renders through `layouts/input.twig`, so the `title=` / `alt=` / label-body vectors apply universally).
PoC 1 — `range.twig` placeholder attribute breakout (Sink class [B])
Set the field's label to:
```
Enter value" onmouseover="alert('XSS via field.label in range.twig')" x="
```
Save the form. Have any visitor (including unauthenticated guests if the form is published) open a page that renders the form. Hovering the range input fires the injected handler.
Rendered HTML:
```html
<input type="range" … placeholder="Enter value" onmouseover="alert('XSS via field.label in range.twig')" …
Related
Alias · CVE (1)
- CVE-2026-52772 YesWiki is a wiki system written in PHP. vulnerability