Skip to content

fix(security)!: ADR-0090 follow-ups — driver tenant wall, batch-insert marshaling, scoped counts, vocabulary leftovers#2745

Merged
os-zhuang merged 1 commit into
mainfrom
fix/adr-0090-followup-issues
Jul 10, 2026
Merged

fix(security)!: ADR-0090 follow-ups — driver tenant wall, batch-insert marshaling, scoped counts, vocabulary leftovers#2745
os-zhuang merged 1 commit into
mainfrom
fix/adr-0090-followup-issues

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes #2722, #2723, #2724, #2734, #2735, #2737 — every issue filed while dogfooding the ADR-0090 showcase permission zoo (#2739), each root-caused on the live runtime before fixing.

Runtime fixes

#2734 — driver tenant wall hid every global row (Setup Access Control empty on fresh deployments)

applyTenantScope (driver-sql) used strict organization_id = :tenantId. Every platform bootstrap row (sys_position, sys_permission_set, sys_business_unit) and every pre-org first-boot seed is org-less → any logged-in admin saw ZERO rows, by-id reads 404'd, and the whole RBAC surface was un-administrable over REST. Now: (org = :tenant OR org IS NULL) — a NULL tenant column is a GLOBAL/platform row belonging to no other tenant; rows stamped with a different org stay invisible exactly as before (cross-tenant isolation tests unchanged + 3 new global-row tests).

#2735 — bulkCreate skipped write-side marshaling (seed batches silently failed)

The batch insert path (the seed/import common case since #2678) handed raw object values to the SQLite binder ("Wrong API use: tried to bind a value of an unknown type") — showcase accounts/tasks/field-zoo seeded ZERO rows on every fresh boot. bulkCreate now mirrors create(): per-row formatInput + applyWriteColumnMap + timestamp stamp, and formatOutput on the read-back. New regression test with location/json/array fields.

#2737total unscoped (row-count oracle) + "admin sees less than the auditor"

engine.count()/aggregate() built a LOCAL ast inside the executor, discarding the RLS/OWD filters the security/sharing middlewares inject into opCtx.ast.whereGET /data/:object returned scoped records with a RAW-table total. Both now carry the ast on the opCtx exactly like find() (4 new tests asserting the driver receives the scoped where). The second observation (platform admin's wildcard VAMA "not widening" private reads) turned out to be the #2734 tenant wall hiding the member's org-less note — with the wall fixed, the admin correctly sees all private rows via the superuser wildcard, no evaluator change needed.

Vocabulary leftovers (one-step renames, no aliases)

Verification

  • Full suite: 126/126 turbo tasks green (incl. dogfood 201 tests; the D7 canary in showcase-d7-default-profile was already migrated in feat(showcase,docs): ADR-0090 permission-model zoo + full docs alignment #2739).
  • check:api-surface unchanged; spec json-schema + references/security/rls.mdx + references/ui/portal.mdx regenerated.
  • Fresh objectstack dev --seed-admin boot, browser-verified with zero workarounds (previously required sqlite org-stamp hacks):
    • boot log: 0 seed insert failures (was 3 failed batches);
    • Setup → 权限集 renders all 12 permission sets, 业务单元 renders the 5-unit tree — on FIRST boot;
    • Showcase → 客户 grid renders all 13 seeded accounts (location JSON fields intact);
    • member's Personal Note list: 1 row + "1 条记录" (count == records); auditor sees 2 (VAMA); admin sees 2 via the superuser wildcard;
    • REST matrix: member total 1 | records 1, auditor/admin total 2 | records 2.

🤖 Generated with Claude Code

…t marshaling, scoped counts, vocabulary leftovers

Runtime (closes #2734, #2735, #2737):
- driver-sql applyTenantScope: NULL organization_id = GLOBAL/platform row —
  (org = :tenant OR org IS NULL). Fresh deployments' admins saw ZERO rows in
  sys_position/sys_permission_set/sys_business_unit (Setup Access Control
  empty) and no first-boot seeds; cross-tenant rows stay hidden as before.
- driver-sql bulkCreate: run each row through formatInput/applyWriteColumnMap
  like create() — raw {lat,lng} objects crashed the SQLite binder and
  silently failed whole seed batches (accounts/tasks/field-zoo empty).
- engine count()/aggregate(): carry the AST on the opCtx so middleware-
  injected read filters (RLS/OWD) apply — total was an unscoped row-count
  oracle while records were scoped.

Vocabulary (closes #2722, #2723, #2724):
- PortalSchema.profiles → positions, with a loud tombstone rejecting the
  removed key (FROM→TO in the message); showcase Client Portal migrated and
  now admits a real declared position (client_portal_user).
- RLSUserContextSchema.role → positions (dead field; runtime already used
  positions). API surface unchanged.
- sys_record_share.recipient_type 'role' → 'position' + ShareRecipientType
  contract aligned with the spec zod enum; plugin-sharing translations
  regenerated (fixes the pre-stale sys_sharing_rule block) with zh/ja labels
  patched per the generated-file contract.

Verified on a fresh `objectstack dev` boot: zero seed failures; admin sees
all RBAC tables and seeds on FIRST boot with no workarounds; member
total==records; platform-admin wildcard VAMA reads private rows (the old
"admin sees less than the auditor" was the tenant wall hiding the member's
org-less note). References regenerated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 10, 2026 5:22am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests protocol:ui tooling size/l labels Jul 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 4 package(s): @objectstack/objectql, @objectstack/driver-sql, @objectstack/plugin-sharing, @objectstack/spec.

100 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 @objectstack/objectql, 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/driver-sql, @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 packages/objectql, @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/migration-from-objectql.mdx (via @objectstack/objectql)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/vercel.mdx (via @objectstack/objectql)
  • 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/glossary.mdx (via @objectstack/driver-sql)
  • 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/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/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/objectql)
  • content/docs/permissions/authentication.mdx (via @objectstack/objectql)
  • content/docs/permissions/authorization.mdx (via packages/plugins/plugin-sharing, @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via packages/plugins/plugin-sharing, @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/anatomy.mdx (via @objectstack/driver-sql)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/objectql, @objectstack/driver-sql, @objectstack/plugin-sharing, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/index.mdx (via @objectstack/objectql, @objectstack/driver-sql)
  • content/docs/protocol/objectos/lifecycle.mdx (via @objectstack/driver-sql, @objectstack/spec)
  • content/docs/protocol/objectos/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/runtime-capabilities.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/plugins/plugin-sharing, packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/objectql, @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 packages/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/objectql, @objectstack/driver-sql, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/objectql, @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.

@os-zhuang os-zhuang merged commit afa8115 into main Jul 10, 2026
17 checks passed
@os-zhuang os-zhuang deleted the fix/adr-0090-followup-issues branch July 10, 2026 05:31
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:ui size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ADR-0090 D3 rename miss: exported RLSUserContextSchema still carries a dead role field

1 participant