# Craft CMS: Missing peer-permission check in `AssetsController::actionDeleteFolder` allows deletion of other users' assets

Craft CMS delete-folder endpoint checks only deleteAssets, not deletePeerAssets, letting low-privilege users cascade-delete other users' assets on shared volumes.

- Payout: Research
- Severity: high
- Program: Craft CMS
- Found by: offset
- Disclosed: 2026-07-02
- CVE: CVE-2026-50284
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-7h62-6v23-v8fm
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-7h62-6v23-v8fm
- Tags: area:web domain:web impact:dos impact:integrity kind:research platform:ghsa sev:high tech:php technique:incomplete-fix technique:source-review vuln:access-control vuln:auth-bypass vuln:business-logic

**Target:** AssetsController::actionDeleteFolder JSON endpoint
**Root cause:** actionDeleteFolder only calls requireVolumePermissionByFolder('deleteAssets'), while Assets::deleteFoldersByIds cascades deleteElement() over all descendant assets without invoking Asset::canDelete(), so the deletePeerAssets check is never evaluated.
**Steps:**
1. Authenticate as a user with deleteAssets:<volume-uid> but not deletePeerAssets on a shared volume.
2. POST to the delete-folder action with `folderId` of a folder containing peer-owned assets.
3. Observe only `deleteAssets` permission is checked; `deleteFoldersByIds($folderId)` deletes folder, descendants and all contained assets regardless of uploader.
**How found:** Noticed the sibling actionMoveFolder was just hardened in commit 05c2042 with deletePeerAssets/savePeerAssets folder checks, then compared actionDeleteFolder and actionDeleteAsset to find the asymmetric missing peer check.
**Exploitation:** Any user granted folder-management rights on a shared volume sends a single JSON request with the target folderId; the cascade deletes peer-owned files and subfolders from the filesystem with no UI recovery.
**Insight:** When a peer-permission fix lands on one folder endpoint, audit every sibling endpoint that reaches the same cascade service (deleteFoldersByIds) — per-asset canDelete() checks are bypassed by bulk service paths.

