Skip to content

translator: translated writes bypass collection access control #113

Description

@SearheiParkhamchuk

Read against 0.11.0. Split out of #102, where it is named as "a separate, smaller improvement".

Problem

saveTranslatedDocument calls payload.update(...) with no user and no overrideAccess: false
(src/server/features/translate-document/handler.ts). The Local API defaults to
overrideAccess: true, so collection access control never runs on a translated write.

Measured on Payload 3.84.1: a collection declared access: { update: () => false } — a blanket deny —
still accepted the translated value into the target locale. Nothing about the write is checked.

Why this matters

The plugin's own endpoint guard (access?: AccessGuard) controls who may trigger a translation, and
it defaults to undefined — no restriction. So on a default install the two layers that could stop an
unauthorized write are both absent: the endpoint is open and the write bypasses collection access.

For a host that models editorial permissions per collection, translation is a hole straight through
them.

Why it is not a one-line fix

Passing overrideAccess: false alone would deny every translation, because there is no user to check
against. Nothing threads one: every handler receives req.payload rather than req, and
TranslateDocumentHandler.handle(payload, input) takes a bare Payload. Background jobs make it
harder still — a job runs with no authenticated user by construction, so even a fully threaded request
has nobody to authorize on the async path.

So a real fix has to decide what a background translation runs as.

Suggested shape

Two parts, roughly independent:

  1. Thread the request. Pass req (or a user) from the endpoint down to the write, so a
    synchronously-run translation is authorized as the caller. Payload's update accepts
    req?: Partial<PayloadRequest> for exactly this, and it threads the transaction too.
  2. Decide the background identity. Give the plugin config a way to name the user a queued
    translation acts as — a fixed service user, or the user who enqueued it, captured at enqueue time
    and replayed on the job.

Whatever the shape, the default cannot silently start denying writes on existing installs.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions