Skip to content

test(security): pin static-readonly server enforcement end-to-end + fix its declared contract (#3003)#3015

Merged
os-zhuang merged 1 commit into
mainfrom
claude/wizardly-fermi-kriftd
Jul 16, 2026
Merged

test(security): pin static-readonly server enforcement end-to-end + fix its declared contract (#3003)#3015
os-zhuang merged 1 commit into
mainfrom
claude/wizardly-fermi-kriftd

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

背景(#3003)

#3003 用真机复现坐实了 #2948 修复前的缺口:仅靠静态 readonly: true "保护"的审批/状态/金额字段,可以在同一登录会话里用一次直接 REST PATCH 伪造写入,在已发布的 15.0.0 上实现草稿自批(RECORD_LOCKED 只拦活动态审批流,草稿从未入流)。

引擎侧修复(stripReadonlyFields,#2957)已在 main 上:非 system 上下文的 UPDATE(单条 + 多行路径)会把调用方提交的静态 readonly 字段静默剥离(HTTP 200、保留原值),与 readonlyWhen 对称,insert 豁免——但该修复在 15.0.0 发布之后才落地,尚未随版本发出。本 PR 不改运行时行为,补的是防止该契约再次静默回退或被误读的全部配套:

变更

  • 端到端 dogfood 证明 showcase-static-readonly.dogfood.test.ts(@proof: readonly-static-write):在真实 showcase 应用上走真 HTTP——非管理员 owner 直接 PATCH 伪造 lead_score → 200 且持久值不变、同载荷的可编辑字段正常落库;纯伪造载荷等价 no-op;INSERT 仍可播种该列(与 readonlyWhen 对称的文档化豁免)。
  • authz 一致性矩阵新增 readonly-static-write 行并列入 HIGH_RISK——证明文件成为强制项,删除即 CI 失败。
  • ADR-0054 proof-registry 新增 bound class,绑定 field/readonly:liveness 台账中该属性标 live 必须携带上述证明。
  • 契约文本修正:FieldSchema.readonly 的描述原文是 "Read-only in UI"——正是 安全:静态字段 readonly: true 仅 UI 层生效、服务端不强制 → 审批/状态/金额字段可被直接 PATCH 绕过写入(FLS 缺口) #3003 所记录的误读根源。现已把 Zod 描述、字段 liveness 台账(readonly + readonlyWhen)与手写文档(fields.mdxvalidation-rules.mdx)统一改为陈述服务端剥离语义(非 system UPDATE 剥离、insert 豁免)。
  • changeset(@objectstack/spec patch,描述性修正)。

验证

  • packages/spec:253 个测试文件 / 6856 个用例全绿(含 proof-registry 绑定更新)。
  • packages/dogfood:50 个文件通过 / 1 个既有 skip(271 通过 / 3 skip),含新证明与矩阵 ledger 校验。
  • pnpm check:liveness:✓,新 proof tag 已注册、非孤儿。
  • 全仓 pnpm build:70/70 成功。

Closes #3003.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TKR6MTrGunV4p4AUfbKMDU


Generated by Claude Code

…ix its declared contract (#3003)

#3003 reproduced the pre-#2948 gap in production terms: approval/status/amount
columns "protected" only by static `readonly: true` were forged with one direct
same-session REST PATCH on a draft record, self-approving a 4-stage approval on
the released 15.0.0 (RECORD_LOCKED only guards pending flows, and the strip that
closes this — stripReadonlyFields, #2957 — landed after that release cut). The
engine fix is already on main; what was still missing is everything that keeps
the contract from silently regressing or being misread again:

- dogfood proof (showcase-static-readonly.dogfood.test.ts, @proof:
  readonly-static-write): over real HTTP on the showcase app, a non-admin
  owner's PATCH forging `lead_score` returns 200 with the persisted value kept
  while sibling editable fields land, an all-forged payload is a no-op, and
  INSERT may still seed the column (the documented readonlyWhen-symmetric
  exemption).
- authz conformance row `readonly-static-write` (+ HIGH_RISK) so the proof is
  mandatory — deleting it fails CI, not review.
- ADR-0054 proof-registry class bound to `field/readonly`, so the liveness
  ledger's `live` classification must carry the proof.
- contract text: FieldSchema.readonly described itself as "Read-only in UI" —
  exactly the misreading #3003 documents. The Zod description, the field
  liveness ledger (readonly + readonlyWhen), and the hand-written field docs
  now state the server-side strip (non-system UPDATE, insert exempt).

Closes #3003.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TKR6MTrGunV4p4AUfbKMDU
@vercel

vercel Bot commented Jul 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Building Building Preview, Comment Jul 16, 2026 4:52am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:data tests tooling size/m labels Jul 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/dogfood, @objectstack/spec.

98 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via packages/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/cli.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via packages/dogfood, @objectstack/spec)
  • content/docs/permissions/delegated-administration.mdx (via packages/dogfood)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via packages/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

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

Labels

documentation Improvements or additions to documentation protocol:data size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

安全:静态字段 readonly: true 仅 UI 层生效、服务端不强制 → 审批/状态/金额字段可被直接 PATCH 绕过写入(FLS 缺口)

2 participants