Skip to content

An app-declared permission baseline REPLACES the platform member_default instead of composing with it — every built-in Account destination 403s for members of any such app #7555

Description

@huangyiirene

Security-adjacent. The observed failure is fail-closed (403s, no data exposure), but the mechanism — one app's baseline silently displacing the platform baseline for the everyone anchor — is a permission-composition defect, and the same displacement decides what an app's members do keep.

Symptom

A fresh member (purpose-signed-up, no positions beyond the implicit ones) is served the whole Account app and can use none of it:

  • All 10 Account-app nav ids are served to the member.
  • 7/7 of the objects backing those entries answer 403 on read.

So every built-in Account destination is a dead end for that persona, while the nav still advertises all of them.

Expected: either the platform baseline still applies to members of an app that declares its own baseline (so the built-in Account destinations keep working), or the entries whose objects the caller cannot read are pruned from the served nav. Neither happens.

Root cause

The report locates the chain, at this level:

  1. The showcase declares its own baseline set and binds it to the everyone anchor. Boot log, verbatim:

    [security] baseline set bound to everyone anchor (ADR-0090 D5)
    
  2. That binding replaces rather than composes with the platform member_default. Measured breadth: platform member_default grants 23 objects — including all the Account ones — while showcase_member_default grants 7.

  3. Therefore this is not a showcase-specific fixture problem: every built-in Account destination dies for members on any app that declares a baseline.

  4. The second, unprunable half: the Account nav entries are gated with requiresObject, not requiredPermissions, so the nav server has no signal with which to prune an entry whose backing object the caller cannot read. The entry is served regardless of the 403 that follows.

The report does not name the exact code site of the replace-vs-compose behaviour — it locates it at the ADR-0090 D5 everyone-anchor binding. The presumed home is the baseline/permission-set assembly in packages/plugins/plugin-security; confirm the site before fixing rather than assuming it from this card.

Reproduction

  1. Boot the showcase app; confirm the boot log line [security] baseline set bound to everyone anchor (ADR-0090 D5).
  2. Sign up a fresh member and sign in.
  3. Read the served nav for the Account app — all 10 nav ids are present.
  4. For each of the 7 objects backing those entries, GET /api/v1/data/<object> as that member — all 7 answer 403.

Reproduced with two independent member sessions.

Related

Source

Extracted from the QA run #7514 (framework a86db17, console 09987b68).

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions