VYPR
Moderate severityNVD Advisory· Published Aug 19, 2025· Updated Aug 20, 2025

CVE-2025-51487

CVE-2025-51487

Description

A Stored Cross-Site Scripting (XSS) vulnerability exists in MoonShine version < 3.12.5, allowing to execute arbitrary JavaScript by using "javascript:" payload, instead of the expected HTTPS protocol, in the CutCode Link parameter when creating/updating a new Article.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Stored XSS in MoonShine < 3.12.5 allows arbitrary JavaScript execution via 'javascript:' payload in the CutCode Link parameter.

A stored cross-site scripting (XSS) vulnerability exists in MoonShine versions prior to 3.12.5. The flaw occurs in the CutCode Link parameter when creating or updating an Article. Instead of validating the protocol, the application accepts URIs starting with 'javascript:', allowing attackers to insert arbitrary JavaScript code that gets stored and executed later [1][2].

Exploitation requires an authenticated attacker with privileges to create or update articles. By entering a payload such as javascript:alert("XSS") into the CutCode field and saving the article, the malicious script is stored. When a victim views the article and clicks on the link, the script executes in their browser. This is a stored XSS that triggers upon user interaction [2].

Successful exploitation allows arbitrary JavaScript execution in the context of the victim's session, potentially leading to cookie theft, session hijacking, or unauthorized actions on behalf of the user. The CVSS 3.1 score for this vulnerability is 4.5 (Medium) [2].

The vendor addressed the issue in MoonShine version 3.12.5 by escaping output values to prevent XSS [4]. Users are strongly advised to upgrade to this or a later version. No workarounds have been provided.

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.

PackageAffected versionsPatched versions
moonshine/moonshinePackagist
< 3.12.43.12.4

Affected products

2

Patches

1
f108f4ea5c0d

fix: Security issue

https://github.com/moonshine-software/moonshineDanil ShutskyMay 14, 2025via ghsa
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

3

News mentions

0

No linked articles in our index yet.