{"id":"CVE-2026-42842","aliases":["GHSA-c2q3-p4jr-c55f"],"url":"https://o3.security/vulnerability/CVE-2026-42842","summary":"grav-plugin-form: XSS via Taxonomy Field Values in Admin Panel","details":"### Summary\n\nA Stored Cross-Site Scripting (XSS) vulnerability exists in the Grav CMS Form plugin's select field template. Taxonomy tag and category values are rendered with the Twig `|raw` filter in the admin panel, bypassing the global autoescape protection. An editor-level user can inject arbitrary JavaScript that executes in any administrator's browser session when they view or edit any page in the admin panel.\n\nAdditionally, Grav's built-in XSS detection (`Security::detectXss()`) can be bypassed by using payloads that close the `<option>/<select>` context and use unquoted event handlers - the `on_events` regex fails to match event handlers without quotes or trailing spaces before `>`.\n\n### Important\n\n- The vulnerability is in the Form plugin (`select.html.twig`), which is installed by default with Grav\n- The XSS is cross-page: a malicious taxonomy value on one page executes when an admin edits any page, because taxonomy options are rendered from a shared global pool\n- An editor can exploit this without any other vulnerability - taxonomy fields are not in the server-side restricted fields list\n- The `HttpOnly` flag on session cookies prevents direct session theft, but the XSS can steal the admin nonce and perform privileged actions via JavaScript\n\n### Permissions Needed\n\n- Editor: can create or edit pages and set taxonomy tag/category values\n\n### Details\n\nThe Form plugin's select field template renders option values using the `|raw` Twig filter, which outputs content without HTML escaping:\n\nFile: `user/plugins/form/templates/forms/fields/select/select.html.twig`\n\n```twig\n{# Line 55 #}\n avalue|raw \n\n{# Line 65 #}\n suboption|t|raw \n\n{# Line 72 #}\n item_value|t|raw \n```\n\nThe taxonomy field in the page editor uses this select template. When a page has taxonomy values (tags, categories), these values are populated as `<option>` elements in the select dropdown. The `value` attribute is properly escaped by the browser's attribute encoding, but the **display text** between `<option>` tags is rendered raw:\n\n```html\n<option value=\"&lt;script&gt;alert(1)&lt;/script&gt;\"><script>alert(1)</script></option>\n```\n\nSince taxonomy options are collected globally across all pages (to provide autocomplete/selection), a malicious taxonomy value on any page will appear in the taxonomy dropdown of every page editor - making this a cross-page stored XSS.\n\nThe server-side field restriction in the flex-objects plugin only blocks `['form', 'forms', 'process', 'twig']` for non-super users. Taxonomy fields are not restricted, so editors can freely set arbitrary taxonomy values.\n\n### XSS Detection Bypass\n\nGrav's `Security::detectXss()` checks for `dangerous_tags` (e.g., `<script>`, `<iframe>`), `on_events` (event handlers), and `invalid_protocols` (e.g., `javascript:`). However, the `on_events` regex:\n\n```php\n'on_events' => '#(<[^>]+[a-z\\x00-\\x20\"\\'\\/)(?:on[a-z]+)\\s*=[\\s|\\'\"'].*[\\s|\\'\"']>#iUu'\n```\n\nrequires either quotes around the handler value or a trailing space before `>`. An unquoted handler like `onerror=alert(1)>` (no space before `>`) bypasses this check entirely.\n\nCombined with `</option></select>` to break out of the select context (neither tag is in `dangerous_tags`), the full payload evades all three detection layers and triggers no XSS warning in the admin panel.\n\n### PoC\n\n#### Step 1: Login as Editor\nNavigate to `http://TARGET/admin/` and authenticate with editor credentials.\n\n#### Step 2: Create a Page with Malicious Taxonomy\n- Go to Pages → Add → Add Page\n- Title: `XSS via editor`\n- Go to **Options** Tap\n- On Taxonomies, Add tag:\n```\n</option></select><img src=x onerror=alert('XSS-via-editor')>\n```\n\nThis payload:\n- Closes `</option></select>` to break out of the select dropdown context\n- Injects an `<img>` tag with an unquoted `onerror` handler (bypasses `on_events` regex)\n- Is not in the `dangerous_tags` list (no `<script>`, `<iframe>`, etc.)\n- Triggers no XSS warning in the admin panel\n\n<img width=\"1221\" height=\"857\" alt=\"image\" src=\"https://github.com/user-attachments/assets/6223cbb2-f04b-46bd-89ce-828c89ad77ab\" />\n\n#### Step 3: Trigger the XSS\nWhen any administrator navigates to the page editor of any page (not just the malicious one), the JavaScript executes immediately.\n\n<img width=\"1224\" height=\"856\" alt=\"image\" src=\"https://github.com/user-attachments/assets/f008b0f2-dedb-4b22-a74a-cdc0d7325cb4\" />\n\nThe XSS fires because taxonomy tag options are collected globally across all pages and rendered with `|raw` in the select dropdown template. The payload breaks out of the `<option>` context, and the browser renders the `<img>` tag as a regular DOM element.\n\n### Impact\n\n- Session hijacking: While `HttpOnly` prevents direct cookie theft, the XSS can steal the admin nonce token and perform any admin action via AJAX requests\n- Privilege escalation: An editor can perform admin-only actions (create users, modify system configuration, install plugins) through the hijacked admin session\n- Cross-page impact: A single malicious taxonomy value affects the entire admin panel - every page editor view is compromised\n\n\n---\n\n## Maintainer note — fix applied (2026-04-24)\n\nFixed across two repos:\n\n1. **grav-plugin-form 9.0.1** (commit [`6bffb4c`](https://github.com/getgrav/grav-plugin-form/commit/6bffb4c)) — the primary fix. All four `|raw` filters in [`templates/forms/fields/select/select.html.twig`](https://github.com/getgrav/grav-plugin-form/blob/develop/templates/forms/fields/select/select.html.twig) (placeholder, avalue, suboption, item_value) have been removed. Option labels — including taxonomy values that propagate cross-page through the admin's shared selection pool — now go through Twig's default escaper, so a lower-privileged editor can no longer inject script that runs in an admin's browser when they open any page editor.\n\n2. **Grav core on the `2.0` branch** (commit [`5a12f9be8`](https://github.com/getgrav/grav/commit/5a12f9be8), ships in **2.0.0-beta.2**) — closes the detection-bypass half of the report. The `on_events` regex in `Security::detectXss()` is tightened so unquoted handlers like `onerror=alert(1)>` are flagged (see separate GHSA-9695-8fr9-hw5q), and `option`/`select` have been added to default `security.xss_dangerous_tags` so `</option></select>…` tripwires the detector (see separate GHSA-w8cg-7jcj-4vv2).\n\nSites running admin2 on Grav 2.0.0-beta.2 get the 9.0.1 form plugin automatically via its existing dependency graph.\n\n**Files:**\n- [`templates/forms/fields/select/select.html.twig`](https://github.com/getgrav/grav-plugin-form/blob/develop/templates/forms/fields/select/select.html.twig) — four `|raw` removed.\n- [`system/config/security.yaml`](https://github.com/getgrav/grav/blob/2.0/system/config/security.yaml) — dangerous-tags list extended.\n- [`system/src/Grav/Common/Security.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Common/Security.php) — `on_events` regex tightened.\n- [`tests/unit/Grav/Common/Security/DetectXssTest.php`](https://github.com/getgrav/grav/blob/2.0/tests/unit/Grav/Common/Security/DetectXssTest.php) — includes the GHSA-c2q3 PoC payload.","published":"2026-05-11T15:29:36.490Z","modified":"2026-10-28T02:30:49.814956032Z","cvss":{"score":5.4,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N"},"epss":{"score":0.0015,"percentile":0.04559,"asOf":"2026-09-16"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"Packagist","name":"getgrav/grav","fixedVersion":"2.0.0-beta.2"}],"fix":{"url":"https://github.com/getgrav/grav-plugin-form/commit/6bffb4c98be468a155d1656544ec45bb4a443957","label":"getgrav/grav-plugin-form@6bffb4c"},"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/42xxx/CVE-2026-42842.json"},{"type":"ADVISORY","url":"https://github.com/getgrav/grav/security/advisories/GHSA-c2q3-p4jr-c55f"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-42842"},{"type":"FIX","url":"https://github.com/getgrav/grav-plugin-form/commit/6bffb4c98be468a155d1656544ec45bb4a443957"},{"type":"WEB","url":"https://github.com/getgrav/grav/commit/5a12f9be8314682c8713e569e330f11805d0a663"},{"type":"PACKAGE","url":"https://github.com/getgrav/grav"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-10-28T02:30:49.814956032Z"}}