VYPR
'` gives:\n\n```html\n\" alt=\"…\" …\n```\n\nThe `\n```\n\nSave. Page render fires the `onerror` immediately — no hover, no click, no `striptags` filter in the way.\n\n## Impact\n\n### Direct\n\n- **Stored XSS on every visitor of any Bazar form page** — a privileged form editor injects script into a field's label/hint and the script runs in the wiki origin against every viewer of the form, including unauthenticated guests. Cookie theft, session hijack of any admin who visits, full content modification, phishing overlays.\n- **Universal sink in `layouts/input.twig`** — sinks [C] and [D] live in the base layout extended by every field type, so a single field with a malicious hint poisons every form render across the wiki, not just forms using a specific input type.\n\n### Indirect / second-order\n\n- **Privilege amplification despite `saisie_formulaire` being admin-only by default** — many deployments grant `saisie_formulaire` to specific user groups (per-deployment ACL configured via `config['permissions']['action']['saisie_formulaire']`). For those deployments, the bug is exploitable by any user in those groups against any visitor. The audit pattern at commit `fe7244b` (the same team explicitly gated `imported-form` POST handling on `saisie_formulaire`) demonstrates that `saisie_formulaire` is in fact a \"trusted-input\" boundary — outputs of that boundary should not assume HTML-safety.\n- **Composability with the unpatched POI/CSRF in `BazarImportAction` (reported separately as `01-bazarimport-poi-csrf.md`)** — once any XSS exists in the wiki origin, an attacker can fetch a CSRF token (if added as part of the POI fix) and chain XSS → POI → RCE without needing to phish the admin onto a third-party origin.\n- **The pre-`fe7244b` window** — for any deployment still running a build that predates `fe7244b` (the `imported-form` auth fix from 2026-05-12), the source of `field.label` / `field.hint` was reachable from **unauthenticated** POST to the `imported-form` handler, making this finding unauth-stored-XSS on those builds. The current code path closes that source side, but reinforces that the sink-side fix at `e6b66aa` should be applied class-wide.\n\n## Suggested fix\n\nApply the same transformation `e6b66aa` applied to `text.twig` / `textarea.twig` placeholders, class-wide:\n\n- For attribute contexts (`placeholder=`, `title=`, `alt=`, etc.) — drop the `raw` filter. Let Twig's attribute-context autoescape handle the value:\n\n ```twig\n placeholder=\"{{ field.label|striptags|trim }}\"\n title=\"{{ field.hint|striptags|trim }}\"\n alt=\"{{ field.hint|striptags|trim }}\"\n ```\n\n `striptags` is fine to keep if there's a UX reason to strip incidental HTML; the security is in the absence of `raw`.\n\n- For label-body contexts (``) — decide which is the design intent and apply it everywhere:\n - If labels really need to render bold/italic/links: pass `field.label` through `HtmlPurifierService::cleanHTML()` at the point where the field object is constructed (i.e. `BazarField::__construct`'s `$this->label = …` line), so any subsequent template emits already-purified HTML and `raw` becomes safe.\n - If labels are plain text: drop the `raw` filter and let `{{ field.label }}` autoescape.\n\nThe label-body case in `layouts/input.twig:9`, `layouts/field.twig:5`, and the four `inputs/*.twig` files is the highest-impact patch target because it's reached by every field type; the attribute-context cases are more surgical.\n\nSweep target list (all in `tools/bazar/templates/`):\n\n- `inputs/range.twig:19`\n- `inputs/email.twig:13`\n- `layouts/input.twig:7, 9`\n- `layouts/field.twig:5`\n- `inputs/textarea.twig:14, 16`\n- `inputs/user.twig:41, 55`\n- `inputs/bookmarklet.twig:4, 6`\n- `inputs/subscribe.twig:16`\n- `inputs/linked-entry.twig:4`\n\nA grep-driven CI check for `|raw('html')` and `|raw` inside Bazar twig templates would surface any future reintroduction.","additionalType":"https://schema.org/SoftwareApplication","sameAs":["https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2026-52772"]},"keywords":"CVE-2026-52772, medium, CWE-116, CWE-79, Yeswiki Yeswiki","mentions":[{"@type":"SoftwareApplication","name":"Yeswiki","applicationCategory":"SecurityApplication","publisher":{"@type":"Organization","name":"Yeswiki"}}],"isAccessibleForFree":true},{"@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://portal.vyprsec.ai/"},{"@type":"ListItem","position":2,"name":"CVEs","item":"https://portal.vyprsec.ai/cves"},{"@type":"ListItem","position":3,"name":"CVE-2026-52772","item":"https://portal.vyprsec.ai/cves/CVE-2026-52772"}]}]}
Medium severityNVD Advisory· Published Jul 9, 2026

