From 01bf476d51ecafa5cc8bc239a2bc802ca99edc6f Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Thu, 3 Sep 2026 14:35:58 -0700 Subject: [PATCH] Require view-instance permission for /-/allowed Refs GHSA-hp2x-vx2r-6vxg Co-authored-by: Alex Garcia <15178711+asg017@users.noreply.github.com> --- datasette/views/special.py | 1 + tests/test_permission_endpoints.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/datasette/views/special.py b/datasette/views/special.py index a77f221f..b3bfeb5c 100644 --- a/datasette/views/special.py +++ b/datasette/views/special.py @@ -311,6 +311,7 @@ class AllowedResourcesView(BaseView): has_json_alternate = False async def get(self, request): + await self.ds.ensure_permission(action="view-instance", actor=request.actor) await self.ds.refresh_schemas() # Check if user has permissions-debug (to show sensitive fields) diff --git a/tests/test_permission_endpoints.py b/tests/test_permission_endpoints.py index c54bfbfd..774064fd 100644 --- a/tests/test_permission_endpoints.py +++ b/tests/test_permission_endpoints.py @@ -494,3 +494,31 @@ async def test_execute_sql_requires_view_database(): ) finally: ds.pm.unregister(plugin) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("path", ["/-/allowed", "/-/allowed.json?action=view-table"]) +async def test_allowed_requires_view_instance(path): + """ + GHSA-hp2x-vx2r-6vxg: /-/allowed should be gated like its /-/rules sibling. + + An actor who is denied view-instance gets 403 from / and /-/rules, but + /-/allowed (HTML and JSON) currently returns 200 to the same actor. + """ + ds = Datasette(config={"allow": {"id": "alice"}}) + await ds.invoke_startup() + db = ds.add_memory_database("live") + await db.execute_write("CREATE TABLE IF NOT EXISTS t (id INTEGER PRIMARY KEY)") + await ds.refresh_schemas() + + assert (await ds.client.get("/")).status_code == 403 + assert (await ds.client.get("/-/rules.json?action=view-table")).status_code == 403 + + response = await ds.client.get(path) + assert response.status_code == 403 + + # Alice is still allowed + response = await ds.client.get( + path, cookies={"ds_actor": ds.client.actor_cookie({"id": "alice"})} + ) + assert response.status_code == 200