Skip to content

plugin-security: the record-grained explain verdict for read asks sharing without the caller's read depth — record.visible is false on rows the caller's find returns #19986

Description

@objectstack-fleet

Filing gate ① — a product defect with a named site and a reproduction (class a).

The defect

POST /api/v1/security/explain with { object, operation: 'read', recordId } answers decision.record.visible: false (decidedBy: 'sharing') for rows that the same caller's find through the real enforcement stack returns.

Direction: fails closed. The report under-states read visibility; enforcement is correct.

Site (read by the seat on origin/main 2c1011b01b)

packages/plugins/plugin-security/src/security-plugin.ts, in explainAccessForCaller's dependency wiring:

? { sharingReadFilter: (o: string, c: any) => sharing.buildReadFilter(o, c) }

It passes the bare context, with no __readScope. The enforcement middleware (step 2.6) stamps __readScope before plugin-sharing's buildReadFilter reads it, so an org- or unit-depth reader gets a null sharing filter on the real path and an owner-only one in explain.

Measured (the #19963 dev's one-time probe on 2c1011b01b; ⛔ not re-run by this seat)

Real SecurityPlugin + SharingService + the security and sharing middleware, the platform member_default baseline, an unset (private) OWD:

  • explain(read) for a principal whose read depth is org answers visible: false, decidedBy: sharing, on a shared, an owned and an unshared row.
  • The same find through the middleware chain returns each row: the sharing filter is null because __readScope is org, and the AST where carries only the id and organization_id terms.
  • Control: a principal with own read depth agrees on both sides.

Fix site and shape

The producer is the explain wiring, not a consumer:

⛔ No objectui change: a consumer ignoring record.visible would show rows where the server refuses.

Serial: after PR #19984 (same region of the same file).

Dedupe

Semantic issue search, open and closed, on objectstack-ai/objectstack:

Seam: spec:ExplainDecision.record.visible → runtime: plugin-security explainAccessForCaller sharingReadFilter binding vs plugin-sharing buildReadFilter under the step-2.6 __readScope stamp.

Dedupe words: explain read visible __readScope · sharingReadFilter bare context · explain read org depth under-report · buildReadFilter readScope explain · explain hides readable row


Generated by Claude Code

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions