Every CVE Search/ Get API key

CVE-2026-50284

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

7.1 HighFix available

Craft CMS is a content management system (CMS). In versions 5.0.0-RC1 through 5.9.21 and 4.0.0-RC1 through 4.17.14, theAssetsController::actionDeleteFolder() only requires the deleteAssets:<volume-uid> permission for the target folder. It never enforces deletePeerAssets:<volume-uid>, even though Assets::deleteFoldersByIds() cascades deletion to every descendant folder and every asset inside, regardless of the uploader's assigned privileges. A low-privilege user who has been granted folder-management rights on a shared volume can therefore destroy assets uploaded by other users (peer assets), bypassing the per-asset peer-permission check that the sibling actionDeleteAsset endpoint correctly applies. This issue has been fixed in versions 4.17.15 and 5.9.22.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
Low
User interaction
None

What an attacker can affect

Confidentiality
None
Integrity
High
Availability
None

Exploit likelihood

0.39%Up 0.1 points since Jul 2, 2026
90%50%10%1%

What each source reports

CVE List Score used 7.1

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

Craft CMS is a content management system (CMS). In versions 5.0.0-RC1 through 5.9.21 and 4.0.0-RC1 through 4.17.14, theAssetsController::actionDeleteFolder() only requires the deleteAssets:<volume-uid> permission for the target folder. It never enforces deletePeerAssets:<volume-uid>, even though Assets::deleteFoldersByIds() cascades deletion to every descendant folder and every asset inside, regardless of the uploader's assigned privileges. A low-privilege user who has been granted folder-management rights on a shared volume can therefore destroy assets uploaded by other users (peer assets), bypassing the per-asset peer-permission check that the sibling actionDeleteAsset endpoint correctly applies. This issue has been fixed in versions 4.17.15 and 5.9.22.

Record
CVE-2026-50284
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:N/SC:N/SI:L/SA:N
Weakness
CWE-862
Updated
2026-11-02
EUVDEUVD-2026-41208 7.1

Craft CMS is a content management system (CMS). In versions 5.0.0-RC1 through 5.9.21 and 4.0.0-RC1 through 4.17.14, theAssetsController::actionDeleteFolder() only requires the deleteAssets:<volume-uid> permission for the target folder. It never enforces deletePeerAssets:<volume-uid>, even though Assets::deleteFoldersByIds() cascades deletion to every descendant folder and every asset inside, regardless of the uploader's assigned privileges. A low-privilege user who has been granted folder-management rights on a shared volume can therefore destroy assets uploaded by other users (peer assets), bypassing the per-asset peer-permission check that the sibling actionDeleteAsset endpoint correctly applies. This issue has been fixed in versions 4.17.15 and 5.9.22.

Record
EUVD-2026-41208
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:N/SC:N/SI:L/SA:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-7h62-6v23-v8fm 7.1

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

Summary

AssetsController::actionDeleteFolder() only requires the deleteAssets:<volume-uid> permission for the target folder. It never enforces deletePeerAssets:<volume-uid>, even though Assets::deleteFoldersByIds() cascades deletion to every descendant folder and every asset inside, regardless of who uploaded them. A low-privilege user who has been granted folder-management rights on a shared volume can therefore destroy assets uploaded by other users (peer assets), bypassing the per-asset peer-permission check that the sibling actionDeleteAsset endpoint correctly applies.

This is the same bug class that was just fixed in actionMoveFolder as GHSA-3w32-23wj-rxg3 (commit 05c2042, Apr 23 2026); the fix added requireVolumePermissionByFolder('deletePeerAssets', …) and savePeerAssets checks to the move endpoint but did not propagate to the delete-folder endpoint.

Details

src/controllers/AssetsController.php:552-569:

public function actionDeleteFolder(): Response
{
    $this->requireAcceptsJson();
    $folderId = $this->request->getRequiredBodyParam('folderId');

    $assets = Craft::$app->getAssets();
    $folder = $assets->getFolderById($folderId);

    if (!$folder) {
        throw new BadRequestHttpException('The folder cannot be found');
    }

    // Check if it's possible to delete objects in the target volume.
    $this->requireVolumePermissionByFolder('deleteAssets', $folder); // <-- only checks deleteAssets
    $assets->deleteFoldersByIds($folderId);

    return $this->asSuccess();
}

requireVolumePermissionByFolder() (src/controllers/AssetsControllerTrait.php:75-88) only resolves to a single requirePermission('deleteAssets:<vol-uid>') call. The peer-equivalent helper (requirePeerVolumePermissionByAsset) is never invoked because there is no folder-level peer helper that iterates the folder's contents.

Assets::deleteFoldersByIds() (src/services/Assets.php:311-349) then enumerates the folder + every descendant folder, queries every asset under those IDs, and calls Craft::$app->getElements()->deleteElement($asset, true) directly:

