# Craft CMS: Authorship spoofing in `entries/save-entry` via pre-check/post-mutation authorization gap

Craft CMS checks entry-edit permission before applying attacker-supplied authors[], letting a low-privileged author reassign entry authorship without the peer-author-change permission.

- Payout: Research
- Severity: high
- Program: Craft CMS
- Found by: larlarua
- Disclosed: 2026-07-02
- CVE: CVE-2026-50279
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-qq2c-2q8j-jh27
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-qq2c-2q8j-jh27
- Tags: area:web domain:web impact:integrity kind:research platform:ghsa sev:high tech:php technique:source-review vuln:access-control vuln:business-logic

**Target:** EntriesController::actionSaveEntry / Entry::setAttributesFromRequest (entries/save-entry endpoint)
**Root cause:** enforceEditEntryPermissions() runs on the pre-mutation entry state, then _populateEntryModel() applies request-controlled authorIds gated only by canChangeAuthor(), which is evaluated against the old author list; no authorization re-check occurs after the author mutation.
**Steps:**
1. Authenticate as a user who can edit entry 345 and is among its existing authors, with viewPeerEntries for the section.
2. Send POST to `entries/save-entry` with `entryId=345` and `authors[]=1` (or `author=1`).
3. enforceEditEntryPermissions() passes on the original entry state.
4. _populateEntryModel() reads `authors[]=1`; setAttributesFromRequest() calls canChangeAuthor(), which returns true because the user is in the old authorIds.
5. saveElement() persists and _saveAuthors() rewrites the entry-author relation, so entry 345 is now authored by user 1.
**How found:** Source review of the save-entry control flow: noticed authorization (enforceEditEntryPermissions) is performed before _populateEntryModel mutates authorIds, and canChangeAuthor() is evaluated against the pre-mutation author set, creating a TOCTOU-style gap.
**Exploitation:** An authenticated low-privileged author of an entry submits a save-entry request with a forged authors[] value; the controller authorizes against the old authorship, then persists the new author relation, falsifying content ownership without the changeAuthorForPeerEntries permission.
**Insight:** In CMS/element save controllers, check whether permission gates run before request-controlled relationship mutations (authors, owners, categories); if the gate reads the old relation state, a self-inclusion in the old set can authorize rewriting it.

