Skip to content

Admin editor can silently overwrite a newer draft: PUTs omit _rev and editor loads can miss the latest draft revision (0.29.0) #2121

Description

@meditatingsurgeon

Version: emdash 0.29.0 (with @emdash-cms/admin 0.29.0), Cloudflare Workers runtime, D1 with session: "auto", KV object cache.

Summary: the admin page editor can silently overwrite a newer draft revision. Two behaviours combine:

  1. The editor's content PUTs never send _rev, so the server-side optimistic concurrency check (validateRev) never engages ("No _rev = blind write" per the comment in the update handler).
  2. Shortly after a save there is a stale read window in which loading the editor returns the entry WITHOUT its newest draft revision. ContentRepository.findById / findBySlug / mapRow build item.data from the base content table columns only; the draft revision is hydrated separately, and in our reproductions the editor sometimes rendered base-only content while draft_revision_id pointed at a newer revision in the revisions table.

When 1 and 2 combine, a user who reloads the editor inside the stale window sees old content, edits it, and saves; the save repoints draft_revision_id at a new revision built from the stale state. The newer draft is orphaned with no warning. We reproduced this twice on a fresh 0.29.0 tenant; the revisions table showed the newer draft (585 bytes, custom blocks intact) and then a post-reload save created a smaller revision from base content and repointed the draft.

Steps to reproduce (timing dependent):

  1. Edit a draft page, add content, save (draft revision written, correct).
  2. Reload the editor shortly after; if the load falls in the stale window the editor shows content without the fresh draft.
  3. Edit and save again: the newer draft is silently replaced.

Suggested fixes: have the admin editor echo _rev from the GET into its PUTs so validateRev refuses the stale save; and/or hydrate the draft revision atomically in the same read that loads the entry for the editor.

Workaround we ship: template middleware injects a small script into the admin shell that remembers _rev from content API responses and adds it to PUT bodies that lack one, letting the existing validateRev do its job.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions