test(acl): cover who may create a data set, and by which route - #110
Merged
Conversation
The suite proved a per-data-set write grant cannot create a data set, and nothing else. That leaves three gaps. - **No allow side.** A rule that only ever denies is indistinguishable from one that denies everyone: the suite would stay green through a change that made data sets uncreatable, with the failures landing in unrelated fixtures where they read as fixture breakage. - **Only the near-miss principal.** MT_WRITEONLY holds a write grant, just a scoped one. The plain cases - read-only and no-grant - are what would catch a check keyed on "holds any write grant" rather than on the blanket one. - **Only one of the two routes.** `/resources/create` gates DATASET/POLICY nodes with its own check, so proving `/datasets/create` is guarded says nothing about it. That route is the one the typed node surface makes natural - `resources.create(vec![Dataset::new(..)])`. Three Rust tests and one Python one. The Python side stays deliberately thin, per that file's own selection principle: it covers the `/resources` route because that is a different binding path (the typed node union), not because the backend rule needs re-proving. Two things the writing found, both now pinned in place rather than in a comment somewhere: - The lowercase-label case is deliberate. A type-label is canonicalised before it is matched, so a gate comparing the raw string would let `dataset` through. - A policy create needs its `type`. Bean validation runs *before* the ACL gate, so a policy without one is a 400 naming `nodes[0].type` and never reaches the permission check - a test built on an invalid body would pass against a backend with no gate at all. Rust: 17 ACL tests pass (was 14). Python: 10 (was 9). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
olavgg
approved these changes
Sep 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The suite proved a per-data-set write grant cannot create a data set, and nothing else. That leaves three gaps.
/resources/creategates DATASET/POLICY nodes with its own check, so proving/datasets/createis guarded says nothing about it. That route is the one the typed node surface makes natural -resources.create(vec![Dataset::new(..)]).Three Rust tests and one Python one. The Python side stays deliberately thin, per that file's own selection principle: it covers the
/resourcesroute because that is a different binding path (the typed node union), not because the backend rule needs re-proving.Two things the writing found, both now pinned in place rather than in a comment somewhere:
datasetthrough.type. Bean validation runs before the ACL gate, so a policy without one is a 400 namingnodes[0].typeand never reaches the permission check - a test built on an invalid body would pass against a backend with no gate at all.Rust: 17 ACL tests pass (was 14). Python: 10 (was 9).