Source/Stage/Api/StageQueryPerformer.cs hard-codes both authorization answers:
:60 public bool AllowsAnonymousAccess => true;
:66 public bool IsAuthorized(QueryContext context) => true;
Nothing is exposed today. Perform (:69-73) returns null / Array.Empty<DynamicReadModel>() unconditionally, so every modeled query answers empty by construction. This is latent, not live — but it is open-by-default, and it becomes exploitable the moment Perform is implemented, which is the natural next step for the runtime host.
The prerequisite is missing, and that is the more interesting half
The performer is not ignoring authorization it has. There is none to read.
Cratis.Stage.Contracts 2.1.0 gained CommandDefinition.Authorization — a requirement tree carrying policies and their and/or structure (#27). Queries got nothing, because the contract has no query definition at all: Slice holds Command, ReadModel and Specifications, and ReadModelDefinition carries no authorization.
So the asymmetry is: a command's authorization survives an import and a read's does not, and the runtime host has no source of truth to consult even if it wanted to.
This is worth noting against #23, whose gap table lists scoped to <scope> as a missing query modifier but never the absence of a query type itself. The renderer sidesteps this because it consumes ApplicationSyntax, which does carry queries — it is only the contract path that has the hole.
Why this is not #13
#13 is a renderer issue; this is the runtime host (Source/Stage/Api). #13 names this file as evidence rather than as a deliverable, and its stated minimum-viable scope is authorization attributes in generated code — so #13 could legitimately close with these two hard-coded trues still in place. Different component, different fix, different prerequisite.
Suggested direction
Three steps, in order, and only the first is small:
- Make the current state honest. Deny by default, or fail loudly, rather than answering
true to both questions while returning nothing. A stub that silently answers "yes, and anonymous" is the shape that becomes a hole later.
- Give the contract a query definition carrying authorization, mirroring
CommandDefinition.Authorization — the requirement tree already exists and should be reused rather than re-invented, and it can be added as an init property without a binary break.
- Have the performer read it, alongside implementing
Perform.
Step 1 stands alone and is worth doing regardless of when 2 and 3 land.
Related: Cratis/Studio#1139 and the read-model authorization scoping in #29, which fixed the analogous "more permissive than the document" defect on the rendering side.
Source/Stage/Api/StageQueryPerformer.cshard-codes both authorization answers:Nothing is exposed today.
Perform(:69-73) returnsnull/Array.Empty<DynamicReadModel>()unconditionally, so every modeled query answers empty by construction. This is latent, not live — but it is open-by-default, and it becomes exploitable the momentPerformis implemented, which is the natural next step for the runtime host.The prerequisite is missing, and that is the more interesting half
The performer is not ignoring authorization it has. There is none to read.
Cratis.Stage.Contracts2.1.0 gainedCommandDefinition.Authorization— a requirement tree carrying policies and theirand/orstructure (#27). Queries got nothing, because the contract has no query definition at all:SliceholdsCommand,ReadModelandSpecifications, andReadModelDefinitioncarries no authorization.So the asymmetry is: a command's authorization survives an import and a read's does not, and the runtime host has no source of truth to consult even if it wanted to.
This is worth noting against #23, whose gap table lists
scoped to <scope>as a missing query modifier but never the absence of a query type itself. The renderer sidesteps this because it consumesApplicationSyntax, which does carry queries — it is only the contract path that has the hole.Why this is not #13
#13 is a renderer issue; this is the runtime host (
Source/Stage/Api). #13 names this file as evidence rather than as a deliverable, and its stated minimum-viable scope is authorization attributes in generated code — so #13 could legitimately close with these two hard-codedtrues still in place. Different component, different fix, different prerequisite.Suggested direction
Three steps, in order, and only the first is small:
trueto both questions while returning nothing. A stub that silently answers "yes, and anonymous" is the shape that becomes a hole later.CommandDefinition.Authorization— the requirement tree already exists and should be reused rather than re-invented, and it can be added as aninitproperty without a binary break.Perform.Step 1 stands alone and is worth doing regardless of when 2 and 3 land.
Related: Cratis/Studio#1139 and the read-model authorization scoping in #29, which fixed the analogous "more permissive than the document" defect on the rendering side.