GHSA-3h6j-9x8m-rg3g
Graby has stored XSS via iframe srcdoc Attribute in htmLawed Sanitization Config
GHSA-3h6j-9x8m-rg3g is a low vulnerability (CVSS 2.1) in j0k3r/graby, published March 31, 2026. No source lists public exploit code. Fixed in j0k3r/graby 2.5.1.
- Severity
- 2.1 LowCVSS 4.0, GitHub Advisories
- Exploit chance
- UnscoredNo EPSS score yet
- Exploitation
- None knownNot on CISA’s or ENISA’s lists
- Fixed in
2.5.1j0k3r/graby +1 more
Description
Summary
Graby's cleanupXss() function configures htmLawed with conflicting settings: safe=1 (which removes <iframe>) combined with 'elements' => '*+iframe-meta' (which re-enables <iframe>). htmLawed does not sanitize the srcdoc attribute, allowing injection of arbitrary JavaScript that executes when the content is rendered via |raw in templates.
Root Cause
src/Graby.php lines 1038-1048:
htmLawed($html, [
'safe' => 1, // removes <iframe>
'elements' => '*+iframe-meta', // re-adds <iframe>, overrides safe=1
'deny_attribute' => 'style', // srcdoc is NOT denied
]);
The safe=1 and +iframe combination is a conflict: safe mode is designed to strip dangerous elements, but the elements override re-enables <iframe> without also blocking the dangerous srcdoc attribute.
Proof of Concept
Input to cleanupXss():
<iframe srcdoc="<script>alert(document.domain)</script>"></iframe>
Output (unchanged — htmLawed passes it through):
<iframe srcdoc="<script>alert(document.domain)</script>"></iframe>
When 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.
Validated on Wallabag (which uses Graby) via Docker: entry created via API with iframe-only content body triggers Readability failure → falls through to cleanupXss() path.
Impact
- Stored XSS in any application rendering Graby-sanitized content via
|raw - In Wallabag: affects both authenticated views and public share pages (unauthenticated)
- No CSP headers in default Wallabag config — no secondary mitigation
Suggested Fix
Either remove +iframe from the elements config to keep iframes blocked:
'elements' => '*-iframe-meta',
Or explicitly deny the srcdoc attribute:
'deny_attribute' => 'style srcdoc',
Credit
Discovered by @tikket1, 2026-03-25. Redirected from wallabag/wallabag advisory by @j0k3r.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- Passive
What an attacker can affect
- Confidentiality
- None
- Integrity
- None
- Availability
- None
Affected packages
| Package | Affected | Fixed |
|---|---|---|
j0k3r/grabyPackagist | through 2.5.0 | 2.5.1 |
j0k3r/grabyPackagist | before 2.5.1, plus listed versions 1.0.01.0.0-alpha.01.0.0-alpha.11.0.0-alpha.21.0.1 Show 45 moreShow less1.0.21.0.31.0.41.0.51.0.61.0.71.0.81.1.01.10.01.10.11.11.01.11.11.12.01.12.11.13.01.13.11.13.21.13.31.13.41.13.51.13.61.14.01.15.01.15.11.15.21.15.31.15.41.15.51.16.01.17.01.18.01.18.11.19.01.19.11.2.01.20.01.20.11.3.01.4.01.4.11.4.21.4.31.4.41.4.51.5.0 | 2.5.1 |
What each source reports
GitHub Advisories Score used 2.1
Graby has stored XSS via iframe srcdoc Attribute in htmLawed Sanitization Config
Summary
Graby's cleanupXss() function configures htmLawed with conflicting settings: safe=1 (which removes <iframe>) combined with 'elements' => '*+iframe-meta' (which re-enables <iframe>). htmLawed does not sanitize the srcdoc attribute, allowing injection of arbitrary JavaScript that executes when the content is rendered via |raw in templates.
Root Cause
src/Graby.php lines 1038-1048:
htmLawed($html, [
'safe' => 1, // removes <iframe>
'elements' => '*+iframe-meta', // re-adds <iframe>, overrides safe=1
'deny_attribute' => 'style', // srcdoc is NOT denied
]);
The safe=1 and +iframe combination is a conflict: safe mode is designed to strip dangerous elements, but the elements override re-enables <iframe> without also blocking the dangerous srcdoc attribute.
Proof of Concept
Input to cleanupXss():
<iframe srcdoc="<script>alert(document.domain)</script>"></iframe>
Output (unchanged — htmLawed passes it through):
<iframe srcdoc="<script>alert(document.domain)</script>"></iframe>
When 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.
Validated on Wallabag (which uses Graby) via Docker: entry created via API with iframe-only content body triggers Readability failure → falls through to cleanupXss() path.
Impact
- Stored XSS in any application rendering Graby-sanitized content via
|raw - In Wallabag: affects both authenticated views and public share pages (unauthenticated)
- No CSP headers in default Wallabag config — no secondary mitigation
Suggested Fix
Either remove +iframe from the elements config to keep iframes blocked:
'elements' => '*-iframe-meta',
Or explicitly deny the srcdoc attribute:
'deny_attribute' => 'style srcdoc',
Credit
Discovered by @tikket1, 2026-03-25. Redirected from wallabag/wallabag advisory by @j0k3r.
- Record
GHSA-3h6j-9x8m-rg3g- CVSS 4.0
- 2.1 Low
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 - Weakness
- CWE-79
- Updated
- 2026-11-02
OSV 2.1
Graby has stored XSS via iframe srcdoc Attribute in htmLawed Sanitization Config
Summary
Graby's cleanupXss() function configures htmLawed with conflicting settings: safe=1 (which removes <iframe>) combined with 'elements' => '*+iframe-meta' (which re-enables <iframe>). htmLawed does not sanitize the srcdoc attribute, allowing injection of arbitrary JavaScript that executes when the content is rendered via |raw in templates.
Root Cause
src/Graby.php lines 1038-1048:
htmLawed($html, [
'safe' => 1, // removes <iframe>
'elements' => '*+iframe-meta', // re-adds <iframe>, overrides safe=1
'deny_attribute' => 'style', // srcdoc is NOT denied
]);
The safe=1 and +iframe combination is a conflict: safe mode is designed to strip dangerous elements, but the elements override re-enables <iframe> without also blocking the dangerous srcdoc attribute.
Proof of Concept
Input to cleanupXss():
<iframe srcdoc="<script>alert(document.domain)</script>"></iframe>
Output (unchanged — htmLawed passes it through):
<iframe srcdoc="<script>alert(document.domain)</script>"></iframe>
When 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.
Validated on Wallabag (which uses Graby) via Docker: entry created via API with iframe-only content body triggers Readability failure → falls through to cleanupXss() path.
Impact
- Stored XSS in any application rendering Graby-sanitized content via
|raw - In Wallabag: affects both authenticated views and public share pages (unauthenticated)
- No CSP headers in default Wallabag config — no secondary mitigation
Suggested Fix
Either remove +iframe from the elements config to keep iframes blocked:
'elements' => '*-iframe-meta',
Or explicitly deny the srcdoc attribute:
'deny_attribute' => 'style srcdoc',
Credit
Discovered by @tikket1, 2026-03-25. Redirected from wallabag/wallabag advisory by @j0k3r.
- Record
GHSA-3h6j-9x8m-rg3g- CVSS 4.0
- 2.1 Low
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 - Weakness
- CWE-79
- Updated
- 2026-11-02
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | GitHub Advisories, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
References
Left out here: 44 versions. The JSON record has them.