GHSA-xc7j-3g8q-9vh4 CVE-2026-52772
YesWiki has stored XSS in Bazar form-field templates via unescaped field.label / field.hint (|raw('html'))
- Severity
- 5.5 / 10
- Medium · CVSS 3.1
- Exploit chance (EPSS)
- 0.34%
- Chance of exploitation in the next 30 days. Higher than 27.24% of scored vulnerabilities.
- Known exploited
- No
- Not in CISA's catalog
- Weakness
- CWE-116 Improper Encoding or Escaping of Output
- CWE-79 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
- Fix
4.6.6- Upgrade yeswiki/yeswiki to this version or later
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— sametitle=/alt=pattern (the commit only fixed line 3, line 14 remains)tools/bazar/templates/inputs/user.twig:41, 55— sametools/bazar/templates/inputs/bookmarklet.twig:4— sametools/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 nostriptagsat all, so direct tag injection (<img src=x onerror=…>) 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
doryphoreat HEAD6c653dd(the audit checkout) - All releases that ship the listed templates with the
|raw('html')/|rawfilter 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 %}
<img loading="lazy" class="tooltip_aide" title="{{ field.hint|raw('html') }}" alt="{{ field.hint|raw('html') }}" src="tools/bazar/presentation/images/aide.png" width="16" height="16" />
{% 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 = '"><script>alert(1)</script>' gives:
<img … title=""><script>alert(1)</script>" alt="…" …
The <script> 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:
{{ field.label|raw }}
tools/bazar/templates/layouts/field.twig:5:
{%- 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 <label> element. raw suppresses escaping, there is no striptags. field.label = '<img src=x onerror=alert(1)>' injects an <img> 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
- Install YesWiki and log in as admin (or as any user with the
saisie_formulaireACL). - 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 throughlayouts/input.twig, so thetitle=/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:
<input type="range" … placeholder="Enter value" onmouseover="alert('XSS via field.label in range.twig')" x="" required />
PoC 2 — layouts/input.twig tooltip injection (Sink class [C])
Set the field's hint (Aide) to:
"><script>alert('XSS via field.hint in layouts/input.twig — fires on EVERY field type')</script><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:
<img src=x onerror="alert('XSS via field.label in layouts/input.twig')">
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_formulairebeing admin-only by default — many deployments grantsaisie_formulaireto specific user groups (per-deployment ACL configured viaconfig['permissions']['action']['saisie_formulaire']). For those deployments, the bug is exploitable by any user in those groups against any visitor. The audit pattern at commitfe7244b(the same team explicitly gatedimported-formPOST handling onsaisie_formulaire) demonstrates thatsaisie_formulaireis 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 as01-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-
fe7244bwindow — for any deployment still running a build that predatesfe7244b(theimported-formauth fix from 2026-05-12), the source offield.label/field.hintwas reachable from unauthenticated POST to theimported-formhandler, making this finding unauth-stored-XSS on those builds. The current code path closes that source side, but reinforces that the sink-side fix ate6b66aashould 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 therawfilter. Let Twig's attribute-context autoescape handle the value:placeholder="{{ field.label|striptags|trim }}" title="{{ field.hint|striptags|trim }}" alt="{{ field.hint|striptags|trim }}"striptagsis fine to keep if there's a UX reason to strip incidental HTML; the security is in the absence ofraw. -
For label-body contexts (
<label>{{ field.label|raw }}</label>) — decide which is the design intent and apply it everywhere:- If labels really need to render bold/italic/links: pass
field.labelthroughHtmlPurifierService::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 andrawbecomes safe. - If labels are plain text: drop the
rawfilter and let{{ field.label }}autoescape.
- If labels really need to render bold/italic/links: pass
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:19inputs/email.twig:13layouts/input.twig:7, 9layouts/field.twig:5inputs/textarea.twig:14, 16inputs/user.twig:41, 55inputs/bookmarklet.twig:4, 6inputs/subscribe.twig:16inputs/linked-entry.twig:4
A grep-driven CI check for |raw('html') and |raw inside Bazar twig templates would surface any future reintroduction.
Affected versions
Packagist
| Package | Vulnerable | Fixed in |
|---|---|---|
| yeswiki/yeswiki | < 4.6.6 |
4.6.6 |
How to fix
Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.
yeswiki/yeswiki Packagist → 4.6.6
- composer
composer require yeswiki/yeswiki:^4.6.6
Severity breakdown
CVSS 3.1 5.5
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:N
- Attack vector
- Network
- Attack complexity
- Low
- Privileges required
- High
- User interaction
- None
- Scope
- Changed
- Confidentiality
- Low
- Integrity
- Low
- Availability
- None
Weakness
CWE-116: Improper Encoding or Escaping of Output
The product prepares a structured message for communication with another component, but encoding or escaping of the data is either missing or done incorrectly. As a result, the intended structure of the message is not preserved.
How to prevent it
- Architecture and Design: Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid. For example, consider using the ESAPI Encoding control [REF-45] or a similar tool, library, or framework. These will help the programmer encode outputs in a manner less prone to error. Alternately, use built-in functions, but consider using wrappers in case those functions are discovered to have a vulnerability.
- Architecture and Design: If available, use structured mechanisms that automatically enforce the separation between data and code. These mechanisms may be able to provide the relevant quoting, encoding, and validation automatically, instead of relying on the developer to provide this capability at every point where output is generated. For example, stored procedures can enforce database query structure and reduce the likelihood of SQL injection.
- Architecture and Design, Implementation: Understand the context in which your data will be used and the encoding that will be expected. This is especially important when transmitting data between different components, or when generating outputs that can contain multiple encodings at the same time, such as web pages or multi-part mail messages. Study all expected communication protocols and data representations to determine the required encoding strategies.
- Architecture and Design: In some cases, input validation may be an important strategy when output encoding is not a complete solution. For example, you may be providing the same output that will be processed by multiple consumers that use different encodings or representations. In other cases, you may be required to allow user-supplied input to contain control information, such as limited HTML tags that support formatting in a wiki or bulletin board. When this type of requirement must be met, use an extremely strict allowlist to limit which control sequences can be used. Verify that the resulting syntactic structure is what you expect. Use your normal encoding methods for the remainder of the input.
- Architecture and Design: Use input validation as a defense-in-depth measure to reduce the likelihood of output encoding errors (see CWE-20).
- Requirements: Fully specify which encodings are required by components that will be communicating with each other.
- Implementation: When exchanging data between components, ensure that both components are using the same character encoding. Ensure that the proper encoding is applied at each interface. Explicitly set the encoding you are using whenever the protocol allows you to do so.
CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
How to prevent it
- Architecture and Design: Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid [REF-1482]. Examples of libraries and frameworks that make it easier to generate properly encoded output include Microsoft's Anti-XSS library, the OWASP ESAPI Encoding module, and Apache Wicket.
- Implementation, Architecture and Design: Understand the context in which your data will be used and the encoding that will be expected. This is especially important when transmitting data between different components, or when generating outputs that can contain multiple encodings at the same time, such as web pages or multi-part mail messages. Study all expected communication protocols and data representations to determine the required encoding strategies. For any data that will be output to another web page, especially any data that was received from external inputs, use the appropriate encoding on all non-alphanumeric characters. Parts of the same output document may require different encodings, which will vary depending on whether the output is in the: HTML body Element attributes (such as src="XYZ") URIs JavaScript sections Cascading Style Sheets and style property etc. Note that HTML Entity Encoding is only appropriate for the HTML body. Consult the XSS Prevention Cheat Sheet [REF-724] for more details on the types of encoding and escaping that are needed.
- Architecture and Design, Implementation: Understand all the potential areas where untrusted inputs can enter your software: parameters or arguments, cookies, anything read from the network, environment variables, reverse DNS lookups, query results, request headers, URL components, e-mail, files, filenames, databases, and any external systems that provide data to the application. Remember that such inputs may be obtained indirectly through API calls.
- Architecture and Design: For any security checks that are performed on the client side, ensure that these checks are duplicated on the server side, in order to avoid CWE-602. Attackers can bypass the client-side checks by modifying values after the checks have been performed, or by changing the client to remove the client-side checks entirely. Then, these modified values would be submitted to the server.
- Architecture and Design: If available, use structured mechanisms that automatically enforce the separation between data and code. These mechanisms may be able to provide the relevant quoting, encoding, and validation automatically, instead of relying on the developer to provide this capability at every point where output is generated.
- Implementation: Use and specify an output encoding that can be handled by the downstream component that is reading the output. Common encodings include ISO-8859-1, UTF-7, and UTF-8. When an encoding is not specified, a downstream component may choose a different encoding, either by assuming a default encoding or automatically inferring which encoding is being used, which can be erroneous. When the encodings are inconsistent, the downstream component might treat some character or byte sequences as special, even if they are not special in the original encoding. Attackers might then be able to exploit this discrepancy and conduct injection attacks; they even might be able to bypass protection mechanisms that assume the original encoding is also being used by the downstream component. The problem of inconsistent output encodings often arises in web pages. If an encoding is not specified in an HTTP header, web browsers often guess about which encoding is being used. This can open up the browser to subtle XSS attacks.
- Implementation: With Struts, write all data from form beans with the bean's filter attribute set to true.
- Implementation: To help mitigate XSS attacks against the user's session cookie, set the session cookie to be HttpOnly. In browsers that support the HttpOnly feature (such as more recent versions of Internet Explorer and Firefox), this attribute can prevent the user's session cookie from being accessible to malicious client-side scripts that use document.cookie. This is not a complete solution, since HttpOnly is not supported by all browsers. More importantly, XmlHttpRequest and other powerful browser technologies provide read access to HTTP headers, including the Set-Cookie header in which the HttpOnly flag is set.
- Implementation: Assume all input is malicious. Use an "accept known good" input validation strategy, i.e., use a list of acceptable inputs that strictly conform to specifications. Reject any input that does not strictly conform to specifications, or transform it into something that does. When performing input validation, consider all potentially relevant properties, including length, type of input, the full range of acceptable values, missing or extra inputs, syntax, consistency across related fields, and conformance to business rules. As an example of business rule logic, "boat" may be syntactically valid because it only contains alphanumeric characters, but it is not valid if the input is only expected to contain colors such as "red" or "blue." Do not rely exclusively on looking for malicious or malformed inputs. This is likely to miss at least one undesirable input, especially if the code's environment changes. This can give attackers enough room to bypass the intended validation. However, denylists can be useful for detecting potential attacks or determining which inputs are so malformed that they should be rejected outright. When dynamically constructing web pages, use stringent allowlists that limit the character set based on the expected value of the parameter in the request. All input should be validated and cleansed, not just parameters that the user is supposed to specify, but all data in the request, including hidden fields, cookies, headers, the URL itself, and so forth. A common mistake that leads to continuing XSS vulnerabilities is to validate only fields that are expected to be redisplayed by the site. It is common to see data from the request that is reflected by the application server or the application that the development team did not anticipate. Also, a field that is not currently reflected may be used by a future developer. Therefore, validating ALL parts of the HTTP request is recommended. Note that proper output encoding, escaping, and quoting is the most ef
- Architecture and Design: When the set of acceptable objects, such as filenames or URLs, is limited or known, create a mapping from a set of fixed input values (such as numeric IDs) to the actual filenames or URLs, and reject all other inputs.
- Operation: Use an application firewall that can detect attacks against this weakness. It can be beneficial in cases in which the code cannot be fixed (because it is controlled by a third party), as an emergency prevention measure while more comprehensive software assurance measures are applied, or to provide defense in depth [REF-1481].
- Operation, Implementation: When using PHP, configure the application so that it does not use register_globals. During implementation, develop the application so that it does not rely on this feature, but be wary of implementing a register_globals emulation that is subject to weaknesses such as CWE-95, CWE-621, and similar issues.
References
Packages
Other links
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why it's related |
|---|---|---|
| GHSA-9369-69wj-7m2f YesWiki Vulnerable to Authenticated PHP Object Injection in BazarImportAction via unserialize | Critical | Also affects yeswiki/yeswiki |
| GHSA-4pf7-cc4r-g63h YesWiki has Authenticated SQL Injection via ReactionManager | High | Also affects yeswiki/yeswiki |
| GHSA-r5xw-gcgw-hwp5 YesWiki Vulnerable to Reflected XSS via Unescaped `id` Parameter in Bazar Widget HTML Attributes | Medium | Also affects yeswiki/yeswiki |
| GHSA-35f3-pg38-486f YesWiki Vulnerable to Reflected XSS via Unescaped Archived-Revision `time` Parameter in `handlers/page/show.php` | Medium | Also affects yeswiki/yeswiki |
| GHSA-8f2v-2qhj-gfwg YesWiki: Second-Order SQL Injection in Page Delete API via Unescaped Page Tag (`ApiController::deletePage`) | High | Also affects yeswiki/yeswiki |
| GHSA-qg78-vmvc-fhjw YesWiki: SQL Injection possible through public Bazar entry-listing APIs via numeric `query`/`queries` filters | High | Also affects yeswiki/yeswiki |
| GHSA-vw42-752g-5mrp YesWiki has Unauthenticated Server-Side Request Forgery via ActivityPub `Signature.keyId` | High | Also affects yeswiki/yeswiki |
| GHSA-mv28-wj57-f57g YesWiki Vulnerable to Unauthenticated ActivityPub Signature-Verification Bypass via `!openssl_verify(...)` accepting `int(-1)` | High | Also affects yeswiki/yeswiki |
MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.
Check your repos with MergeHarbor