Skip to content

fix(rest,runtime): make api-exposure metadata fail-open observable (#3545) - #3567

Merged
os-zhuang merged 1 commit into
mainfrom
claude/complete-scheduled-development-xqcwch
Jul 27, 2026
Merged

fix(rest,runtime): make api-exposure metadata fail-open observable (#3545)#3567
os-zhuang merged 1 commit into
mainfrom
claude/complete-scheduled-development-xqcwch

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

背景

#3391 的独立 follow-up ②(见 #3545)。API 曝露 gate(apiEnabled/apiMethods)在元数据不可解析时故意 fail-open,以免元数据短暂不可用时阻断流量。本 PR 是该 issue「评估 + 决策」的落地。

评估结论

残余安全风险 = 低,现行 fail-open 可接受。 关键框架:这个 gate 是曝露面(surface-area)控制,不是授权边界——每个请求无论 gate 结果如何,都仍要过 auth + ObjectQL 安全中间件(CRUD / FLS / RLS)。所以元数据缺失时 fail-open 无法绕过数据授权;最坏情况只是「作者想从 API 隐藏的某操作」短暂可达(且仍受完整访问控制)。

唯一的缺口是 fail-open 静默:持续性的元数据故障(存储宕机 / schema 文档损坏)期间 gate 无声放行一切,与健康状态无法区分。

分级决策

成因 处理
整个元数据服务未就绪(冷启动 / 注册竞态 / scoped kernel 预热) 保持 fail-open。fail-closed 会在正常启动窗口 405 掉每个请求,且无安全收益(数据调用要么失败、要么独立授权)
元数据读抛错(存储宕机 / 文档损坏,真实故障) fail-open + 记日志(本 PR)
对象可解析、但 enable/apiMethods 存在却不可读(非数组)→ 现在静默当 unrestricted 经 Zod 校验的注册路径不可达(仅 raw/带外写入能造成),收紧到 fail-closed 推迟到曝露语义窗口 #3543,不在此单方面改

改动

  • rest loadObjectItems:对抛出的元数据读记 warn(真实故障),而对合法空 registry(冷启动 [])保持静默——把「真故障」与「空 registry」区分开(issue checkbox ①)。行为不变(仍返回 [] → gate 放行 → 数据层 + 安全层裁决)。
  • runtime api-exposure.ts:把上面的分级决策写进契约 doc(为什么 !def → allow 是刻意的,以及 Shape-B 推迟到 P2:ApiMethod 枚举收缩至 6 原语(breaking,独立版本) #3543)。

契约 / 兼容

对 gate 行为零变更——纯可观测性 + 决策记录。

测试

关联

🤖 Generated with Claude Code

https://claude.ai/code/session_012L8EfEa157Pe6C73qRnaJH


Generated by Claude Code

…3545)

#3545 evaluated the residual risk of the API-exposure gate failing OPEN when
object metadata can't be resolved. Conclusion: acceptable — the gate is a
surface-area control, not the authz boundary (auth + CRUD/FLS/RLS enforce on the
data call regardless), and failing closed would 405 every request during the
cold-start window for no security gain. The one gap was that the fail-open was
SILENT.

- rest: loadObjectItems logs a THROWN metadata read (a real fault) while leaving
  a legitimately-empty registry silent — so a persistent outage, during which
  the gate allows every op unchecked, is diagnosable without cold-start false
  alarms. Behavior unchanged (still returns [] → gate abstains).
- runtime: api-exposure.ts records the #3545 tiered decision in its contract doc
  (keep fail-open when the whole metadata service is unavailable; defer the
  narrow present-but-unreadable-policy widen — unreachable via Zod-validated
  registration — to the exposure-semantics window #3543).

Tests: rest gate suite gains three #3545 cases (thrown read → fail-open + logged;
empty registry → fail-open + silent; enforceApiAccess does not block on throw).
Full rest.test.ts 171 pass; rest + runtime build (CJS/ESM/DTS) clean.

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

vercel Bot commented Jul 27, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 27, 2026 6:31am

Request Review

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/rest, @objectstack/runtime.

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

  • content/docs/api/error-catalog.mdx (via @objectstack/rest)
  • content/docs/api/error-handling-server.mdx (via @objectstack/rest)
  • content/docs/api/index.mdx (via @objectstack/rest, @objectstack/runtime)
  • content/docs/api/wire-format.mdx (via @objectstack/runtime)
  • content/docs/automation/hook-bodies.mdx (via @objectstack/runtime)
  • content/docs/concepts/north-star.mdx (via packages/runtime)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/runtime)
  • content/docs/deployment/index.mdx (via @objectstack/runtime)
  • content/docs/deployment/production-readiness.mdx (via @objectstack/runtime)
  • content/docs/deployment/single-project-mode.mdx (via @objectstack/runtime)
  • content/docs/deployment/vercel.mdx (via @objectstack/runtime)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/runtime)
  • content/docs/permissions/authentication.mdx (via @objectstack/runtime)
  • content/docs/permissions/authorization.mdx (via packages/runtime)
  • content/docs/plugins/index.mdx (via @objectstack/rest)
  • content/docs/plugins/packages.mdx (via @objectstack/rest, @objectstack/runtime)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/i18n-standard.mdx (via packages/rest)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/runtime)
  • content/docs/releases/implementation-status.mdx (via @objectstack/rest, @objectstack/runtime)
  • content/docs/releases/v12.mdx (via @objectstack/rest)

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.

@os-zhuang
os-zhuang marked this pull request as ready for review July 27, 2026 06:52
@os-zhuang
os-zhuang merged commit 3c8cfd1 into main Jul 27, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the claude/complete-scheduled-development-xqcwch branch July 27, 2026 06:52
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 size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants