CVE-2026-55890

MEDIUMPre-NVD 4.84.8
EchelonGraph scoreLOW confidence

This medium-severity CVE scores 4.8 under the CNA's CVSS (NVD's own analysis pending). EPSS exploit-prediction score not yet available (the EPSS model rescores nightly; freshly-published CVEs typically appear within 48 hours). GitHub Security Advisory data not yet ingested — confidence will rise once GHSA publishes (typical lag: hours to days for open-source ecosystem CVEs; never for infrastructure-only CVEs).

Triggered by: NVD CVSS baseline
Sources: cna:github_m
4.8
EchelonGraph verdictMonitorLow exploitation likelihood right now — keep watching.
  • Lower severity and no public exploit yet
CISA-KEV: Not listedEPSS: CVSS: 4.8Exploit: NoneExposed: 0

No vendor fix yet — apply a workaround or compensating control (WAF / firewall / segmentation) and watch for a patch.

Grav: Stored CSS injection via Markdown image ?style=… reaches MediaObjectTrait::style() — incomplete patch of GHSA-r7fx-8g49-7hhr

Summary

The fix for GHSA-r7fx-8g49-7hhr / CVE-2026-42841 (Stored XSS via Markdown media attribute() action) is incomplete. The maintainer patched MediaObjectTrait::attribute() to deny dangerous attribute names (event handlers, style, xmlns, srcdoc, formaction) but the sibling MediaObjectTrait::style() method is reachable through the same Markdown excerpt-action pipeline and writes editor-controlled strings straight into the rendered ` attribute with no sanitization.

Any user with admin.pages permission (e.g. an editor) can save Markdown like:

!logo

which renders to a stored-CSS payload that any higher-privileged viewer (administrator, super-admin, reviewer) loads in their authenticated session. Same trust boundary, same victim, same attacker, same Markdown input vector as the patched GHSA-r7fx-8g49-7hhr issue — the fix simply patched the attribute() entry point and missed the style() sibling.

Affected versions

Vulnerable at HEAD across every currently-shipping branch (verified 2026-06-15):

| Branch / tag | MediaObjectTrait::style() | |---|---| | develop (f4c0f42) | unpatched | | 2.0 (96e1d2d) | unpatched | | 2.0.0-rc.8 (latest 2.0 RC tag) | unpatched | | 1.7.52 (latest 1.7 stable) | unpatched |

Per SECURITY.md, this advisory targets the 2.0 line (publisher-level exploit, not eligible for 1.7 backport per the project's stated policy).

Trust boundary

Per the project's SECURITY.md:

> A vulnerability is when an actor can escape the trust scope of their role: a publisher whose stored content compromises an admin session, an unauthenticated visitor who reaches a privileged sink, an account at any tier that gains capabilities it was not granted.

An editor authoring Markdown is operating within their role. A higher-privilege admin loading that editor's page in their authenticated session and getting attacker-controlled CSS painted into their browser is across the trust boundary — the same framing that was accepted for GHSA-r7fx-8g49-7hhr (MODERATE) and GHSA-c2q3-p4jr-c55f (MODERATE).

Details

Original GHSA-r7fx-8g49-7hhr fix (commit 5a12f9be8, 2026-04-23)

public function attribute($attribute = null, $value = '')
{
    if (empty($attribute) || !is_string($attribute)) {
        return $this;
    }
    if (!self::isSafeAttributeName($attribute)) {
        return $this;
    }
    $this->attributes[$attribute] = $value;
    return $this;
}

private static function isSafeAttributeName(string $name): bool { if (!preg_match('/^[A-Za-z][A-Za-z0-9_:.\-]*$/', $name)) { return false; } $lower = strtolower($name); if (str_starts_with($lower, 'on')) { // event handlers return false; } $denylist = ['style', 'xmlns', 'srcdoc', 'formaction']; return !in_array($lower, $denylist, true); }

style is the second-named entry on the denylist — the maintainer explicitly recognised that editor-supplied style was dangerous when arriving via the attribute() action. The fix simply didn't reach the parallel sink.

The unpatched sibling: MediaObjectTrait::style() (line 519)

/**
 * Allows to add an inline style attribute from Markdown or Twig
 * Example: !Example
 */
public function style($style)
{
    $this->styleAttributes[] = rtrim($style, ';') . ';';
    return $this;
}

The function is unchanged before, during, and after the GHSA-r7fx-8g49-7hhr fix. The PHPDoc on the very next line names the Markdown invocation form (?style=…). The rtrim is for clean concatenation, not security.

$styleAttributes is concatenated and assigned to attributes['style'] in parsedownElement() (lines 242–251):

$style = '';
foreach ($this->styleAttributes as $key => $value) {
    if (is_numeric($key)) {        // editor-supplied entries are numeric-keyed
        $style .= $value;
    } else {
        $style .= $key . ': ' . $value . ';';
    }
}
if ($style) {
    $attributes['style'] = $style;
}

Parsedown then runs htmlspecialchars on the value (so quote-breakout into a new attribute is blocked), but arbitrary CSS as the value is enough.

Source → sink trace

The Markdown processor wires query-string keys to method calls on the Medium object (system/src/Grav/Common/Page/Markdown/Excerpts.php:262):

foreach ($actions as $action) {
    $matches = [];
    if (preg_match('/\[(.*)\]/', (string) $action['params'], $matches)) {
        $args = [explode(',', $matches[1])];
    } else {
        $args = explode(',', (string) $action['params']);
    }
    $medium = call_user_func_array([$medium, $action['method']], $args);
}

?style=position:fixed;top:0;left:0 becomes $medium->style('position:fixed;top:0;left:0').

Save-side XSS detector misses the payload

AdminController::savePage() runs Security::detectXssFromArray() on data[content] before persisting (classes/plugin/AdminController.php:1402). All five default patterns miss the Markdown form:

  • on_events: requires <…on*= in source.
  • invalid_protocols: requires javascript:/data:/etc. — the phishing-overlay payload uses none.
  • moz_binding: requires -moz-binding: literally.
  • html_inline_styles: requires <…style=…(url:|x:expression); Markdown source has no < and no url:.
  • dangerous_tags: requires carrying the unsanitised CSS.

Suggested fix

Apply the same denylist + identifier-shape gate to style() that isSafeAttributeName() enforces for attribute():

public function style($style)
 {
+    if (!is_string($style) || !self::isSafeStyleValue($style)) {
+        return $this;
+    }
     $this->styleAttributes[] = rtrim($style, ';') . ';';
     return $this;
 }

+/** + * Editor-controlled style values arrive via Markdown ?style=… and reach + * the rendered attribute verbatim. Limit to a conservative + * set of CSS that themes legitimately use from content (sizing, float, + * margin, etc.) and reject anything that opens a phishing-overlay or + * data-exfil primitive. Matches the spirit of the attribute() denylist + * from GHSA-r7fx-8g49-7hhr — same trust boundary, sibling sink. + */ +private static function isSafeStyleValue(string $css): bool +{ + $css = strtolower($css); + // Deny: phishing-overlay positioning, CSS-selector exfil sinks + // (background/content url(...)), expression() (legacy IE), + // -moz-binding (legacy FF), behavior: url() (IE). + $deny = ['position:', '@import', 'url(', 'expression(', + '-moz-binding', 'behavior:', 'z-index:', 'fixed', 'absolute']; + foreach ($deny as $needle) { + if (str_contains($css, $needle)) { + return false; + } + } + return (bool) preg_match('/^[A-Za-z0-9 :;%.,\-#\/]*$/', $css); +}

Alternatively, deprecate the Markdown ?style=… action entirely — themes can still set inline styles from PHP, but accepting attacker-controlled CSS from page content was always a footgun.

Defense in depth: extend Security::detectXss()'s html_inline_styles rule to also match Markdown-form ?style= query parameters in data[content] on save.

References

  • Original advisory: GHSA-r7fx-8g49-7hhr
  • Fix commit: 5a12f9be8 (system/src/Grav/Common/Media/Traits/MediaObjectTrait.php)
  • Unpatched code: system/src/Grav/Common/Media/Traits/MediaObjectTrait.php lines 519–524
  • Project security policy: SECURITY.md` (trust-boundary severity model)