YesWiki has stored XSS in Bazar form-field templates via unescaped field.label / field.hint (|raw('html'))

CVE-2026-52772

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:19placeholder="{{ field.label|raw('html')|striptags }}"
  • tools/bazar/templates/inputs/email.twig:13placeholder="{{ field.label|raw('html')|striptags }}"
  • tools/bazar/templates/layouts/input.twig:7title="{{ 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:

$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:

placeholder="{{ field.label|raw('html')|striptags }}"

tools/bazar/templates/inputs/email.twig:13:

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:

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:

{% 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 = '">' gives:

" alt="…" …

The <span x=" ` Save. Any page that renders the form executes the script at parse time, before any user interaction. The vector is universal because layouts/input.twig is the base template extended by every field type. ### PoC 3 — layouts/input.twig label-body injection (Sink class [D]) Set the field's label to: ``


Save. Page render fires the `onerror` immediately — no hover, no click, no `striptags` filter in the way.

## Impact

### Direct

- **Stored XSS on every visitor of any Bazar form page** — a privileged form editor injects script into a field's label/hint and the script runs in the wiki origin against every viewer of the form, including unauthenticated guests. Cookie theft, session hijack of any admin who visits, full content modification, phishing overlays.
- **Universal sink in `layouts/input.twig`** — sinks [C] and [D] live in the base layout extended by every field type, so a single field with a malicious hint poisons every form render across the wiki, not just forms using a specific input type.

### Indirect / second-order

- **Privilege amplification despite `saisie_formulaire` being admin-only by default** — many deployments grant `saisie_formulaire` to specific user groups (per-deployment ACL configured via `config['permissions']['action']['saisie_formulaire']`). For those deployments, the bug is exploitable by any user in those groups against any visitor. The audit pattern at commit `fe7244b` (the same team explicitly gated `imported-form` POST handling on `saisie_formulaire`) demonstrates that `saisie_formulaire` is in fact a "trusted-input" boundary — outputs of that boundary should not assume HTML-safety.
- **Composability with the unpatched POI/CSRF in `BazarImportAction` (reported separately as `01-bazarimport-poi-csrf.md`)** — once any XSS exists in the wiki origin, an attacker can fetch a CSRF token (if added as part of the POI fix) and chain XSS → POI → RCE without needing to phish the admin onto a third-party origin.
- **The pre-`fe7244b` window** — for any deployment still running a build that predates `fe7244b` (the `imported-form` auth fix from 2026-05-12), the source of `field.label` / `field.hint` was reachable from **unauthenticated** POST to the `imported-form` handler, making this finding unauth-stored-XSS on those builds. The current code path closes that source side, but reinforces that the sink-side fix at `e6b66aa` should be applied class-wide.

## Suggested fix

Apply the same transformation `e6b66aa` applied to `text.twig` / `textarea.twig` placeholders, class-wide:

- For attribute contexts (`placeholder=`, `title=`, `alt=`, etc.) — drop the `raw` filter. Let Twig's attribute-context autoescape handle the value:

  ```twig

placeholder="{{ field.label|striptags|trim }}" title="{{ field.hint|striptags|trim }}" alt="{{ field.hint|striptags|trim }}" `` striptags is fine to keep if there's a UX reason to strip incidental HTML; the security is in the absence of raw. - For label-body contexts ({{ field.label|raw }}) — decide which is the design intent and apply it everywhere: - If labels really need to render bold/italic/links: pass field.label through HtmlPurifierService::cleanHTML() at the point where the field object is constructed (i.e. BazarField::__construct's $this->label = … line), so any subsequent template emits already-purified HTML and raw becomes safe. - If labels are plain text: drop the raw filter and let {{ field.label }} autoescape. The label-body case in layouts/input.twig:9, layouts/field.twig:5, and the four inputs/*.twig files is the highest-impact patch target because it's reached by every field type; the attribute-context cases are more surgical. Sweep target list (all in tools/bazar/templates/): - inputs/range.twig:19 - inputs/email.twig:13 - layouts/input.twig:7, 9 - layouts/field.twig:5 - inputs/textarea.twig:14, 16 - inputs/user.twig:41, 55 - inputs/bookmarklet.twig:4, 6 - inputs/subscribe.twig:16 - inputs/linked-entry.twig:4 A grep-driven CI check for |raw('html') and |raw` inside Bazar twig templates would surface any future reintroduction.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
yeswiki/yeswikiPackagist
< 4.6.64.6.6

Affected products

1

Patches

Vulnerability mechanics

References

3

News mentions

0

No linked articles in our index yet.