Severity by source
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Stored XSS requiring low-privilege write access to populate the data source (PR:L); victim must trigger the dropdown (UI:R); scope changes to the victim's browser (S:C).
Primary rating from Vendor (https://github.com/symfony/ux).
CVSS VectorVendor: https://github.com/symfony/ux
Lifecycle Timeline
3DescriptionCVE.org
Description
The Stimulus controller shipped with symfony/ux-autocomplete renders AJAX response items into the dropdown by interpolating the text field directly into HTML template literals (<div>${item[labelField]}</div>) inside _createAutocompleteWithRemoteData(). The value is parsed as HTML rather than text, so any markup contained in the AJAX response is executed by the browser.
When the dropdown values are derived from user-supplied content, an attacker can craft a string that triggers stored XSS in the browser of any other user who later opens a page containing an autocomplete widget backed by the same data.
Resolution
The option and item renderers used in _createAutocompleteWithRemoteData() now use TomSelect's escape helper to HTML-escape the value by default. Endpoints that legitimately return HTML (for example, to highlight the search term) can opt back in to the previous behavior by setting options_as_html: true. The AutocompleteChoiceTypeExtension normalizer that previously forced options_as_html=false when autocomplete_url was set has been dropped so the opt-in is reachable from the form layer.
The patch for this issue is available here for branch 2.x (and forward-ported to 3.x).
Credits
Symfony would like to thank Alex Ashkov for reporting the issue and Hugo Alliaume for providing the fix.
AnalysisAI
Stored cross-site scripting in symfony/ux-autocomplete allows an attacker who can write user-controlled data to an AJAX autocomplete endpoint to execute arbitrary JavaScript in the browser of any user who subsequently loads a page containing an affected autocomplete widget. The Stimulus controller's _createAutocompleteWithRemoteData() function interpolates the text field from AJAX responses directly into HTML template literals without sanitization, causing the browser to parse and execute embedded markup. The vulnerability affects composer package versions 2.2.0-2.36.0 and 3.0.0-3.1.0; a vendor-released patch is available, and no active exploitation has been confirmed (not in CISA KEV).
Technical ContextAI
The affected code resides in the Stimulus JavaScript controller bundled with the symfony/ux-autocomplete Composer package (pkg:composer/symfony_ux-autocomplete). The controller integrates TomSelect for dropdown UI rendering and uses _createAutocompleteWithRemoteData() to populate options from a remote endpoint. In vulnerable versions, the option and item renderer callbacks interpolate the text field from AJAX JSON responses directly into HTML template literals (<div>${item[labelField]}</div>), causing the browser's HTML parser to treat the string as markup rather than text content. This is a textbook CWE-79 (Improper Neutralization of Input During Web Page Generation) instance: untrusted data enters an HTML context without encoding. Because the payload persists in the application's data store before rendering (stored XSS), it executes for every subsequent victim who triggers the dropdown. The fix applies TomSelect's built-in escape() HTML-encoding helper to the text field before interpolation, with an explicit opt-in path (options_as_html: true) for endpoints that legitimately return HTML such as search-term highlighting.
RemediationAI
Upgrade symfony/ux-autocomplete to version 2.36.0 (for 2.x installations) or 3.1.0 (for 3.x installations) as confirmed by the vendor advisory at https://github.com/symfony/ux/security/advisories/GHSA-mwqm-4fw3-cjvr; the underlying fix is available at commit https://github.com/symfony/ux/commit/842ae54bc74de389299f975f01aafae272cb0019. After upgrading, AJAX response text fields are HTML-escaped by default. Applications that intentionally return HTML in the text field (e.g., to bold matched search terms) must explicitly set options_as_html: true in their form type configuration to restore the previous rendering behavior - omitting this setting after upgrade causes raw HTML to display as escaped text rather than rendered markup, which is a visual regression but not a security issue. If an immediate upgrade is not feasible, a compensating control is to sanitize or strip HTML from all values stored in data sources that feed AJAX autocomplete endpoints at the persistence layer; this prevents malicious payloads from entering the data store, though it is less reliable than the vendor patch.
Same weakness CWE-79 – Cross-site Scripting (XSS)
View allShare
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-45216
GHSA-mwqm-4fw3-cjvr