docs(adr): ADR-0106 — metadata-plane FLS, per-caller masking of object schemas (#3661 step ③) - #3678
Merged
Merged
Conversation
…t schemas (#3661 step ③) Object-schema reads currently ship the full schema (field names, labels, picklist option values, formula expressions, visibleWhen predicates, requiredPermissions capability names) to any authenticated caller — the metadata-plane twin of the export-header leak the #3391 series closed on the data plane. ObjectStack is a development platform: whether a deployment's authenticated callers are trusted is the customer's call, so the platform must enforce rather than assume. Decisions (Proposed): - D1 mask by readable projection, remove masked fields entirely - D2 enforce in the dispatch layer; metadata-protocol stays caller-free - D3 mask-after-cache with a field-visibility fingerprint folded into the ETag (single full copy per object, cohort-correct 304s) - D4 isSystem / platform-admin exemption (Studio needs full schema) - D5 coverage: single read (cached + uncached), list read, runtime /metadata catch-all, plus an outlet audit - D6 three-tier failure posture: no-service → open; undefined → open with telemetry + private cache headers; evaluation throws → 5xx (never the unmasked body on an error path, never an empty-fields 200) - D7 zero-set callers resolve the fallback permission set - D8 default on, current major, config escape hatch Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AEb4XCVb7iLEBVe9HghjTt
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
3 tasks
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AEb4XCVb7iLEBVe9HghjTt
Distills what the disclosure boundary is worth: the claimable FLS invariant (pentest/questionnaire/Salesforce-describe parity), the platform-leverage shape (customers cannot fix /meta exposure in their own tier), unlocked mixed-sensitivity modeling (no object splitting for portal scenarios), and schema shape as business information — against a deliberately near-zero cost side (D3/D8). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AEb4XCVb7iLEBVe9HghjTt
os-zhuang
marked this pull request as ready for review
July 27, 2026 14:12
10 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Step ③ of #3661, as an ADR (status: Proposed). Steps ① ② shipped client-side as objectstack-ai/objectui#2866.
Why the decision flipped
An earlier disposition ("target deployments have no untrusted authenticated users → document no-masking as the explicit default") died on the platform premise: ObjectStack customers build portals and products whose authenticated callers the platform cannot vouch for. Per ADR-0049, enforcement must therefore be the platform default — so this ADR decides to mask, and resolves the three hard problems the issue flagged (cache key, protocol context, exemptions) instead of documenting their absence.
Decision summary
fieldsontosecurity.getReadableFields(object, caller); masked fields are removed entirely (name, label, options, formula,visibleWhen,requiredPermissions— the leak is more than names).readableonly;editablestays with/auth/me/permissions, so nogetEditableFieldsdual is needed./meta+ runtime/metadata);metadata-protocolsignatures unchanged.doc/bookpattern) is recorded as implementation fallback, not the end state.isSystem/ platform-admin exemption (Studio/Setup authoring needs the full schema).undefined→ open with telemetry +private, no-store(matches the engine-middleware and security 服务 getReadableFields 查询面(export 列投影的长期正解) #3547 export precedents; window is operational, not attacker-inducible; failing closed risks a bootstrap deadlock since permission sets are themselves metadata); evaluation throws → 5xx — never the unmasked body on an error path, never an empty-fields 200 (silently wrong and cacheable poison). Includes the reconciliation with ADR-0049 and whydoc/bookfail-closed is a different class (content access control vs disclosure control on infrastructure).member_default/guest), aligning with/auth/me/permissions— public deployments' schema exposure becomes a deliberate permission-set decision.Verification
Docs-only change. Cross-linked ADR files (0046/0049/0066/0090) verified present; no ADR index file requires registration.
Implementation (D1–D5 + tests in
rest/runtime/plugin-security) is deliberately a separate PR once this is accepted.🤖 Generated with Claude Code
https://claude.ai/code/session_01AEb4XCVb7iLEBVe9HghjTt
Generated by Claude Code