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. |
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.