$assetQuery = Asset::find()->folderId($allFolderIds);
$elementService = Craft::$app->getElements();

foreach (Db::each($assetQuery) as $asset) {
    $asset->keepFileOnDelete = !$deleteDir;
    $elementService->deleteElement($asset, true);
}

This bypasses Asset::canDelete() (src/elements/Asset.php:1515-1536):

public function canDelete(User $user): bool
{
    if ($this->isFolder) { return false; }
    if (parent::canDelete($user)) { return true; }
    $volume = $this->getVolume();
    if (Assets::isTempUploadFs($volume->getFs())) { return true; }

    if ($this->uploaderId !== $user->id) {
        return $user->can("deletePeerAssets:$volume->uid"); // <-- never reached on cascade delete
    }
    return $user->can("deleteAssets:$volume->uid");
}

Compare to actionDeleteAsset (src/controllers/AssetsController.php:579-613), which correctly does:

$this->requireVolumePermissionByAsset('deleteAssets', $asset);
$this->requirePeerVolumePermissionByAsset('deletePeerAssets', $asset);

The fix that landed in 05c2042 for actionMoveFolder (src/controllers/AssetsController.php:733-765) added both savePeerAssets and deletePeerAssets requireVolumePermissionByFolder checks to mirror the per-asset pattern, but the same hardening was not applied to actionDeleteFolder or actionRenameFolder (which also calls deleteFoldersByIds indirectly through later logic).

The asymmetry between the two endpoints demonstrates the missing check.

Impact

  • Integrity / availability of other users' assets on any volume where the attacker has deleteAssets but not deletePeerAssets: the attacker can permanently delete peer-owned files (and their parent folder structure) on the underlying filesystem, with no recovery via Craft's UI.
  • The Craft permission model explicitly distinguishes "delete your own assets"…
Record
GHSA-7h62-6v23-v8fm
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:N/SC:N/SI:L/SA:N
Weakness
CWE-862
Updated
2026-11-02
NVD 7.1

Craft CMS is a content management system (CMS). In versions 5.0.0-RC1 through 5.9.21 and 4.0.0-RC1 through 4.17.14, theAssetsController::actionDeleteFolder() only requires the deleteAssets:<volume-uid> permission for the target folder. It never enforces deletePeerAssets:<volume-uid>, even though Assets::deleteFoldersByIds() cascades deletion to every descendant folder and every asset inside, regardless of the uploader's assigned privileges. A low-privilege user who has been granted folder-management rights on a shared volume can therefore destroy assets uploaded by other users (peer assets), bypassing the per-asset peer-permission check that the sibling actionDeleteAsset endpoint correctly applies. This issue has been fixed in versions 4.17.15 and 5.9.22.

Record
CVE-2026-50284
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:N/SC:N/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Weakness
CWE-862
Updated
2026-11-02
OSV 7.1

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

Craft CMS is a content management system (CMS). In versions 5.0.0-RC1 through 5.9.21 and 4.0.0-RC1 through 4.17.14, theAssetsController::actionDeleteFolder() only requires the deleteAssets:<volume-uid> permission for the target folder. It never enforces deletePeerAssets:<volume-uid>, even though Assets::deleteFoldersByIds() cascades deletion to every descendant folder and every asset inside, regardless of the uploader's assigned privileges. A low-privilege user who has been granted folder-management rights on a shared volume can therefore destroy assets uploaded by other users (peer assets), bypassing the per-asset peer-permission check that the sibling actionDeleteAsset endpoint correctly applies. This issue has been fixed in versions 4.17.15 and 5.9.22.

Record
CVE-2026-50284
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:N/SC:N/SI:L/SA:N
Weakness
CWE-862
Updated
2026-11-02
OSVGHSA-7h62-6v23-v8fm 7.1

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

Summary

AssetsController::actionDeleteFolder() only requires the deleteAssets:<volume-uid> permission for the target folder. It never enforces deletePeerAssets:<volume-uid>, even though Assets::deleteFoldersByIds() cascades deletion to every descendant folder and every asset inside, regardless of who uploaded them. A low-privilege user who has been granted folder-management rights on a shared volume can therefore destroy assets uploaded by other users (peer assets), bypassing the per-asset peer-permission check that the sibling actionDeleteAsset endpoint correctly applies.

This is the same bug class that was just fixed in actionMoveFolder as GHSA-3w32-23wj-rxg3 (commit 05c2042, Apr 23 2026); the fix added requireVolumePermissionByFolder('deletePeerAssets', …) and savePeerAssets checks to the move endpoint but did not propagate to the delete-folder endpoint.

Details

src/controllers/AssetsController.php:552-569:

public function actionDeleteFolder(): Response
{
    $this->requireAcceptsJson();
    $folderId = $this->request->getRequiredBodyParam('folderId');

    $assets = Craft::$app->getAssets();
    $folder = $assets->getFolderById($folderId);

    if (!$folder) {
        throw new BadRequestHttpException('The folder cannot be found');
    }

    // Check if it's possible to delete objects in the target volume.
    $this->requireVolumePermissionByFolder('deleteAssets', $folder); // <-- only checks deleteAssets
    $assets->deleteFoldersByIds($folderId);

    return $this->asSuccess();
}

requireVolumePermissionByFolder() (src/controllers/AssetsControllerTrait.php:75-88) only resolves to a single requirePermission('deleteAssets:<vol-uid>') call. The peer-equivalent helper (requirePeerVolumePermissionByAsset) is never invoked because there is no folder-level peer helper that iterates the folder's contents.

Assets::deleteFoldersByIds() (src/services/Assets.php:311-349) then enumerates the folder + every descendant folder, queries every asset under those IDs, and calls Craft::$app->getElements()->deleteElement($asset, true) directly:

$assetQuery = Asset::find()->folderId($allFolderIds);
$elementService = Craft::$app->getElements();

foreach (Db::each($assetQuery) as $asset) {
    $asset->keepFileOnDelete = !$deleteDir;
    $elementService->deleteElement($asset, true);
}

This bypasses Asset::canDelete() (src/elements/Asset.php:1515-1536):

public function canDelete(User $user): bool
{
    if ($this->isFolder) { return false; }
    if (parent::canDelete($user)) { return true; }
    $volume = $this->getVolume();
    if (Assets::isTempUploadFs($volume->getFs())) { return true; }

    if ($this->uploaderId !== $user->id) {
        return $user->can("deletePeerAssets:$volume->uid"); // <-- never reached on cascade delete
    }
    return $user->can("deleteAssets:$volume->uid");
}

Compare to actionDeleteAsset (src/controllers/AssetsController.php:579-613), which correctly does:

$this->requireVolumePermissionByAsset('deleteAssets', $asset);
$this->requirePeerVolumePermissionByAsset('deletePeerAssets', $asset);

The fix that landed in 05c2042 for actionMoveFolder (src/controllers/AssetsController.php:733-765) added both savePeerAssets and deletePeerAssets requireVolumePermissionByFolder checks to mirror the per-asset pattern, but the same hardening was not applied to actionDeleteFolder or actionRenameFolder (which also calls deleteFoldersByIds indirectly through later logic).

The asymmetry between the two endpoints demonstrates the missing check.

Impact

  • Integrity / availability of other users' assets on any volume where the attacker has deleteAssets but not deletePeerAssets: the attacker can permanently delete peer-owned files (and their parent folder structure) on the underlying filesystem, with no recovery via Craft's UI.
  • The Craft permission model explicitly distinguishes "delete your own assets"…
Record
GHSA-7h62-6v23-v8fm
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:N/SC:N/SI:L/SA:N
Weakness
CWE-862
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
craftcms/cms
Packagist
5.0.0-RC1 up to 5.9.22
5.9.22
craftcms/cms
Packagist
4.0.0-RC1 up to 4.17.15
4.17.15
craftcms/cms
Packagist
5.0.0-RC1 up to 5.9.22, plus listed versions
5.0.05.0.15.0.25.0.35.0.4
Show 45 moreShow less
5.0.55.0.65.1.05.1.15.1.105.1.25.1.35.1.45.1.55.1.65.1.75.1.85.1.95.2.05.2.0-beta.15.2.0-beta.25.2.0-beta.35.2.0-beta.45.2.0-beta.55.2.0-beta.65.2.15.2.105.2.25.2.35.2.45.2.4.15.2.55.2.65.2.75.2.85.2.95.3.05.3.0-beta.15.3.0-beta.25.3.0.15.3.0.25.3.0.35.3.15.3.25.3.35.3.45.3.55.3.65.4.05.4.0.1
5.9.22
craftcms/cms
Packagist
4.0.0-RC1 up to 4.17.15, plus listed versions
4.0.04.0.0-RC24.0.0-RC34.0.0.14.0.1
Show 45 moreShow less
4.0.24.0.34.0.44.0.54.0.5.14.0.5.24.0.64.1.04.1.0.14.1.0.24.1.14.1.24.1.34.1.44.1.4.14.10.04.10.0-beta.14.10.0-beta.24.10.14.10.24.10.34.10.44.10.54.10.64.10.74.10.84.11.04.11.0.14.11.0.24.11.14.11.24.11.34.11.44.11.54.12.04.12.14.12.24.12.34.12.44.12.4.14.12.54.12.64.12.6.14.12.74.12.8
4.17.15

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-50284 is a high vulnerability (CVSS 7.1) in Craftcms Cms, published July 1, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.39%, the 32nd percentile. Fixed in craftcms/cms 5.9.22.

References

This page doesn't list everything

Left out here: 314 versions and the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open