Require view-instance permission for /-/allowed

Refs GHSA-hp2x-vx2r-6vxg

Co-authored-by: Alex Garcia <15178711+asg017@users.noreply.github.com>
This commit is contained in:
Simon Willison 2026-09-03 14:35:58 -07:00
commit 01bf476d51
2 changed files with 29 additions and 0 deletions

View file

@ -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)

View file

@ -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