PackageTrack
Sign in Get early access

awcodes/filament-curator

A media picker plugin for FilamentPHP.

v5.3.2 409K downloads/mo #4308 most downloaded on Packagist awcodes/filament-curator

What this package is like to depend on

Last release 9 days ago

14 Aug 2026

Ships fairly regularly

a new release about every 2 weeks

Rarely documented

notes for 10 of 240 stable releases

Nothing withdrawn

no release was ever pulled

4 years old

261 releases · first in 2022

47 releases in the last 12 months

see the full history below

Release timeline

261 releases · Apr 2022 to Aug 2026
2023 2024 2025 2026
Release Pre-release

Releases

latest 60 of 261
  1. v5.3.2 14 Aug 2026
    Release notes

    Security release

    Fixes an SVG sanitization bypass and unvalidated curation paths. See GHSA-3xm3-q2fj-x8rq for the full advisory.

    • SVG sanitization could be bypassed by renaming the file. Sanitization was gated on the client-supplied filename extension while acceptance and the served content type were decided from the file's contents, so SVG markup uploaded as payload.txt was stored unsanitized. Sanitizing now keys off the detected type as well as the extension, in both the uploader and CuratorUtils::importMedia, and the serving layer pins Content-Type from the stored extension instead of the sniffed bytes.
    • The curation modal wrote to a client-supplied path. saveCuration() consumed the crop payload unvalidated, so a key such as ../../other could overwrite a sibling file inside the storage disk. The payload is now validated before anything is written.
    • curator:sanitize-svgs now selects on the detected type as well as the extension, so rows stored under a spoofed filename are no longer invisible to it.

    After upgrading

    Re-scan stored media to clean up anything already on disk:

    php artisan curator:sanitize-svgs --dry-run   # report only
    php artisan curator:sanitize-svgs

    The serving-layer change already prevents affected rows from rendering as documents, so this is cleanup rather than the primary fix.

    Also in this release

    • Removed the unused exif form view. It was not referenced anywhere in the package and could not render as shipped, since it requested an Alpine component that is never registered. If you published views with vendor:publish --tag=curator-views, your local copy is untouched by this removal and still contains a raw {!! !!} echo of EXIF metadata — escape or delete it.

    Reported by Afsana Alijabarova (@afa114).

    Open source →
    Release notes

    v5.3.2 Latest

    Latest

    Compare

    Choose a tag to compare

    Open source →
  2. v5.3.1 14 Aug 2026
    Release notes

    What's Changed

    • fix: align Media docblock with the nullable migration columns by @awcodes in #722
    • fix: resolve string media keys in the glider component by @awcodes in #723

    Full Changelog: v5.3.0...v5.3.1

    Open source →
    Release notes

    v5.3.1

    Compare

    Choose a tag to compare

    Open source →
  3. v5.3.0 12 Aug 2026
    Release notes

    What's Changed

    • Support Filament 4 and 5 from a single branch by @awcodes in #720

    Full Changelog: v5.2.0...v5.3.0

    Open source →
    Release notes

    v5.3.0

    Compare

    Choose a tag to compare

    Open source →
  4. v5.2.0 08 Aug 2026
    Release notes

    Added

    The media picker's search now matches each term on its own instead of looking for the whole input as a single substring. A file named my-image.png could not previously be found by typing "my image".

    The search is split on whitespace, hyphens and underscores, and every term has to match — though any of the five searchable columns (name, title, alt, caption, description) may be the one matching it. So my-image.png is now found by "my image", "my_image", "my-image", or "image my", while my-document.png stays out of those results.

    LIKE wildcards typed into the search are now escaped, so searching for 100% looks for a literal percent sign rather than matching everything. Worth knowing that _ has always been an unescaped single-character wildcard, so photo_8-sunset matched photo-8-sunset by accident while a hyphen did nothing; that inconsistency is gone.

    Case sensitivity is still left to the database — Postgres LIKE is case-sensitive where MySQL and SQLite are not. That is unchanged from previous versions.

    Thanks to @amywestlake for reporting this and proposing the original approach in #623.

    Fixed

    Search results are now ordered by the panel's sort direction, matching the unfiltered list. There was no ORDER BY at all before, so the 50-row limit truncated an arbitrary slice of matches.

    GliderFallback's setters were typed to require a value, while every property except the name is optional and every getter already returned null. Building a fallback from a conditional expression — the usual reason to have one — threw a TypeError in a service provider before the application could boot:

    GliderFallback::make('logo')
        ->alt(config('app.name'))
        ->source(filled(setting('logo')) ? $logo->url : Vite::image('logo.webp'))

    alt(), height(), source(), type() and width() now all accept null.

    A registered fallback that ends up with no source still cannot be rendered, but it now names itself — The [logo] glider fallback does not have a source. — instead of reporting the media item as invalid, which pointed at the wrong thing.

    Thanks again to @battulga0719 for reporting this in #718.

    Upgrading

    No changes are required for ordinary use. Two notes:

    • If you extended GliderFallback and overrode any of the setters with the narrower string/int signature, widen it to ?string/?int to match the parent.
    • Typing 0 into the picker search previously reset the list to show everything. It now searches for "0"; whitespace-only input is what falls back to the unfiltered list.
    Open source →
    Release notes

    v5.2.0

    Compare

    Choose a tag to compare

    Open source →
  5. v5.1.5 05 Aug 2026
    Release notes

    Security

    Curator's upload components fell back to a default list of accepted file types that included text/html, application/xhtml+xml, text/javascript, application/xml and application/octet-stream. An authenticated user with permission to upload media could upload an HTML file containing a <script> tag. The file was stored unmodified and later served from the application's own origin, executing the script with the session of whoever opened it.

    This affected the Media resource form, MultiUploadAction, CuratorPicker and the rich editor's attachment flow, which all share the same default. Applications that already set acceptedFileTypes() themselves — globally or per field — were never affected, because the vulnerable list was only used as a fallback.

    The default is now MimeType::defaults(): the full list minus types that are effectively executable content. MimeType::toArray() is unchanged, so code referencing the enum directly still works. As defense in depth, media served through Curator's own route now sends X-Content-Type-Options: nosniff, and restricted types are forced to Content-Disposition: attachment.

    If your application genuinely needs to host these types, opt back in explicitly:

    use Awcodes\Curator\Enums\MimeType;
    
    Curator::acceptedFileTypes([...MimeType::defaults(), 'text/html']);

    Note that Curator only sanitizes SVG uploads. Anything else you allow is stored and served verbatim, and with the default public disk it is also reachable through the storage symlink, where the new response headers do not apply. Serve deliberately-allowed executable types from a private disk.

    The 3.x line is not affected — its accepted types come from config('curator.accepted_file_types'), whose default has always been limited to images and PDFs.

    Fixed

    Glider fallbacks could not be used at all. Several faults compounded:

    • GliderFallback::make() passed the name through the container, but the class has no constructor, so Laravel discarded it. The name stayed null and getName() threw a TypeError, meaning a fallback built exactly as documented failed before it could be registered.
    • The <x-curator-glider> component rejected a null media item, which is the main reason to configure a fallback in the first place. It now accepts null.
    • handleInt() checked the raw id rather than the looked-up record, so the fallback branch never ran and a missing record produced "Attempt to read property path on null" instead. The documented <x-curator-glider :media="1" fallback="thumbnail"/> could not work.

    Fallback resolution now happens in one place, so a null media item, an id that does not resolve, and a blank string all reach it. An unregistered fallback name, or one with no source, no longer dereferences null.

    Also fixed while in there:

    • GliderFallback's optional getters were typed non-nullable while every property defaults to null, so a partially configured fallback threw. They are nullable now.
    • GliderFallback::isPreviewable() called Curator::isResizable(), reporting svg sources as not previewable.

    Thanks to @battulga0719 for reporting the fallback issues in #717.

    Open source →
    Release notes

    v5.1.5

    Compare

    Choose a tag to compare

    Open source →
  6. v5.1.4 25 Jul 2026
    Release notes

    Fixed

    • The media picker breadcrumb's root Disk label and the Selected Files heading in the selection modal were hardcoded in English and ignored the active locale. Both now resolve through the translation layer. The breadcrumb reuses the existing curator::views.details.disk key, so it is already translated in every shipped locale.

    Localization

    Adds a new attach_curator_media.modal.selected_files key under resources/lang/en/views.php. Other locales fall back to English until translated — contributions welcome.

    Thanks to @Elgorm for reporting and fixing this in #716.

    Open source →
    Release notes

    v5.1.4

    Compare

    Choose a tag to compare

    Open source →
  7. v5.1.3 25 Jul 2026

    Nothing published for this version

  8. v5.1.2 06 Jul 2026

    Nothing published for this version

  9. v5.1.1 30 Jun 2026

    Nothing published for this version

  10. v5.1.0 28 Jun 2026

    Nothing published for this version

  11. v5.0.8 08 Jun 2026

    Nothing published for this version

  12. v5.0.7 13 Apr 2026

    Nothing published for this version

  13. v5.0.6 26 Mar 2026

    Nothing published for this version

  14. v5.0.5 13 Mar 2026

    Nothing published for this version

  15. v5.0.4 09 Mar 2026

    Nothing published for this version

  16. v5.0.3 05 Mar 2026

    Nothing published for this version

  17. v5.0.2 28 Jan 2026

    Nothing published for this version

  18. v5.0.1 24 Jan 2026

    Nothing published for this version

  19. v5.0.0 19 Jan 2026

    Nothing published for this version

  20. v4.2.1 14 Aug 2026
    Release notes

    Security release

    Fixes an SVG sanitization bypass and unvalidated curation paths. See GHSA-3xm3-q2fj-x8rq for the full advisory.

    • SVG sanitization could be bypassed by renaming the file. Sanitization was gated on the client-supplied filename extension while acceptance and the served content type were decided from the file's contents, so SVG markup uploaded as payload.txt was stored unsanitized. Sanitizing now keys off the detected type as well as the extension, in both the uploader and CuratorUtils::importMedia, and the serving layer pins Content-Type from the stored extension instead of the sniffed bytes.
    • The curation modal wrote to a client-supplied path. saveCuration() consumed the crop payload unvalidated, so a key such as ../../other could overwrite a sibling file inside the storage disk. The payload is now validated before anything is written.
    • curator:sanitize-svgs now selects on the detected type as well as the extension, so rows stored under a spoofed filename are no longer invisible to it.

    After upgrading

    Re-scan stored media to clean up anything already on disk:

    php artisan curator:sanitize-svgs --dry-run   # report only
    php artisan curator:sanitize-svgs

    The serving-layer change already prevents affected rows from rendering as documents, so this is cleanup rather than the primary fix.

    Also in this release

    • Removed the unused exif form view. It was not referenced anywhere in the package and could not render as shipped, since it requested an Alpine component that is never registered. If you published views with vendor:publish --tag=curator-views, your local copy is untouched by this removal and still contains a raw {!! !!} echo of EXIF metadata — escape or delete it.

    Reported by Afsana Alijabarova (@afa114).

    Open source →
    Release notes

    v4.2.1

    Compare

    Choose a tag to compare

    Open source →
  21. v4.2.0 08 Aug 2026
    Release notes

    Added

    The media picker's search now matches each term on its own instead of looking for the whole input as a single substring. A file named my-image.png could not previously be found by typing "my image".

    The search is split on whitespace, hyphens and underscores, and every term has to match — though any of the five searchable columns (name, title, alt, caption, description) may be the one matching it. So my-image.png is now found by "my image", "my_image", "my-image", or "image my", while my-document.png stays out of those results.

    LIKE wildcards typed into the search are now escaped, so searching for 100% looks for a literal percent sign rather than matching everything. Worth knowing that _ has always been an unescaped single-character wildcard, so photo_8-sunset matched photo-8-sunset by accident while a hyphen did nothing; that inconsistency is gone.

    Case sensitivity is still left to the database — Postgres LIKE is case-sensitive where MySQL and SQLite are not. That is unchanged from previous versions.

    Thanks to @amywestlake for reporting this and proposing the original approach in #623.

    Fixed

    Search results are now ordered by the panel's sort direction, matching the unfiltered list. There was no ORDER BY at all before, so the 50-row limit truncated an arbitrary slice of matches.

    GliderFallback's setters were typed to require a value, while every property except the name is optional and every getter already returned null. Building a fallback from a conditional expression — the usual reason to have one — threw a TypeError in a service provider before the application could boot:

    GliderFallback::make('logo')
        ->alt(config('app.name'))
        ->source(filled(setting('logo')) ? $logo->url : Vite::image('logo.webp'))

    alt(), height(), source(), type() and width() now all accept null.

    A registered fallback that ends up with no source still cannot be rendered, but it now names itself — The [logo] glider fallback does not have a source. — instead of reporting the media item as invalid, which pointed at the wrong thing.

    Thanks again to @battulga0719 for reporting this in #718.

    Upgrading

    No changes are required for ordinary use. Two notes:

    • If you extended GliderFallback and overrode any of the setters with the narrower string/int signature, widen it to ?string/?int to match the parent.
    • Typing 0 into the picker search previously reset the list to show everything. It now searches for "0"; whitespace-only input is what falls back to the unfiltered list.
    Open source →
    Release notes

    v4.2.0

    Compare

    Choose a tag to compare

    Open source →
  22. v4.1.5 05 Aug 2026
    Release notes

    Security

    Curator's upload components fell back to a default list of accepted file types that included text/html, application/xhtml+xml, text/javascript, application/xml and application/octet-stream. An authenticated user with permission to upload media could upload an HTML file containing a <script> tag. The file was stored unmodified and later served from the application's own origin, executing the script with the session of whoever opened it.

    This affected the Media resource form, MultiUploadAction, CuratorPicker and the rich editor's attachment flow, which all share the same default. Applications that already set acceptedFileTypes() themselves — globally or per field — were never affected, because the vulnerable list was only used as a fallback.

    The default is now MimeType::defaults(): the full list minus types that are effectively executable content. MimeType::toArray() is unchanged, so code referencing the enum directly still works. As defense in depth, media served through Curator's own route now sends X-Content-Type-Options: nosniff, and restricted types are forced to Content-Disposition: attachment.

    If your application genuinely needs to host these types, opt back in explicitly:

    use Awcodes\Curator\Enums\MimeType;
    
    Curator::acceptedFileTypes([...MimeType::defaults(), 'text/html']);

    Note that Curator only sanitizes SVG uploads. Anything else you allow is stored and served verbatim, and with the default public disk it is also reachable through the storage symlink, where the new response headers do not apply. Serve deliberately-allowed executable types from a private disk.

    The 3.x line is not affected — its accepted types come from config('curator.accepted_file_types'), whose default has always been limited to images and PDFs.

    Fixed

    Glider fallbacks could not be used at all. Several faults compounded:

    • GliderFallback::make() passed the name through the container, but the class has no constructor, so Laravel discarded it. The name stayed null and getName() threw a TypeError, meaning a fallback built exactly as documented failed before it could be registered.
    • The <x-curator-glider> component rejected a null media item, which is the main reason to configure a fallback in the first place. It now accepts null.
    • handleInt() checked the raw id rather than the looked-up record, so the fallback branch never ran and a missing record produced "Attempt to read property path on null" instead. The documented <x-curator-glider :media="1" fallback="thumbnail"/> could not work.

    Fallback resolution now happens in one place, so a null media item, an id that does not resolve, and a blank string all reach it. An unregistered fallback name, or one with no source, no longer dereferences null.

    Also fixed while in there:

    • GliderFallback's optional getters were typed non-nullable while every property defaults to null, so a partially configured fallback threw. They are nullable now.
    • GliderFallback::isPreviewable() called Curator::isResizable(), reporting svg sources as not previewable.

    Thanks to @battulga0719 for reporting the fallback issues in #717.

    Open source →
    Release notes

    v4.1.5

    Compare

    Choose a tag to compare

    Open source →
  23. v4.1.4 25 Jul 2026

    Nothing published for this version

  24. v4.1.3 25 Jul 2026

    Nothing published for this version

  25. v4.1.2 06 Jul 2026

    Nothing published for this version

  26. v4.1.1 30 Jun 2026

    Nothing published for this version

  27. v4.1.0 28 Jun 2026

    Nothing published for this version

  28. v4.0.8 08 Jun 2026

    Nothing published for this version

  29. v4.0.7 13 Apr 2026

    Nothing published for this version

  30. v4.0.6 26 Mar 2026

    Nothing published for this version

  31. v4.0.5 13 Mar 2026

    Nothing published for this version

  32. v4.0.4 05 Mar 2026

    Nothing published for this version

  33. v4.0.3 28 Jan 2026

    Nothing published for this version

  34. v4.0.2 25 Jan 2026

    Nothing published for this version

  35. v4.0.1 24 Jan 2026

    Nothing published for this version

  36. v4.0.0 22 Dec 2025

    Nothing published for this version

  37. v4.0.0-alpha.7 22 Dec 2025 pre-release

    Nothing published for this version

  38. v4.0.0-alpha.6 14 Dec 2025 pre-release

    Nothing published for this version

  39. v4.0.0-alpha.5 12 Dec 2025 pre-release

    Nothing published for this version

  40. v4.0.0-alpha.4 12 Dec 2025 pre-release

    Nothing published for this version

  41. v4.0.0-alpha.3 25 Nov 2025 pre-release

    Nothing published for this version

  42. v4.0.0-alpha.2 13 Nov 2025 pre-release

    Nothing published for this version

  43. v4.0.0-alpha.1 28 Oct 2025 pre-release

    Nothing published for this version

  44. v3.7.11 14 Aug 2026
    Release notes

    Security release

    See GHSA-3xm3-q2fj-x8rq for the full advisory.

    • Media streamed from disk was typed by sniffing its contents. SVG markup stored under another extension came back as image/svg+xml under an inline disposition and could render as a document in the application's origin. The serving layer now pins Content-Type from the stored extension, forces a download when the extension is unknown and the sniff would render as a document, and sends X-Content-Type-Options: nosniff throughout.
    • The curation modal wrote to a client-supplied path. saveCuration() consumed the crop payload unvalidated, so a key such as ../../other could overwrite a sibling file inside the storage disk. The payload is now validated before anything is written.
    • curator:sanitize-svgs could never finish on larger libraries. Its two selection criteria were or'd at the top level, so chunkById's and id > ? bound to only one of them and every type-matched row was handed back on each pass. Any library with more than 100 SVG records looped until the command was killed. If a scan appeared to hang on an earlier version, re-run it.

    The upload-side half of the advisory does not affect 3.x: its uploader already decided whether to sanitize from the detected type rather than the filename.

    After upgrading

    php artisan curator:sanitize-svgs --dry-run   # report only
    php artisan curator:sanitize-svgs

    Reported by Afsana Alijabarova (@afa114).

    Open source →
    Release notes

    v3.7.11

    Compare

    Choose a tag to compare

    Open source →
  45. v3.7.10 25 Jul 2026

    Nothing published for this version

  46. v3.7.9 06 Jul 2026

    Nothing published for this version

  47. v3.7.8 30 Jun 2026

    Nothing published for this version

  48. v3.7.7 06 Jun 2025

    Nothing published for this version

  49. v3.7.6 29 May 2025

    Nothing published for this version

  50. v3.7.5 25 May 2025

    Nothing published for this version

  51. v3.7.4 19 May 2025

    Nothing published for this version

  52. v3.7.3 22 Mar 2025

    Nothing published for this version

  53. v3.7.2 01 Mar 2025

    Nothing published for this version

  54. v3.7.1 28 Feb 2025

    Nothing published for this version

  55. v3.7.0 27 Feb 2025

    Nothing published for this version

  56. v3.6.18 25 Feb 2025

    Nothing published for this version

  57. v3.6.15 28 Dec 2024

    Nothing published for this version

  58. v3.6.14 03 Dec 2024

    Nothing published for this version

  59. v3.6.13 26 Nov 2024

    Nothing published for this version

  60. v3.6.12 11 Nov 2024

    Nothing published for this version

Every package, every release, already written down.

The archive is open and free. Watching your own project is what we are building next.

Browse the archive