Skip to content

enforceFilesCapability / enforceFeedsCapability are beforeInsert-only — a re-point via update dodges the capability opt-in on the NEW parent object #10170

Description

@os-zhuang

Found while implementing #10091 (the sys_attachmentbeforeUpdate authorization guard). Filed rather than fixed: that card's surface is attachment-access-hooks.ts only, and this gap lives in packages/plugins/plugin-audit/src/audit-writers.ts.

What is there and what is not

Both capability gates register on insert only:

  • enforceFilesCapabilityaudit-writers.ts (engine.registerHook('beforeInsert', …, { object: 'sys_attachment' }), ~:1617): refuses a sys_attachment insert whose parent_object does not declare enable: { files: true } (403 FILES_DISABLED, fail-closed, opt-in means explicit).
  • enforceFeedsCapability — same file: the enable.feeds twin on sys_comment inserts (403 FEEDS_DISABLED).

Neither fires on update. Both objects' access kits authorize a parent re-point on update — comment-access-hooks.ts has since #4630 (the new thread_id's parent must be readable), and attachment-access-hooks.ts does once #10091 lands (the new parent_object/parent_id must be editable) — but the re-point checks are access checks, not capability checks. So a caller with edit on a record whose object declares enable.files: false (spec default) cannot create an attachment on it, yet can move an existing attachment onto it via update; likewise a comment can be re-pointed into a thread on a feeds-disabled object.

On today's origin/main the update verb on sys_attachment is entirely ungated, so the dodge is trivially reachable; after #10091 it narrows to callers who pass the row + re-point access rules — the capability half is still never asked.

Why it looks like a defect

The gates' own doc comment says the flag answers "does this object allow comments/files at all" — a property of the target object, not of the verb that got the row there. object.zod.ts's enable.files description says the flag "permits attachments targeting this object; otherwise creation is rejected" — a re-point makes the row target the object without a creation.

Suggested shape

Register the same two enforcement functions on beforeUpdate for their objects, reading the effective new parent (payload value, falling back per row) — or extend the existing insert handlers' registration, since both already read only input.data. For sys_comment the parent object comes out of the new thread_id; for sys_attachment it is data.parent_object (only when present — an unchanged parent needs no re-check).

Backlinks: #10091 (where found; its re-point access rule is the neighbouring half), #2707/#2727 (the capability gates' own cards), #4630 (the comment kit's re-point rule).


Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions