GitHub Security Advisory: GHSA-mx4q-xxc9-pf5q
Package: sylius/sylius (COMPOSER)
Severity: MODERATE (CVSS 4.8)
CWE: CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
Vulnerable versions: >= 2.2.0, <= 2.2.2
Patched in: 2.2.3

Summary: Sylius Vulnerable to Authenticated Stored XSS

Details:
### Impact

An authenticated stored cross-site scripting (XSS) vulnerability exists in multiple places across the shop frontend and admin panel due to unsanitized entity names being rendered as raw HTML.

**Shop breadcrumbs** (`shared/breadcrumbs.html.twig`): The `breadcrumbs` macro uses the Twig `|raw` filter on label values. Since taxon names, product names, and ancestor names flow directly into these labels, a malicious taxon name like `<img src=x onerror=alert('XSS')>` is rendered and executed as JavaScript on the storefront.

**Admin product taxon picker** (`ProductTaxonTreeController.js`): The `rowRenderer` method interpolates `${name}` directly into a template literal building HTML, allowing script injection through taxon names in the admin panel.

**Admin autocomplete fields** (Tom Select): Dropdown items and options render entity names as raw HTML without escaping, allowing XSS through any autocomplete field displaying entity names.

An **authenticated administrator** can inject arbitrary HTML or JavaScript via entity names (e.g. taxon name) that is persistently rendered for all users.

### Patches

The issue is fixed in versions: 2.0.16, 2.1.12, 2.2.3 and above.

### Workarounds

Override vulnerable templates and JavaScript controllers at the project level.

---

#### Step 1 — Override shop breadcrumbs template

`templates/bundles/SyliusShopBundle/shared/breadcrumbs.html.twig`:

```twig
{% macro breadcrumbs(items) %}
    <ol class="breadcrumb" aria-label="breadcrumbs">
        {% for item in items %}
            <li class="breadcrumb-item fw-normal{{ item.active is defined and item.active ? ' active' }}">
                {% if item.path is defined %}
                    <a class="link-reset" href="{{ item.path }}" {{ item.test_attribute is defined ? sylius_test_html_attribute(item.test_attribute) }}>{{ item.label }}</a>
                {% else %}
                    <span class="text-body-tertiary text-break" {{ item.test_attribute is defined ? sylius_test_html_attribute(item.test_attribute) }}>{{ item.label }}</span>
                {% endif %}
            </li>
        {% endfor %}
    </ol>
{% endmacro %}
```

#### Step 2 — Override order breadcrumbs template

`templates/bundles/SyliusShopBundle/account/order/show/content/breadcrumbs.html.twig`:

```twig
{% from '@SyliusShop/shared/breadcrumbs.html.twig' import breadcrumbs as breadcrumbs %}

{% set order = hookable_metadata.context.order %}

<div class="col-12">
    {{ breadcrumbs([
        { label: 'sylius.ui.home'|trans, path: path('sylius_shop_homepage')},
        { label: 'sylius.ui.my_account'|trans, path: path('sylius_shop_account_dashboard')},
        { label: 'sylius.ui.order_history'|trans, path: path('sylius_shop_account_order_index')},
        { label: '#'~order.number, active: true, test_attribute: 'order-number' }
    ]) }}
</div>
```

#### Step 3 — Override ProductTaxonTreeController.js

Disable the vendor controller in `assets/admin/controllers.json`:

```diff
  "produ