CVSS v3
4.8
EG Score
4.8(low)
EPSS
KEV
Not listed

Published

June 18, 2026

Last Modified

June 18, 2026

Vendor Advisories for CVE-2026-55890(1)

These vendors published their own advisory mentioning this CVE — often with vendor-specific remediation steps + affected product lists not in NVD.

Affected Packages

(1 across 1 ecosystem)
Packagist(1)
PackageVulnerable rangeFixed inDependents
getgrav/grav0.8.0 ... 2.0.0-rc.8 (329 versions)2.0.0-rc.9

Data Freshness Timeline

(refreshed 8× in last 7d / 21× in last 30d)

Each row is a source pipeline that fetched or updated this CVE on that date, with what changed. For example, "NVD update" means NVD published or revised its analysis for this CVE; "MITRE cvelistV5" means we ingested or refreshed it from the CNA feed. Most recent first.

  1. 2026-07-06 21:39 UTCEG score recompute
  2. 2026-07-05 23:11 UTCEG score recompute
  3. 2026-07-05 01:19 UTCEG score recompute
  4. 2026-07-04 03:23 UTCEG score recompute
  5. 2026-07-03 04:34 UTCEG score recompute
  6. 2026-07-02 06:37 UTCEG score recompute
  7. 2026-07-01 08:49 UTCEG score recompute
  8. 2026-06-30 11:00 UTCEG score recompute
  9. 2026-06-29 13:00 UTCEG score recompute
  10. 2026-06-28 15:11 UTCEG score recompute
  11. 2026-06-27 17:21 UTCEG score recompute
  12. 2026-06-26 19:33 UTCEG score recompute
  13. 2026-06-25 21:45 UTCEG score recompute
  14. 2026-06-24 23:56 UTCEG score recompute
  15. 2026-06-24 02:08 UTCEG score recompute
  16. 2026-06-23 04:20 UTCEG score recompute
  17. 2026-06-22 06:29 UTCEG score recompute
  18. 2026-06-21 08:38 UTCEG score recompute
  19. 2026-06-20 10:49 UTCEG score recompute
  20. 2026-06-19 13:00 UTCEG score recompute
  21. 2026-06-18 15:08 UTCEG score recompute

Frequently asked(4)

What is CVE-2026-55890?
CVE-2026-55890 is a medium vulnerability published on June 18, 2026. Grav: Stored CSS injection via Markdown image ?style=… reaches MediaObjectTrait::style() — incomplete patch of GHSA-r7fx-8g49-7hhr Summary The fix for GHSA-r7fx-8g49-7hhr / CVE-2026-42841 (Stored XSS via Markdown media attribute() action) is incomplete. The maintainer patched…
When was CVE-2026-55890 disclosed?
CVE-2026-55890 was first published in the National Vulnerability Database on June 18, 2026. EchelonGraph re-ingests CVE updates from NVD on a 2-hour cycle, so this page reflects the latest published state.
What is the CVSS score of CVE-2026-55890?
CVE-2026-55890 has a CVSS v4.0 base score of 4.8 (CNA self-assessment; NVD's own analysis pending). The EG score is currently aggregating — additional source signals are being incorporated as they become available..
How do I remediate CVE-2026-55890?
Patch to the fixed version published by the affected vendor. Where vendor advisories exist for CVE-2026-55890, EchelonGraph cross-links them in the Vendor Advisories panel below — those typically contain the canonical remediation steps, fixed version numbers, and any vendor-specific mitigations.

Dependency Blast Radius

See which npm, PyPI, Go, and Maven packages are affected by CVE-2026-55890

Explore →

Is Your Infrastructure Affected by CVE-2026-55890?

EchelonGraph automatically scans your cloud infrastructure and maps CVE exposure using blast radius analysis.