Skip to content

Unpublished object denied with "the security posture … could not be resolved" — the deny is right, the explanation sends readers to change permissions #10401

Description

@os-zhuang

What happens

A read against an object that exists only as an unpublished draft is refused by the
authorization middleware with:

[Security] Access denied: the security posture of object 'shyx_customer' could not be resolved for operation 'find'

Producer: packages/plugins/plugin-security/src/security-plugin.ts:1420 (the #3545
fail-closed branch, secMeta.unresolved). The sibling prose lives in
packages/plugins/plugin-security/src/explain-engine.ts:1121.

Why the message is wrong for this case

The deny is correct — nothing here argues with #3545's fail-closed stance. The
explanation is what misleads:

  1. It describes an internal resolution step, so every reader — human and model — reads it
    as a permissions problem and goes looking for a sharing rule to change. The object's
    declaration is fine; it simply has not been published.
  2. It is indistinguishable from the genuine #3545 case (an authenticated principal asking
    for an object whose declaration really is missing/corrupt). Two very different remedies,
    one string.

Measured downstream cost (cloud#1481): an end-user AI turn asked "how many customers do I
have?" against a draft-only object and spent seven tool calls / 31 seconds oscillating
between a metadata plane that said the object existed and this refusal, then told the user
the object "is missing its sharing/visibility setting" — confident, professional, and wrong.
On a free plan that single turn also exhausted the daily allowance.

Suggested shape

Where the posture is unresolvable because the object has a pending draft and no active
row
(the runtime already distinguishes these — that is what previewDrafts overlays),
deny with its own code and message, e.g. unpublished_object: "object 'X' is not published
yet"
. Keep the existing message for the true unresolvable case.

The consumer side was deliberately not patched: cloud's ask agent now answers the
draft-only case from the metadata plane before a query is attempted
(objectstack-ai/cloud#1481), but the data plane's own message is the producer's to fix and
is reachable from every other client too (REST, GraphQL, objectui).

Filed from objectstack-ai/cloud#1481 (cloud PR objectstack-ai/cloud#1485).

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions