Repository navigation
[Decision] should a file field's sys_file metadata (name, size, type) follow the holding record's read, as the download door and attachment rows already do? (access half of #22593) #22624
Description
Activity
objectstack-fleet commented
on Oct 10, 2026 ContributorAuthorMore actionsRuling: batch #311 item 2 · letter B · maintainer 「同意」 2026-10-10T07:10Z
Director seat, summon #36,
session_019fWAt2renophxLVg5aJXMH(GitHubhotlong; written asobjectstack-fleet[bot]via the relay). Batch #311 (three cards, this one second) was presented in chat with this seat's own readings and the recommendation B, fallback A; the maintainer answered 「同意」, which takes the recommendation. Freshness gate: the body (9,371 bytes, unedited since filing) and the comment list (none) were re-read before this record. Thread-read: none on this card.The ruling
- B — a file field's metadata follows the same parent-derived verdict the download door already applies. When the caller's own
sys_fileobject read is refused,resolveFileReferencesstill hydrates a referenced file's{ id, name, size, mimeType, url }for exactly the files whoseref_object/ref_idname the record being read, judged by the rulebuildFileReadAuthorizerapplies to the bytes (storage-service-plugin.ts:1237-1265): a declaredfileAccessDelegatedecides when the owner object names one, else the caller's read of that record decides. At hydration that record is the one the caller has just read, so the verdict reduces to "the file's reference points at this record", with the delegate consulted where declared. A referenced id that does not point at the record being read keeps fix(objectql): a refused sys_file read marks the file field refused instead of reading as no file #22620's refused marker ({ id, metadataRefused: true }). Direct queries ofsys_fileare unchanged;public_readis unchanged. - ⛔ Not taken: A (the status quo plus the loud marker: two contradictory rules for one file, and every AI-authored application with a file field has to remember an extra
sys_filegrant or ship the hotclm symptom) and C (a row-level rule onsys_fileplus a default-set change: a new gate and a new default grant, where B needs one engine seam).
Why B on the first axis
ADR-0104 D3 already rules it: "field-referenced files get parent-derived read checks, reusing the attachments
authorizeFileReadverdict model", and the expanded form "is produced at read/expand time from thesys_filerow". The download door implements D3; the hydration did not. B finishes the record's one verdict model instead of adding a second, and the population that gains the name, size and type is exactly the population that can already fetch the bytes, not one person more. Salesforce models it the same way: a record's reader sees the files linked to it (ContentDocumentLink) without a grant on the file object.Prior rulings read: ADR-0104 D3; ADR-0110 D3 (the loud half, PR #22620); #22593 (the loud half's card,
pm:dispatched); #22431, #22455, #10702 (closed; none rules the hydration's read); thread: none. 自检:只看①选 B;②③④是否翻转:否。State and execution
- This card closes
completedin this act;needs-user-decisionleaves with the close (it carried no other label). - Pointer on objectql: a file field reads "no file" for a record reader without sys_file read: the hydration lookup runs as the caller, and a refusal reads as absent data (split from #22590 item 1) #22593: the
domain:engineseat (seat 2, which filed this card from objectql: a file field reads "no file" for a record reader without sys_file read: the hydration lookup runs as the caller, and a refusal reads as absent data (split from #22590 item 1) #22593'saccess_half) files the execution card:Clause-②: yes(a read widens to the parent-derived population), one contract-review-tier review before the queue; pins cover a field-owned file on the record read, a referenced id not field-owned by that record, an owner object with afileAccessDelegate, andpublic_read, with an ablation; PR fix(objectql): a refused sys_file read marks the file field refused instead of reading as no file #22620's marker stays for the non-owned case. Workload, as the rule requires: one engine seam inresolveFileReferencesplus pins, by the card's reading a few hundred lines; not measured.
Generated by Claude Code
- B — a file field's metadata follows the same parent-derived verdict the download door already applies. When the caller's own
Ruled: 6094990917 · letter B · 2026-10-10T07:12Z
Filing gate: ② a decision only the maintainer can make. This is the access half of #22593, which that card sends to the maintainer: whether a file field's
sys_filemetadata (name, size, type) should follow the holding record's read, the way the download door and attachment rows already do. Answering yes loosenssys_file's read boundary. Filed bydomain:engineseat 2 (seat post #20966) ·session_01Bw3y2DWhT9RPnrmDsNqEVG, from the read-only measurement in #22593's os-dev-report (access_half, 6094498292).⛔ Not graded or routed here; ⛔ not a claim.Ruled B (6094990917); carrier: the domain:engine seat on #22593.Who acts on it: the maintainer answers one letter; triage grades it; the
domain:engineseat carries the answer. #22593's loud half (PR #22620) does not wait for it. It marks a refused hydration{ id, metadataRefused: true }instead of a bare id that reads as "no file".维护者速读
sys_file读权限的申请人,看自己刚上传的文件字段,显示「没有文件」。objectql: a file field reads "no file" for a record reader without sys_file read: the hydration lookup runs as the caller, and a refusal reads as absent data (split from #22590 item 1) #22593 的显式那一半(PR fix(objectql): a refused sys_file read marks the file field refused instead of reading as no file #22620)把它改成「有文件,但详情无权查看」,不再冒充空值。sys_file的对象读权限。默认成员权限集没有这项,所以同一个人能下载文件内容,却看不到文件名。sys_file的规则不变。回一个字母:A / B / C。一句话问题
能读一条记录的人,能不能看到这条记录文件字段里文件的名称、大小和类型?
Background (read on
faf689872c, from #22593'saccess_half; read-only, nothing changed)sys_fileread today.SystemFile(service-storage/src/objects/system-file.object.ts:21) declares no access block and nosharingModel; it is tenant-scoped.PermissionEvaluator.checkObjectPermission('find', 'sys_file', …)(plugin-security/src/permission-evaluator.ts:205). It passes only when a set grantsallowReadonsys_fileor'*'.'*'read inadmin_full_access,organization_adminandviewer_readonly.member_default, the everyone baseline, names neither, so a plain member has nosys_fileread unless an application set grants it.ObjectQL.resolveFileReferences(objectql/src/engine.ts) reads the referencedsys_filerows in one batch, as the caller. A refusal arrives asPermissionDeniedError(403) for the whole sub-read, measured on the real stack.service-storage/src/storage-routes.ts,GET /storage/files/:fileIdand/url).sys_fileread.authorizeDownloadletsacl: 'public_read'through anonymously. A file with no scope and no field owner needs only a session.buildFileReadAuthorizer(storage-service-plugin.ts:1210):ref_object+ref_id) is allowed if a declaredfileAccessDelegatesays so, else if the caller can read that record;sys_attachmentparent is caller-readable.installAttachmentReadVisibility(service-storage/src/attachment-access-hooks.ts:758) ANDs a caller-readable-parent filter into everysys_attachmentread, behind an object grant.Governing text:
authorizeFileReadverdict model — possession of a URL stops being possession of the bytes." The expanded form{ id, name, size, mimeType, url }is "produced at read/expand time from thesys_filerow".选项 × 真实代价
sys_file读权限的人;想让成员看到文件名,应用自己在权限集里给sys_file读ref_object/ref_id与这条记录一致),调用者能读这条记录就水合;其他情况仍按sys_file读权限;直接查询sys_file不变sys_file本身加一条行级规则(类似附件行跟随父记录),并让默认成员权限集带上sys_file读sys_file也跟着开放到父记录可读的范围;改动面最大,要动默认权限集与新的行级闸门业务含义直译:
os-decision-facets
sys_file行产生。B 让水合与下载入口用同一条推导规则,一处授权语义。A 保留两套互相矛盾的规则。C 把推导规则下沉为sys_file的行级闸门,更彻底,但新增闸门与默认权限变更。main: drive the whole contract lifecycle as every audience, in en and zh-CN, and report what a real user hits hotclm#87)中的合同申请人;main 上复现(objectql: a file field reads "no file" for a record reader without sys_file read: the hydration lookup runs as the caller, and a refusal reads as absent data (split from #22590 item 1) #22593 报告)。默认成员权限集不含sys_file读,所以任何没有额外授权的应用都会遇到。sys_file读,漏授静默降级。B 结构性地消除这个漏授点。C 同样结构性,但新增的默认授权本身是 AI 容易误解的面。Prior rulings read: sys_file read, file field authorization, attachment parent visibility → ADR-0104 D3 (parent-derived read for field files), #22431 (closed: an unowned file needs a session;
public_readstays anonymous), #22455 (closed: the attachment write gate), #10702 (closed:sys_filemetadata writability). Thread: #22593, #22590. None rules the hydration's read.推荐
B:元数据的读权限与下载入口同源。
裁后执行
domain:engine立执行卡。resolveFileReferences在对象读被拒时,对ref_object/ref_id与当前记录一致的文件 id,按buildFileReadAuthorizer的同一判定读取元数据。不一致的 id 保持 fix(objectql): a refused sys_file read marks the file field refused instead of reading as no file #22620 的拒绝标记。钉子覆盖字段所属、非所属与public_read三类,并做消融。Clause-②: yes,契约复审档复核。domain:services出sys_file行级闸门与默认权限集变更的方案卡,再定执行顺序。Dedupe: MCP
search_issues, repo-scoped, open and closed, two queries:PERMISSION_DENIEDcreate grant,FILES_DISABLED) still leaves the uploadedsys_filecommitted with no join row #22547, storage(attachments): a committedsys_filewhose attach is refused is never reaped — the lifecycle tombstones only files that lose their last join row, so a file that never gained one stays forever #22466, security(storage): the attachment gate's delete and update refusals name the parent record to a caller outside the floor's domain who cannot read it #21755, docs(permissions): attachments-access says Field.file stores a file URL in the column — since ADR-0104 D3 it stores a sys_file id #17406, [security-sensitive] sys_file mime_type/size are writable via the data API — may neutralize field accept/maxSize re-check (verify non-admin) #10702 andsys_commenthas no record-level authorization: any org member reads and writes comments on records they cannot see #4630. None decides it.PERMISSION_DENIEDcreate grant,FILES_DISABLED) still leaves the uploadedsys_filecommitted with no join row #22547, service-storage: a file with no scope and no field owner is downloadable by an anonymous caller who has its id; the download should require a signed-in caller, whileacl: 'public_read'stays anonymous (ADR-0104) #22431, security(attachments): the attach / delete gate asks plugin-sharing's canEdit, which reads every controlled_by_parent object as public — a member with sys_attachment create/delete writes files on child records they cannot edit #22455 and docs(permissions): attachments-access says Field.file stores a file URL in the column — since ADR-0104 D3 it stores a sys_file id #17406. None decides it.Dedupe words:
sys_file metadata read parent-derived·file field hydration refused name size·download door vs hydration authorization asymmetry