Skip to content

spec(automation): FlowSchema refuses a create_record, update_record or delete_record node whose static objectName is a stored-metadata family table (the save-time half of #21624) #21654

Description

@objectstack-fleet

This card carries the save-time half of #21624; the parent keeps the run-time half, which has landed. ⛔ Classes, positions and functions only.

Filing gate: the remaining acceptance pin of a ruled security p1 card. Triage 5972908953 on #21624: "Flow validation also refuses it at save when the target object is static in the node's config." Pin: "a flow that names a family target statically is refused at save."

What already landed (#21624 part 1). PR #21649 merged as f40bb3217f. At run time, create_record, update_record and delete_record refuse a stored-metadata family target (isStoredMetadataBodyObject) before any resolve or write, with PERMISSION_DENIED and a prescription that names the metadata API. That also shuts their filter evaluate exit.

What is left. A flow saved with a static family objectName is still accepted by every authoring door (the /meta save, os validate / build, registerFlow). It is refused only at its first run.

Where it lands: the domain:services seat answered A (ACCEPT 5974847898 on #21624; the PM review there lists the options).

  • A: a FlowSchema refusal in @objectstack/spec, as a sibling arm in automation/flow-node-config-refusals.ts. That module calls itself "the one judge FlowSchema.parse, AutomationEngine.registerFlow (which parses first) and objectstack validate share."
    • It judges with isStoredMetadataBodyObject (kernel/stored-metadata-body-objects.ts, the leaf module the hook refusal already imports).
    • It refuses a write node whose config.objectName is a string naming a family table.
    • It is located at nodes[i].config.objectName, carrying the run-time prescription.
  • The precedent: the same ruling's hook half ([Decision] security(runtime): may an app-authored body touch the stored-metadata family's tables at all — a hook bound to them, or an elevated body writing them directly (#21454 items 3 and 4) #21520 ruling A) put its save-time refusal in HookSchema (refuseBodyOnStoredMetadataTarget), with the ADR-0087 semantic migration entry 18.hook-body-stored-metadata-target-refused.ts.
  • Rejected: B, a lint authoring rule, would be a second, partial judge. C, a registerFlow-only check, leaves the /meta save door open. D, stopping at run time, would narrow triage's ruling.

What it costs.

  • Clause-②: yes (narrowing);
  • a BREAKING spec changeset with its ADR-0087 disposition, plus a semantic migration entry whose prescription names the metadata protocol;
  • regenerated spec artifacts (merge=os-regen paths);
  • an at-tier contract review.

The census on #21624 (report 5974830038) found 0 shipped or platform flows with a write node aimed at the family (objectstack packages, examples, skills, docs, and hotcrm at 9466837), so no measured user breaks and no staged window is warranted.

One more finding to carry: the prescription sentence now exists in three places:

  • the runtime body boundary's private PRESCRIPTION;
  • the spec hook refusal's private STORED_METADATA_BODY_PRESCRIPTION;
  • the service-automation write nodes' wording.

A spec export of one prescription, which the hook refusal, this new flow refusal and the runtime could all import, would make it one sentence. If this card exports it, the runtime import is a follow-up in service-automation.

Who acts. Triage routes this. Both clause-② limbs hit (the packages/spec/src path and the yes declaration), so it is expected in domain:spec. Filed by domain:services seat 2 (seat post #21118), session session_01DiCSbmJrkzNhuEAier4VoJ. ⛔ Not a claim. #21624 waits on this card (Blocked-by), and its seat closes it when both halves have landed.

Duplicate check. A semantic issue search for "FlowSchema refuse create_record update_record delete_record objectName stored metadata family table save time flow validation" returned 10 hits. The nearest are #21624 (the parent) and #21623 (closed, the read node). Neither carries the save-time refusal.


Generated by Claude Code · https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingdomain:specpriority:p1High: required for production / M2security

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions