Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐘 Packagist

GHSA-3h6j-9x8m-rg3g

GHSA-3h6j-9x8m-rg3g is a security vulnerability in j0k3r/graby. O3 Security confirms whether GHSA-3h6j-9x8m-rg3g is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.

Graby has stored XSS via iframe srcdoc Attribute in htmLawed Sanitization Config

Published
Mar 31, 2026
Updated
Mar 31, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed

Blast Radius

1 pkg affected
🐘j0k3r/graby

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Packagist packages — download data is not available via public APIs for these ecosystems.

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="&lt;script&gt;alert(document.domain)&lt;/script&gt;"></iframe>

Output (unchanged — htmLawed passes it through):

<iframe srcdoc="&lt;script&gt;alert(document.domain)&lt;/script&gt;"></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.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐘Packagistj0k3r/grabyall versions2.5.1

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for j0k3r/graby. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.

  2. Fix

    Update j0k3r/graby to 2.5.1 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-3h6j-9x8m-rg3g is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 pinpoints whether GHSA-3h6j-9x8m-rg3g is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.

Tailored to GHSA-3h6j-9x8m-rg3g. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

## 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:** ```php htmLawed($html, [ 'safe' => 1, // removes <iframe> 'elements' => '*+iframe-meta', // re-adds <iframe>, overrides safe=1 'deny_attribute' => 's
O3 Security · Impact-Aware SCA

Is GHSA-3h6j-9x8m-rg3g in your dependencies?

O3 detects GHSA-3h6j-9x8m-rg3g across Packagist dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.