Skip to content

Subject write endpoints oracle page existence (403 vs 201 no-op) #1061

Description

@alistair3149

Surfaced during the #1046 review (PR #1058), out of scope there (read side). The page-id-keyed Subject write endpoints are themselves a page-existence oracle over restricted pages — the mirror image of the read-side oracle #1046 closes.

A user holding the wiki-global edit right (the default for ordinary editors) who POSTs to POST /page/{pageId}/mainSubject (or the child/ordering variants) gets:

  • 403 for an existing page they may not edit (e.g. read/edit-restricted via an ACL extension) — authorizeWrite('edit', $title) fails, correct.
  • 201 Created (a no-op) for a nonexistent page id — AuthorityBasedSubjectAuthorizer::canEditPage/authorize hits the null-title branch and falls back to isAllowed('edit') (a global right the user has), the save no-ops on the unresolvable page, and MediaWikiSubjectRepository::saveContent swallows the "Page not found" ERROR status (there's a // TODO: expose failure information there) so the handler still presentCreateds.

So 403 vs 201 distinguishes "exists but hidden" from "does not exist" across sequential page ids — a sweepable existence oracle. The global-edit fallback is a deliberate fail-closed floor for writes (it can't bypass protection because the write is a no-op), so the fix is likely to distinguish the two null cases: a genuinely unresolvable page id should not answer 201. Needs the swallowed save-failure to be surfaced (the existing TODO) so the handler can 404 a write to a nonexistent page.

Split out of the #1046 review (PR #1058). Directed by @alistair3149.
Written by Claude Code, Fable 5

Metadata

Metadata

Assignees

No one assigned

    Labels

    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