Security Release
This release patches multiple authorization bypasses in the admin Livewire surface and a long-standing race condition on Discount.usage_limit reported against v2.7.3. Upgrading is strongly recommended for every Shopper install.
Two GitHub Security Advisories are published alongside this tag, disclosed responsibly by @baradika:
- Authorization bypass in multiple Livewire admin components (CWE-862 / CWE-285)
- Race condition on
Discount.usage_limitallows silent over-redemption (CWE-362 / CWE-840)
Security Fixes
- fix(security): require
edit_orderson allOrder/DetailandOrder/ShipmentsFilament actions (cancel, startProcessing, markPaid, markComplete, capturePayment, archive, markDelivered, edit) by @mckenziearts in #511 - fix(security): require
edit_productsinsideProducts/Form/{Edit,Inventory,Seo,Shipping,Files}::store()by @mckenziearts in #511 - fix(security): require
edit_ordersonOrderNotes::leaveNotes()by @mckenziearts in #511 - fix(security): require
access_settingonSettings/Team/Index::createRoleandDeleteActionby @mckenziearts in #511 - fix(security): require
access_settingonSettings/Team/RolePermissionwrite actions by @mckenziearts in #511 - fix(security): require
access_settingonPaymentMethods,Currencies,CarriersToggleColumn, record actions and bulk actions by @mckenziearts in #511 - fix(security): require correct permission on
Locations/InventoryForm::store()andLegal/PolicyForm::store()sub-components by @mckenziearts in #511 - fix(security): lock public Eloquent properties with
#[Locked]on every Livewire component exposing a model (Order, OrderShipping, Discount, Review, ShopperUser, Role, Inventory, Legal, Product) by @mckenziearts in #511 - fix(security): switch product barcode
TextInputtoregex('/^[A-Za-z0-9\-]*$/')to close stored XSS on the admin barcode renderer by @mckenziearts in #511 - fix(security): drop
_passwordhidden-field leak inCustomers/Create::store()(replaceArr::exceptwith explicitArr::onlyallow-list) by @mckenziearts in #511 - fix(security): guard
Settings/Team/Permissions::togglePermissionandremovePermissionagainst nullPermissionlookups by @mckenziearts in #511
Bug Fixes
- fix(cart): reserve the discount slot atomically before order creation in
CreateOrderFromCartAction, fixing silent over-redemption whenusage_limitwas reached between cart validation and commit (issue #510) by @mckenziearts in #511 - fix(cart): enforce
usage_limit_per_userby counting prior orders on the neworders.discount_idcolumn instead of the never-incrementedDiscountDetail.total_usecounter by @mckenziearts in #511 - fix(cart): surface the per-user limit at cart-apply time via
DiscountValidatorso the rejection no longer surprises the customer at checkout by @mckenziearts in #511
Improvements
- refactor(admin): migrate Filament
Actionhandlers from$this->authorize()inside the closure to->authorize('ability')chained on the action, so forbidden actions are hidden in the UI instead of throwing on click by @mckenziearts in #511
Upgrading
This release includes one additive migration. Run after pulling:
composer update shopper/admin shopper/cart shopper/core
php artisan migrateThe migration adds the following columns to the orders table:
discount_id— nullable foreign key referencingdiscounts.id(nullOnDelete)discount_code,discount_type,discount_value_at_apply,discount_currency_code— snapshot fields preserved when the originating discount is later edited or deleted
All columns are nullable. Existing rows are left untouched.
If you have custom Livewire components extending Shopper's order/product/settings surfaces, review them for the same authorization pattern: every ->action(...) handler should either chain ->authorize('ability') on the Filament Action, or call $this->authorize('ability') on the first line of a Livewire method.
Credits
Authorization bypasses and the discount race condition were reported privately and responsibly by Fase Rais Baradika.
Full Changelog: v2.7.3...v2.8.0