github shopperlabs/shopper v2.10.1
v2.10.1: Media Upload and Customer Access Security

latest release: v3.0.0-beta.3
5 hours ago

Highlights

Stored XSS through media uploads closed (GHSA-vmvv-x5cp-2qv6)

Media is served inline from the same origin as the admin panel, and two upload paths accepted executable content. image/svg+xml was allowlisted for every image collection, and the product files collection had no MIME restriction at all, so a staff member holding only an upload permission such as edit_products could store an HTML or SVG payload served at /storage/.... Opened by an administrator, that payload runs in their session and can mint a new administrator account.

SVG is removed from the default image allowlist, and the product files collection is now restricted to a configurable allowlist of document and archive types. Enforcement is server side through the media library and mirrored on the Filament upload. Severity: High (CVSS 8.7). Reported by @luuhung1217.

IDOR on the customer detail route closed (GHSA-w7mm-g5pr-gpff)

The customer listing scopes results to the customer role, but the detail page loaded any user by id with findOrFail(). A staff member holding only read_customers could open /customers/{id}/show against an administrator or staff account and read their profile, addresses, and orders, bypassing the view_users boundary that protects team management.

The detail lookup now applies the same customers scope, so non customer accounts resolve to a 404. The locked customer property keeps the anonymize action bound to a scoped customer as well. Severity: Medium (CVSS 6.5). Reported by @thanhtung4102.

Dashboard access is now an overridable gate

The dashboard middleware special cased only the administrator role, so the shipped manager role was locked out of the panel by default until an administrator manually granted access_dashboard. The decision now lives in an overridable canAccessDashboard() method on the ShopperUser contract: administrators, managers, and holders of the access_dashboard permission can enter, and host apps can override the rule on their own user model.

Bug Fixes

  • fix(security): block executable uploads in media collections by @mckenziearts in #595
  • fix(security): scope the customer detail route to customers only by @mckenziearts in #594

New Features

  • feat(admin): gate dashboard access behind canAccessDashboard() by @mckenziearts in #593

Upgrading

1. Published media.php config. Stores that published config/shopper/media.php keep their own copy and must update it by hand to receive the upload hardening:

// Remove image/svg+xml from accepts_mime_types
'accepts_mime_types' => [
    'image/jpeg',
    'image/png',
    'image/webp',
    'image/avif',
],

// Add an allowlist for the product files collection
'accepts_file_types' => [
    'application/pdf',
    'application/zip',
    'application/x-rar-compressed',
    'application/x-7z-compressed',
    'application/msword',
    'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    'application/vnd.ms-excel',
    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    'text/plain',
    'text/csv',
],

If you rely on SVG uploads, re add image/svg+xml only after sanitizing SVGs server side.

2. Custom user models implementing ShopperUser directly (without the InteractsWithShopper trait) must add the new contract method:

public function canAccessDashboard(): bool
{
    return $this->isAdmin()
        || $this->isManager()
        || $this->hasPermissionTo('access_dashboard');
}

Models using the InteractsWithShopper trait get this implementation automatically.

Contributors

@mckenziearts, @luuhung1217, @thanhtung4102

Full Changelog: v2.10.0...v2.10.1

Don't miss a new shopper release

NewReleases is sending notifications on new releases.