Skip to content

fix(spec): cross-validate permission object grants against declared objects#1997

Merged
os-zhuang merged 2 commits into
mainfrom
fix/validate-permission-object-refs
Jun 17, 2026
Merged

fix(spec): cross-validate permission object grants against declared objects#1997
os-zhuang merged 2 commits into
mainfrom
fix/validate-permission-object-refs

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

What

Add the missing permission/profile → object cross-reference check to validateCrossReferences.

Bug

validateCrossReferences validated hook/view/seed/app-nav/action references against the declared object set — but not permissions[].objects grants. So a profile that grants on a non-existent object passed build / validate / test silently:

// objects are crm_lead / crm_case, but the profile grants on short names:
objects: { lead: { allowCreate: true }, case: { allowCreate: true } }  // ← no such objects

The grant then applies to nothing. The authenticated path may namespace-resolve the short name, but the anonymous / explicit-permission-set path does not — so e.g. a public Web-to-Lead INSERT is denied for "roles []", with no diagnostic anywhere. (This is exactly what blocked HotCRM's web-to-lead; see hotcrm#399.)

validateNamespacePrefix's own doc already assumed this check existed here ("…sharing rules, permissions) — but those are checked by the existing validateCrossReferences…"). It didn't. This makes the code match the documented intent.

Fix

Every permissions[].objects key must reference a declared object, or strict validation fails loudly at build time.

Tests

stack.test.ts +2 cases: short/undefined object name → throws; full name → passes. 82/82 spec stack tests pass. Framework examples (app-crm, showcase) already key full names, so unaffected.

🤖 Generated with Claude Code

os-zhuang and others added 2 commits June 17, 2026 09:49
The app-crm example showed Web-to-Lead only via `customer.portal` `anonymousEntry`
— a spec property with NO runtime consumer, so it never worked (404). Add the
mechanism that actually does, and make the example coherent:

- lead.view.ts: `web_to_lead` form view with `sharing.allowAnonymous` →
  live `GET/POST /api/v1/forms/contact-us` (+ /submit).
- sales-roles.ts: `GuestPortalProfile` (isProfile, INSERT-only on crm_lead, keyed
  by FULL object name — the anonymous permission path requires it).
- lead.object.ts: `status` gets `defaultValue: 'new'` so a minimal public create
  satisfies `required` (the option-level `default` is only a UI preselect).
- customer.portal.ts: drop the dead `anonymousEntry` routes; point to the working
  form view instead (re-add when the runtime mounts anonymousEntry).
- security/index.ts + objectstack.config.ts: export + register the guest profile.

Doubles as a CI regression test for the public-form path (builds against the
workspace) — the #1989 flattened-metadata resolution bug would have been caught here.

Verified end-to-end (app-crm, no auth): GET /forms/contact-us → 200 form spec;
POST /forms/contact-us/submit → creates a crm_lead (status=new); GET /data/crm_lead
→ 401 (guests can't read).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…clared objects

`validateCrossReferences` checked hook/view/seed/app-nav/action references against
the declared object set — but NOT permission-set/profile `objects` grants, even
though `validateNamespacePrefix`'s doc already assumed it did. So a profile that
grants on a non-existent object (e.g. a short `lead` instead of the namespaced
`crm_lead`) passed build/validate/test silently. The grant then applies to
nothing: the authenticated path may namespace-resolve the short name, but the
anonymous / explicit-permission-set path does not — so e.g. a public Web-to-Lead
INSERT is denied for "roles []", with no diagnostic anywhere.

Add the missing check: every `permissions[].objects` key must reference a declared
object, or strict validation fails loudly at build time.

- stack.zod.ts: validate permission/profile object grants → object references
- stack.test.ts: +2 cases (short/undefined name fails; full name passes)

82/82 spec stack tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 17, 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 Jun 17, 2026 2:23am

Request Review

@github-actions github-actions Bot added the tests label Jun 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

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

  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/cloud-artifact-api.mdx (via packages/spec)
  • content/docs/concepts/cluster-semantics.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/implementation-status.mdx (via @objectstack/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/concepts/packages.mdx (via @objectstack/spec)
  • content/docs/concepts/setup-app.mdx (via @objectstack/spec)
  • content/docs/concepts/skills.mdx (via @objectstack/spec)
  • content/docs/concepts/webhook-delivery.mdx (via @objectstack/spec)
  • content/docs/getting-started/architecture.mdx (via @objectstack/spec)
  • content/docs/getting-started/cli.mdx (via @objectstack/spec)
  • content/docs/getting-started/core-concepts.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/guides/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/guides/ai-capabilities.mdx (via @objectstack/spec)
  • content/docs/guides/airtable-dashboard-analysis.mdx (via @objectstack/spec)
  • content/docs/guides/analytics-datasets.mdx (via @objectstack/spec)
  • content/docs/guides/api-reference.mdx (via @objectstack/spec)
  • content/docs/guides/business-logic.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/error-catalog.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/field-type-gallery.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/field-validation-rules.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/protocol-diagram.mdx (via packages/spec)
  • content/docs/guides/cheatsheets/query-cheat-sheet.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/quick-reference.mdx (via @objectstack/spec)
  • content/docs/guides/client-sdk.mdx (via @objectstack/spec)
  • content/docs/guides/common-patterns.mdx (via @objectstack/spec)
  • content/docs/guides/contracts/auth-service.mdx (via packages/spec)
  • content/docs/guides/contracts/cache-service.mdx (via packages/spec)
  • content/docs/guides/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/guides/contracts/index.mdx (via @objectstack/spec)
  • content/docs/guides/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/guides/contracts/storage-service.mdx (via packages/spec)
  • content/docs/guides/data-modeling.mdx (via @objectstack/spec)
  • content/docs/guides/deployment-vercel.mdx (via @objectstack/spec)
  • content/docs/guides/driver-configuration.mdx (via @objectstack/spec)
  • content/docs/guides/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/guides/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/guides/formula.mdx (via @objectstack/spec)
  • content/docs/guides/hook-bodies.mdx (via packages/spec)
  • content/docs/guides/kernel-services.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/dashboard.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/field.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/flow.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/index.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/object.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/validation.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/workflow.mdx (via @objectstack/spec)
  • content/docs/guides/packages.mdx (via @objectstack/spec)
  • content/docs/guides/plugin-development.mdx (via @objectstack/spec)
  • content/docs/guides/plugins.mdx (via @objectstack/spec)
  • content/docs/guides/project-scoping.mdx (via @objectstack/spec)
  • content/docs/guides/public-forms.mdx (via @objectstack/spec)
  • content/docs/guides/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/guides/runtime-services/index.mdx (via packages/spec)
  • content/docs/guides/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/guides/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/guides/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/guides/security.mdx (via @objectstack/spec)
  • content/docs/guides/seed-data.mdx (via @objectstack/spec)
  • content/docs/guides/skills.mdx (via @objectstack/spec)
  • content/docs/guides/standards.mdx (via @objectstack/spec)
  • content/docs/guides/troubleshooting.mdx (via @objectstack/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/lifecycle.mdx (via @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/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 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/index.mdx (via @objectstack/spec)
  • content/docs/releases/v9.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 cb352a0 into main Jun 17, 2026
15 checks passed
@os-zhuang
os-zhuang deleted the fix/validate-permission-object-refs branch June 17, 2026 03:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant