CVE-2025-51488
Description
A Stored Cross-Site Scripting (XSS) vulnerability exists in MoonShine version < 3.12.4, allowing remote attackers to store and execute arbitrary JavaScript by including a malicious HTML payload in the Name parameter when creating a new Admin.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Stored XSS in MoonShine < 3.12.4 allows attackers to inject arbitrary JavaScript via the Name parameter when creating an Admin.
A Stored Cross-Site Scripting (XSS) vulnerability exists in MoonShine versions prior to 3.12.4. The root cause is insufficient sanitization of the Name parameter during admin user creation, allowing arbitrary HTML to be persisted. [1]
An attacker with admin privileges (PR:H) can craft a payload such as `` and submit it via the Name field. The payload is stored and executed when any user views a page that renders the admin name, with no user interaction required (UI:N). [2]
The stored script executes in the context of the victim's session, potentially leading to unauthorized actions or data theft. The CVSS 3.1 score is 4.9 (Medium) with high confidentiality impact. [1][2]
The issue is fixed in MoonShine 3.12.4, confirmed by the commit that escapes output using WithEscapedValue trait. Users should upgrade immediately. [2][4]
AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
moonshine/moonshinePackagist | < 3.12.14 | 3.12.14 |
Affected products
2- MoonShine/MoonShinedescription
Patches
1f108f4ea5c0dfix: Security issue
6 files changed · +41 −12
src/Laravel/src/Fields/Relationships/BelongsTo.php+8 −2 modified@@ -18,6 +18,7 @@ use MoonShine\UI\Traits\Fields\HasPlaceholder; use MoonShine\UI\Traits\Fields\Searchable; use MoonShine\UI\Traits\Fields\WithDefaultValue; +use MoonShine\UI\Traits\Fields\WithEscapedValue; use Throwable; /** @@ -37,6 +38,7 @@ class BelongsTo extends ModelRelationField implements use WithDefaultValue; use HasPlaceholder; use BelongsToOrManyCreatable; + use WithEscapedValue; protected string $view = 'moonshine::fields.relationships.belongs-to'; @@ -50,7 +52,9 @@ class BelongsTo extends ModelRelationField implements protected function resolvePreview(): string { if (! $this->getResource()->hasAnyAction(Action::VIEW, Action::UPDATE)) { - return parent::resolvePreview(); + return $this->isUnescape() + ? parent::resolvePreview() + : $this->escapeValue((string) parent::resolvePreview()); } if (! $this->hasLink() && $this->toValue()) { @@ -68,7 +72,9 @@ protected function resolvePreview(): string ); } - return parent::resolvePreview(); + return $this->isUnescape() + ? parent::resolvePreview() + : $this->escapeValue((string) parent::resolvePreview()); } protected function resolveValue(): mixed
src/Laravel/src/Fields/Relationships/MorphTo.php+5 −1 modified@@ -143,11 +143,15 @@ protected function resolvePreview(): string $column = $this->getSearchColumn($value::class); $type = $item->{$this->getMorphType()}; - return str($this->types[$type] ?? $type) + $preview = str($this->types[$type] ?? $type) ->append('(') ->append(data_get($value, $column)) ->append(')') ->value(); + + return $this->isUnescape() + ? $preview + : $this->escapeValue($preview); } public function getTypeValue(): string
src/Support/src/DTOs/Select/Option.php+10 −2 modified@@ -18,12 +18,20 @@ public function __construct( public function getLabel(): string { - return $this->label; + return htmlspecialchars( + $this->label, + ENT_QUOTES | ENT_SUBSTITUTE, + 'UTF-8', + ); } public function getValue(): string { - return $this->value; + return htmlspecialchars( + $this->value, + ENT_QUOTES | ENT_SUBSTITUTE, + 'UTF-8', + ); } public function isSelected(): bool
src/UI/src/Components/Link.php+5 −1 modified@@ -73,7 +73,11 @@ public function getView(): string protected function prepareBeforeRender(): void { $this->customAttributes([ - 'href' => value($this->href, $this), + 'href' => htmlspecialchars( + value($this->href, $this), + ENT_QUOTES | ENT_SUBSTITUTE, + 'UTF-8', + ), ]); }
src/UI/src/Fields/Url.php+8 −5 modified@@ -7,7 +7,7 @@ use Closure; use Illuminate\Contracts\Support\Renderable; use MoonShine\Support\Enums\TextWrap; -use MoonShine\UI\Components\Url as UrlComponent; +use MoonShine\UI\Components\Link; class Url extends Text { @@ -36,6 +36,7 @@ public function blank(): static protected function resolvePreview(): Renderable|string { $value = $this->toFormattedValue() ?? ''; + $title = $this->isUnescape() ? $value : $this->escapeValue($value); @@ -44,12 +45,14 @@ protected function resolvePreview(): Renderable|string return ''; } - return UrlComponent::make( + return Link::make( href: $value, - value: \is_null($this->titleCallback) + label: \is_null($this->titleCallback) ? $title : (string) \call_user_func($this->titleCallback, $title, $this), - blank: $this->blank - )->render(); + )->when( + $this->blank, + fn (Link $ctx): Link => $ctx->blank() + )->icon('link')->render(); } }
src/UI/src/Traits/Fields/WithLink.php+5 −1 modified@@ -27,7 +27,11 @@ public function hasLink(): bool public function getLinkValue(mixed $value = null): string { - return value($this->linkValue, $value ?? $this->toValue(withDefault: false), $this); + return htmlspecialchars( + value($this->linkValue, $value ?? $this->toValue(withDefault: false), $this), + ENT_QUOTES | ENT_SUBSTITUTE, + 'UTF-8', + ); } public function getLinkName(mixed $value = null): string
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
3News mentions
0No linked articles in our index yet.