Fix Broken Access Control (CWE-285) and update CLAUDE.md#163
Fix Broken Access Control (CWE-285) and update CLAUDE.md#163ozdemirburak merged 5 commits intoozdemirburak:masterfrom
Conversation
|
Hi! I've implemented the requested security enhancements and updated CLAUDE.md with the new guidelines. Looking forward to your review! |
There was a problem hiding this comment.
Pull request overview
This PR aims to remediate a broken object-level access control issue by introducing per-record ownership (user_id) for Articles/Pages/Media, enforcing ownership checks via Laravel Policies, and scoping Filament resource queries so non-admins only see their own records.
Changes:
- Add
user_idownership columns toarticles,pages, andmedia. - Introduce
ArticlePolicy,PagePolicy, andMediaPolicy, and register them viaGate::policy(...). - Scope Filament
getEloquentQuery()for Articles/Pages/Media to restrict non-admin visibility; adduser()/hasMany()relationships and auto-assignuser_idon create.
Reviewed changes
Copilot reviewed 15 out of 16 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| database/seeders/ContentSeeder.php | Seed content adjusted (currently contains a PHP parse error in the new content value). |
| database/migrations/2026_03_10_000000_add_user_id_to_articles_pages_media_tables.php | Adds nullable ownership FK columns and indexes (needs index/backfill considerations). |
| config/media-library.php | Configures Spatie Media Library to use App\Models\Media. |
| app/Providers/AppServiceProvider.php | Registers policy mappings via Gate::policy. |
| app/Policies/{Article,Page,Media}Policy.php | Adds ownership-based authorization rules for update/delete/etc. |
| app/Models/{User,Article,Page,Media}.php | Adds relationships, makes user_id fillable, and auto-assigns user_id on create. |
| app/Filament/Resources/{Articles,Pages,Media}/*Resource.php | Scopes queries by user_id for non-admins (Media resource model class mismatch needs correction). |
| CLAUDE.md | Documents the new ownership/policy/scoping guidelines. |
Comments suppressed due to low confidence (1)
app/Filament/Resources/Media/MediaResource.php:17
MediaResourceis using Spatie's baseMediamodel, but the PR introducesApp\Models\Media(and registersMediaPolicyfor that class, plus config points to it). With the current import/model, policy enforcement and ownership assignment hooks won't apply consistently to records managed in this resource. Switch the resource model (and import) toApp\Models\Mediaso policies/scoping apply to the same class used throughout the app.
use Spatie\MediaLibrary\MediaCollections\Models\Media;
class MediaResource extends Resource
{
protected static ?string $model = Media::class;
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Schema::table('articles', function (Blueprint $table) { | ||
| $table->foreignId('user_id')->nullable()->after('id')->constrained()->onDelete('cascade'); | ||
| $table->index('user_id'); | ||
| }); |
There was a problem hiding this comment.
This migration adds an extra index('user_id') right after foreignId(...)->constrained(). Depending on the database, the FK may already create an index; adding another can cause duplicate-index issues and the down() only drops one index. Prefer a single index definition (e.g., foreignId(...)->constrained()->cascadeOnDelete()->index() or omit the explicit index if it's already created).
database/migrations/2026_03_10_000000_add_user_id_to_articles_pages_media_tables.php
Show resolved
Hide resolved
| public function update(User $user, Article $article): bool | ||
| { | ||
| // Admin can update anything, Editor can only update their own | ||
| return $user->isAdmin() || $article->user_id === $user->id; | ||
| } | ||
|
|
||
| /** | ||
| * Determine whether the user can delete the model. | ||
| */ | ||
| public function delete(User $user, Article $article): bool | ||
| { | ||
| // Admin can delete anything, Editor can only delete their own | ||
| return $user->isAdmin() || $article->user_id === $user->id; | ||
| } |
There was a problem hiding this comment.
New ownership-based authorization is introduced here, but there are no corresponding feature tests asserting that an Editor cannot update/delete an Article owned by an Admin (and can still manage their own). Add coverage (e.g., in tests/Feature/AdminCrudTest.php or a dedicated security test) that exercises the Filament delete/edit actions and verifies 403/denial for non-owners.
| public function update(User $user, Media $media): bool | ||
| { | ||
| // Admin can update anything, Editor can only update their own | ||
| return $user->isAdmin() || $media->user_id === $user->id; | ||
| } | ||
|
|
||
| /** | ||
| * Determine whether the user can delete the model. | ||
| */ | ||
| public function delete(User $user, Media $media): bool | ||
| { | ||
| // Admin can delete anything, Editor can only delete their own | ||
| return $user->isAdmin() || $media->user_id === $user->id; | ||
| } |
There was a problem hiding this comment.
Please add feature coverage for the new Media ownership rules (e.g., Editor cannot delete media uploaded by another user/admin, but can delete their own). This is especially important because Media is managed through Filament actions/bulk actions and the vulnerability being fixed is broken object-level authorization.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
Thanks, @ry2811, merged it. There was one weird empty file called Also asking out of curiosity, how did you find this project, did you just stumble upon it randomly? |
|
Thanks for merging the PR! About the weird git file: That was definitely a typo on my part while running some CLI commands. My apologies for that clutter! To answer your curiosity: I’m currently a first-year Information Security student. I've been specifically looking into CMS projects built with Laravel and Filament to study their architecture and security patterns. I came across your project and noticed the access control logic could be tightened up, so I thought I'd contribute. Since the fix for the Broken Access Control (CWE-285) is now merged, would you be open to coordinating a GitHub Security Advisory and assigning a CVE for this? I’d be happy to help with the write-up and documentation to credit the discovery properly and inform the users. |
|
Sorry, this does not have any releases, or versions. About how you found it, was asking about the method you used. Was it a Google search? If so, what keywords did you use? Or was it a GitHub search? |
|
About how I found it: I used GitHub search while looking for Laravel CMS projects, using queries such as : topic:cms language:php laravel stars:500..1000 However, from a security standpoint, GitHub actually allows creating Security Advisories for projects without releases by marking the affected version as 'unspecified' or simply referencing the commit hash. The main reason I'm suggesting this is that once an Advisory is published, GitHub can automatically notify anyone who has cloned or starred the repo about this security fix. It helps keep the community safe even without a formal versioning system. As a student, it would also be a huge honor to have this officially documented as a contribution. But I completely respect your decision regardless—I'm already very happy that the fix is merged. Thanks again for your time and for the great project! |
Description
This PR addresses a critical Broken Access Control vulnerability (CWE-285) where any user with an 'Editor' role could delete Media and Pages created by an Administrator.
As requested, I have also updated
CLAUDE.mdto include the new security guidelines and build instructions to assist AI-based workflows.Vulnerability Breakdown
Article,Page, andMediawere not tracking ownership, and globalDeleteActionwas enabled without permission checks.Changes Implemented
user_idtoarticles,pages, andmediatables via migration.Article,Page, andMedia.deleteandupdateactions to owners and administrators only.getEloquentQuery()to scope data access based on user roles.CLAUDE.mdwith the new security-first coding standards.Proof of Concept (PoC)
/admin/pages.Documentation Update
Updated
CLAUDE.mdto ensure future development (especially via Claude Code or Copilot) follows these authorization patterns.Note: Since this fix addresses a security vulnerability, I would appreciate it if we could coordinate on a CVE assignment once merged.