Skip to content

Compile has no permission gate: a Viewer can compile a NodeType they may only read #3128

Closed

Description

@rbuergi

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 _Activity node under the target, and can un-park a parked type. Recycle requires Update for 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 two CheckPermissionOutcome call sites in the file are Export and Recycle.
  • src/MeshWeaver.Mesh.Contract/Security/Role.cs — Permission.Compile on Editor, absent from Viewer.
  • 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.

Found while fixing #3121 (PR #3127).

Activity

  1. rbuergi commented on Sep 2, 2026

    @rbuergi
    ContributorAuthor

    Fixed by #3145, merged 2026-09-02 19:14:50Z.

    MeshOperations.Compile is now gated on Permission.Compile — the entitlement the role table already declared (Editor/Admin/PlatformAdmin carry it, Viewer and Commenter do not) and that nothing consulted. Tri-state, mirroring Recycle: 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.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions