{"id":"GHSA-3h6j-9x8m-rg3g","summary":"Graby has stored XSS via iframe srcdoc Attribute in htmLawed Sanitization Config","details":"## Summary\n\nGraby's `cleanupXss()` function configures htmLawed with conflicting settings: `safe=1` (which removes `\u003ciframe\u003e`) combined with `'elements' =\u003e '*+iframe-meta'` (which re-enables `\u003ciframe\u003e`). htmLawed does not sanitize the `srcdoc` attribute, allowing injection of arbitrary JavaScript that executes when the content is rendered via `|raw` in templates.\n\n## Root Cause\n\n**`src/Graby.php` lines 1038-1048:**\n```php\nhtmLawed($html, [\n    'safe' =\u003e 1,                    // removes \u003ciframe\u003e\n    'elements' =\u003e '*+iframe-meta',  // re-adds \u003ciframe\u003e, overrides safe=1\n    'deny_attribute' =\u003e 'style',    // srcdoc is NOT denied\n]);\n```\n\nThe `safe=1` and `+iframe` combination is a conflict: `safe` mode is designed to strip dangerous elements, but the elements override re-enables `\u003ciframe\u003e` without also blocking the dangerous `srcdoc` attribute.\n\n## Proof of Concept\n\nInput to `cleanupXss()`:\n```html\n\u003ciframe srcdoc=\"&lt;script&gt;alert(document.domain)&lt;/script&gt;\"\u003e\u003c/iframe\u003e\n```\n\nOutput (unchanged — htmLawed passes it through):\n```html\n\u003ciframe srcdoc=\"&lt;script&gt;alert(document.domain)&lt;/script&gt;\"\u003e\u003c/iframe\u003e\n```\n\nWhen rendered via `{{ content|raw }}` in a template, `srcdoc` executes in an `about:srcdoc` frame with the same origin as the page. **Confirmed via Puppeteer/Chromium headless: `alert(document.domain)` fires.**\n\nValidated on Wallabag (which uses Graby) via Docker: entry created via API with iframe-only content body triggers Readability failure → falls through to `cleanupXss()` path.\n\n## Impact\n\n- Stored XSS in any application rendering Graby-sanitized content via `|raw`\n- In Wallabag: affects both authenticated views and public share pages (unauthenticated)\n- No CSP headers in default Wallabag config — no secondary mitigation\n\n## Suggested Fix\n\nEither remove `+iframe` from the elements config to keep iframes blocked:\n```php\n'elements' =\u003e '*-iframe-meta',\n```\n\nOr explicitly deny the `srcdoc` attribute:\n```php\n'deny_attribute' =\u003e 'style srcdoc',\n```\n\n## Credit\n\nDiscovered by @tikket1, 2026-03-25. Redirected from wallabag/wallabag advisory by @j0k3r.","modified":"2026-03-31T23:32:26.906898Z","published":"2026-03-31T23:12:36Z","database_specific":{"severity":"LOW","github_reviewed":true,"github_reviewed_at":"2026-03-31T23:12:36Z","nvd_published_at":null,"cwe_ids":["CWE-79"]},"references":[{"type":"WEB","url":"https://github.com/j0k3r/graby/security/advisories/GHSA-3h6j-9x8m-rg3g"},{"type":"WEB","url":"https://github.com/j0k3r/graby/commit/0295d828822f7a59c5751a8199973a4f965a99b0"},{"type":"PACKAGE","url":"https://github.com/j0k3r/graby"},{"type":"WEB","url":"https://github.com/j0k3r/graby/releases/tag/2.5.1"}],"affected":[{"package":{"name":"j0k3r/graby","ecosystem":"Packagist","purl":"pkg:composer/j0k3r/graby"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.5.1"}]}],"versions":["1.0.0","1.0.0-alpha.0","1.0.0-alpha.1","1.0.0-alpha.2","1.0.1","1.0.2","1.0.3","1.0.4","1.0.5","1.0.6","1.0.7","1.0.8","1.1.0","1.10.0","1.10.1","1.11.0","1.11.1","1.12.0","1.12.1","1.13.0","1.13.1","1.13.2","1.13.3","1.13.4","1.13.5","1.13.6","1.14.0","1.15.0","1.15.1","1.15.2","1.15.3","1.15.4","1.15.5","1.16.0","1.17.0","1.18.0","1.18.1","1.19.0","1.19.1","1.2.0","1.20.0","1.20.1","1.3.0","1.4.0","1.4.1","1.4.2","1.4.3","1.4.4","1.4.5","1.5.0","1.5.1","1.5.2","1.5.3","1.5.4","1.6.0","1.6.1","1.6.2","1.7.0","1.7.1","1.8.0","1.8.1","1.8.2","1.9.0","1.9.1","1.9.2","1.9.3","2.0.0","2.0.0-alpha.0","2.0.1","2.0.2","2.1.0","2.1.1","2.2.0","2.2.1","2.2.4","2.2.5","2.2.6","2.2.7","2.3.0","2.3.1","2.3.2","2.3.3","2.3.4","2.3.5","2.4.0","2.4.1","2.4.2","2.4.3","2.4.4","2.4.5","2.4.6","v2.2.2","v2.2.3","v2.5.0"],"database_specific":{"last_known_affected_version_range":"\u003c= 2.5.0","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-3h6j-9x8m-rg3g/GHSA-3h6j-9x8m-rg3g.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V4","score":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N/E:P"}]}