• Mahmoud Aglan's avatar
    fix(cache): stop refusing to restore the objects we cache on purpose · 905ffefc
    Mahmoud Aglan authored
    Every branded page on the live tenant answered 500 with
    "BrandingService::for(): Return value must be of type BrandProfile,
    __PHP_Incomplete_Class returned".
    
    config/cache.php shipped Laravel's default serializable_classes => false,
    which unserializes cache values with allowed_classes: false. That is safe for
    an app that caches only scalars and arrays, and fatal for one that does not.
    We cache whole value objects deliberately: BrandProfile is the entire tenant
    brand, resolved once and held until branding changes, and read by every admin,
    portal and print layout. With classes refused it came back as
    __PHP_Incomplete_Class, the return type threw, and the admin went dark.
    
    The setting exists to stop a gadget chain in a cache an attacker can already
    write to. Ours is the tenant's own Postgres, reachable only by the app;
    anyone who can write there can do worse directly. CACHE_SERIALIZABLE_CLASSES
    lets a deployment pass its own allowlist without a code change.
    
    BrandingService now also checks what the cache handed back before trusting it,
    and rebuilds when it is not a profile. A cache that cannot return this class
    should cost a rebuild per request, never a 500 — the failure has to degrade,
    not detonate.
    
    Verified: every admin screen and the group roster render against a restored
    copy of the live tenant with CACHE_STORE=database.
    Co-Authored-By: 's avatarClaude Opus 5 (1M context) <noreply@anthropic.com>
    905ffefc
Name
Last commit
Last update
..
BrandingService.php Loading commit data...
PlatformFeeService.php Loading commit data...
ProrationService.php Loading commit data...
ReportService.php Loading commit data...
ServiceRequestService.php Loading commit data...
SettingsService.php Loading commit data...