Stop unknown plugin icon names from 500ing every page - #486
Draft
simonhamp wants to merge 2 commits into
Draft
Conversation
plugins.icon_name is free text typed by developers, but was only validated for shape, never for whether the Heroicon actually exists. Views rendered it via <x-dynamic-component :component="'heroicon-o-' . $plugin->icon_name" />, which throws InvalidArgumentException at component resolution when the name is unknown — taking down the whole page. "image" (should be "photo") and "location" (should be "map-pin") were both saved in production. Add Plugin::getIconComponent(), which resolves the name through the blade-icons factory and falls back to heroicon-o-cube, and use it at all ten render sites (plugin card, public listing, cart, Ultra index, team page, purchased plugins, developer plugin page). This recovers the rows already broken in production. Also reject non-existent icon names in Show::updateIcon() so no new bad data lands, and point the input hint at heroicons.com instead of inviting guesses. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The messages() relation already sorts with oldest(), so the test's
latest('id') landed as a secondary key: `order by created_at asc, id desc`.
That returns the *earliest* message, not the newest. Laravel stores
timestamps at second precision, so the test only passed when the admin
message and the developer reply happened to land in the same second — it
went red on CI the moment they straddled a second boundary.
Order the relation by id, which is monotonic and unique, so the same
chronological order holds with ties defined. Clear the inherited sort in the
test with reorder(), and travel a second before replying so the cross-second
case is always exercised rather than hidden by equal timestamps.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes Nightwatch issue #53 —
InvalidArgumentException: Unable to locate a class or view for component [heroicon-o-image].Root cause
plugins.icon_nameis free text typed by developers — the "Heroicon name" input on the plugin Icon card.Show::updateIcon()only validated its shape (/^[a-z0-9-]+$/), never whether the icon actually exists. Views then rendered it as:Blade resolves that alias at component-resolution time, so an unknown name throws
InvalidArgumentExceptionand 500s the entire page. The two names in the reports simply aren't Heroicons — it'sphoto, notimage, andmap-pin, notlocation. Once saved, a single bad row poisons every page that renders that plugin.The blast radius was wider than the reported route: 10 render sites across 7 files, including the public plugin directory card, the public plugin listing, the cart, the Ultra index, the team page, and purchased plugins.
Changes
Plugin::getIconComponent()resolves the stored name through the blade-icons factory and falls back toheroicon-o-cubewhen it doesn't exist. All 10 call sites now use it. This is what recovers the rows already broken in production.Show::updateIcon()now rejects names that aren't real Heroicons, with a message pointing at heroicons.com.photo/map-pinas examples.The shape regex runs before the factory lookup, which also blocks the
str_replace('.', '/')path traversal in blade-icons' file resolution (covered by a test).Verification
New
tests/Feature/PluginIconFallbackTest.phpcovers name validation, the render fallback, the four affected page types, and both validation branches on save.I reverted the view changes and confirmed the tests reproduce the reported error verbatim —
Unable to locate a class or view for component [heroicon-o-image].— then restored and confirmed green. 82 tests across the plugin/cart/ultra/team suites pass. Pint clean.Notes for review
php artisan testwith a broad--filterOOMs at the default 128M CLI memory limit (dies in a FilamentGridDirection.php). Not touched here.🤖 Generated with Claude Code