Repository navigation
Compile has no permission gate: a Viewer can compile a NodeType they may only read #3128
Description
What is failing
MeshOperations.Compile carries no permission check at all. Recycle requires Update on the target (MeshOperations.cs, hub.CheckPermissionOutcome(resolvedPath, Permission.Update)) and Export requires Export per node; Compile requires nothing. A caller who may only read a NodeType can trigger a Roslyn compile of it, which also mints an _Activity node under it.
Permission.Compile exists and is deliberately scoped — Role.Editor has it ("Space editors ship releases by default"), Role.Viewer does not (Role.Read | Execute | Api) — so the intended entitlement is already modelled. Nothing consults it on this path.
Measured
Deterministic, on a MonolithMeshTestBase mesh with real RLS (ConfigureMeshBase, no PublicAdminAccess), a Viewer on the partition, calling through a real SessionHubFactory session hub:
[PROBE] gate on MESH hub for probe-viewer: granted=False undetermined=False (Update on the target)
[PROBE] Compile ANSWERED: {"status":"Ok","path":"Probe3121/Ty",
"activityPath":"Probe3121/Ty/_Activity/compile-2026090214404154608876ead0444445582aa8de31b29aabd",
"message":"Compile SUCCEEDED."}
Measured both before and after #3127 — that PR fixes the session hub's inert evaluator, which is why create / recycle / export changed answer there. Compile did not change, because there is no check for the evaluator to answer.
Why it was not folded into #3127
It needs a scope decision that PR does not make: is a compile a read or a write?
- The compile itself is arguably a read-shaped, cache-filling operation — and
Role.Editor's own comment says "the pure compilation that fills the cache still runs as System (not the editor)", i.e. the execution is deliberately not the caller's. - But the request is not free: it schedules Roslyn work, writes an
_Activitynode under the target, and can un-park a parked type.RecyclerequiresUpdatefor a strictly smaller reason ("it disposes the node's hub").
So the candidates are Permission.Compile (matches the permission's own name and the Editor/Viewer split) or Permission.Update (matches Recycle, its nearest neighbour). Either is a behaviour change for existing callers and wants a deliberate choice, not a drive-by.
Where to look
src/MeshWeaver.Mesh.Operations/MeshOperations.cs—Compile(string path); the only twoCheckPermissionOutcomecall sites in the file areExportandRecycle.src/MeshWeaver.Mesh.Contract/Security/Role.cs—Permission.CompileonEditor, absent fromViewer.- A Denial Is an Answer — the surrounding rules, and why a check on the wrong hub used to grant everything.
Whichever permission is chosen, the refusal must be rendered as the operation's own envelope (never raised), and a non-verdict must keep its own retryable wording — MeshOperations.IsWriteDenial and RecycleUndeterminedMessage are the existing shapes to reuse.
Activity
rbuergi commentedon Sep 2, 2026
Fixed by #3145, merged 2026-09-02 19:14:50Z.
MeshOperations.Compileis now gated onPermission.Compile— the entitlement the role table already declared (Editor/Admin/PlatformAdmin carry it, Viewer and Commenter do not) and that nothing consulted. Tri-state, mirroringRecycle: granted runs, denied gets an actionable refusal naming the permission, and a fold that reached no verdict is reported as retryable rather than as a denial.Closing by hand: the PR body carries
Fixes #3128.but the merge went through the merge queue, which did not fire the closing keyword.