### Summary The `EntriesController::actionMoveToSection()` endpoint checks only whether the current user can view the destination section, but it does not require permission to save entries into that section. A low-privileged authenticated control-panel user who can move an entry out of its current section can therefore move that entry into a different section where they have read access but no write access. ### Details The vulnerable route is implemented in [EntriesController.php](/D:/files/projects/cms-5.9.19/cms-5.9.19/src/controllers/EntriesController.php):465: The destination check is only `viewEntries:$section->uid` . The source-entry gate is `Entry::canMove()`, which verifies whether the user can move the existing entry based on the source section: This closes the exploit chain: 1. External source: authenticated CP request to `entries/move-to-section`. 2. Missing authorization check: destination section requires only `viewEntries`, not `saveEntries`. 3. Privileged sink: `moveEntryToSection()` rewrites `sectionId` and saves the entry into the unauthorized section. Preconditions derived from the code: 1. The attacker is authenticated to the control panel. 2. Entry `345` is movable by the attacker from its current section. 3. The attacker can satisfy `viewEntries` on destination section `12`. 4. The attacker does not have `saveEntries:DESTINATION_UID`, which is the missing check that makes the bypass possible. Result: 1. The controller accepts the request because `viewEntries:$section->uid` passes. 2. Each source entry passes `canMove()` based on source-section permissions. 3. `moveEntryToSection()` updates the entry’s `sectionId` and saves it. 4. The entry is now located in a section where the attacker did not have write permission. ### Impact This breaks the intended section-level authorization model. A user with limited content permissions can inject or relocate content into a protected section, interfering with editorial boundaries, approval workflows, section-specific business logic, and content ownership expectations.
### Summary The `EntriesController::actionMoveToSection()` endpoint checks only whether the current user can view the destination section, but it does not require permission to save entries into that section. A low-privileged authenticated control-panel user who can move an entry out of its current section can therefore move that entry into a different section where they have read access but no write access. ### Details The vulnerable route is implemented in [EntriesController.php](/D:/files/projects/cms-5.9.19/cms-5.9.19/src/controllers/EntriesController.php):465: The destination check is only `viewEntries:$section->uid` . The source-entry gate is `Entry::canMove()`, which verifies whether the user can move the existing entry based on the source section: This closes the exploit chain: 1. External source: authenticated CP request to `entries/move-to-section`. 2. Missing authorization check: destination section requires only `viewEntries`, not `saveEntries`. 3. Privileged sink: `moveEntryToSection()` rewrites `sectionId` and saves the entry into the unauthorized section. Preconditions derived from the code: 1. The attacker is authenticated to the control panel. 2. Entry `345` is movable by the attacker from its current section. 3. The attacker can satisfy `viewEntries` on destination section `12`. 4. The attacker does not have `saveEntries:DESTINATION_UID`, which is the missing check that makes the bypass possible. Result: 1. The controller accepts the request because `viewEntries:$section->uid` passes. 2. Each source entry passes `canMove()` based on source-section permissions. 3. `moveEntryToSection()` updates the entry’s `sectionId` and saves it. 4. The entry is now located in a section where the attacker did not have write permission. ### Impact This breaks the intended section-level authorization model. A user with limited content permissions can inject or relocate content into a protected section, interfering with editorial boundaries, approval workflows, section-specific business logic, and content ownership expectations.
Update craftcms/cms to 5.9.21 if you use the affected versions. Test the change in a non-production environment first.
Local check
hol-guard supply-chain scanCraft CMS: Authorization bypass in `entries/move-to-section` via missing target-section save check affects craftcms/cms (composer). Severity is medium. ### Summary The `EntriesController::actionMoveToSection()` endpoint checks only whether the current user can view the destination section, but it does not require permission to save entries into that section. A low-privileged authenticated control-panel user who can move an entry out of its current section can therefore move that entry into a different section where they have read access but no write access. ### Details The vulnerable route is implemented in [EntriesController.php](/D:/files/projects/cms-5.9.19/cms-5.9.19/src/controllers/EntriesController.php):465: The destination check is only `viewEntries:$section->uid` . The source-entry gate is `Entry::canMove()`, which verifies whether the user can move the existing entry based on the source section: This closes the exploit chain: 1. External source: authenticated CP request to `entries/move-to-section`. 2. Missing authorization check: destination section requires only `viewEntries`, not `saveEntries`. 3. Privileged sink: `moveEntryToSection()` rewrites `sectionId` and saves the entry into the unauthorized section. Preconditions derived from the code: 1. The attacker is authenticated to the control panel. 2. Entry `345` is movable by the attacker from its current section. 3. The attacker can satisfy `viewEntries` on destination section `12`. 4. The attacker does not have `saveEntries:DESTINATION_UID`, which is the missing check that makes the bypass possible. Result: 1. The controller accepts the request because `viewEntries:$section->uid` passes. 2. Each source entry passes `canMove()` based on source-section permissions. 3. `moveEntryToSection()` updates the entry’s `sectionId` and saves it. 4. The entry is now located in a section where the attacker did not have write permission. ### Impact This breaks the intended section-level authorization model. A user with limited content permissions can inject or relocate content into a protected section, interfering with editorial boundaries, approval workflows, section-specific business logic, and content ownership expectations.
AI coding agents often install or upgrade packages automatically in composer. A medium vulnerability in a dependency can be pulled into a project through a normal install or update without a human reviewing the change, expanding the blast radius from a single package to every agent workspace that depends on it.
| Package | Affected range | Fixed version |
|---|---|---|
| craftcms/cmscomposer | >=5.0.0-RC1,<5.9.21 | 5.9.21 |
Fixed versions are reported by the source feed; confirm compatibility before updating.
Reported by GitHub Security Advisories (ghsa).
HOL Guard can help your team review package activity against supported protection paths.
Explore HOL GuardUpdate craftcms/cms to 5.9.21 if you use the affected versions. Test the change in a non-production environment first.
Local check
hol-guard supply-chain scanCraft CMS: Authorization bypass in `entries/move-to-section` via missing target-section save check affects craftcms/cms (composer). Severity is medium. ### Summary The `EntriesController::actionMoveToSection()` endpoint checks only whether the current user can view the destination section, but it does not require permission to save entries into that section. A low-privileged authenticated control-panel user who can move an entry out of its current section can therefore move that entry into a different section where they have read access but no write access. ### Details The vulnerable route is implemented in [EntriesController.php](/D:/files/projects/cms-5.9.19/cms-5.9.19/src/controllers/EntriesController.php):465: The destination check is only `viewEntries:$section->uid` . The source-entry gate is `Entry::canMove()`, which verifies whether the user can move the existing entry based on the source section: This closes the exploit chain: 1. External source: authenticated CP request to `entries/move-to-section`. 2. Missing authorization check: destination section requires only `viewEntries`, not `saveEntries`. 3. Privileged sink: `moveEntryToSection()` rewrites `sectionId` and saves the entry into the unauthorized section. Preconditions derived from the code: 1. The attacker is authenticated to the control panel. 2. Entry `345` is movable by the attacker from its current section. 3. The attacker can satisfy `viewEntries` on destination section `12`. 4. The attacker does not have `saveEntries:DESTINATION_UID`, which is the missing check that makes the bypass possible. Result: 1. The controller accepts the request because `viewEntries:$section->uid` passes. 2. Each source entry passes `canMove()` based on source-section permissions. 3. `moveEntryToSection()` updates the entry’s `sectionId` and saves it. 4. The entry is now located in a section where the attacker did not have write permission. ### Impact This breaks the intended section-level authorization model. A user with limited content permissions can inject or relocate content into a protected section, interfering with editorial boundaries, approval workflows, section-specific business logic, and content ownership expectations.
AI coding agents often install or upgrade packages automatically in composer. A medium vulnerability in a dependency can be pulled into a project through a normal install or update without a human reviewing the change, expanding the blast radius from a single package to every agent workspace that depends on it.
| Package | Affected range | Fixed version |
|---|---|---|
| craftcms/cmscomposer | >=5.0.0-RC1,<5.9.21 | 5.9.21 |
Fixed versions are reported by the source feed; confirm compatibility before updating.
Reported by GitHub Security Advisories (ghsa).
HOL Guard can help your team review package activity against supported protection paths.
Explore HOL Guard