Disallow ?_through= if user lacks view-table permission

Refs GHSA-53fc-rhfg-h7qp

Co-authored-by: Alex Garcia <15178711+asg017@users.noreply.github.com>
This commit is contained in:
Simon Willison 2026-09-03 14:35:45 -07:00
commit 577aeb73f0
2 changed files with 37 additions and 1 deletions

View file

@ -1778,3 +1778,34 @@ async def test_next_url_included_by_default(ds_client):
data = response.json()
assert data["next"] is None
assert data["next_url"] is None
@pytest.mark.asyncio
async def test_table_through_requires_view_table_on_through_table():
# GHSA-53fc-rhfg-h7qp issue 3: ?_through= runs a sub-select against the
# caller-supplied through table, so the actor must be allowed to view it.
# Otherwise it is an equality oracle over any column of a denied table.
from datasette.app import Datasette
ds = Datasette(
memory=True,
config={"databases": {"data": {"tables": {"salaries": {"allow": False}}}}},
)
db = ds.add_memory_database("table_through_denied", name="data")
await db.execute_write("create table people (id integer primary key, name text)")
await db.execute_write(
"create table salaries (id integer primary key, "
"person_id integer references people(id), note text)"
)
await db.execute_write("insert into people values (1, 'alice'), (2, 'bob')")
await db.execute_write("insert into salaries values (1, 1, 'TOPSECRET-A')")
await ds.invoke_startup()
# Sanity: anonymous cannot read salaries directly
assert (await ds.client.get("/data/salaries.json")).status_code == 403
response = await ds.client.get(
"/data/people.json?_shape=array"
'&_through={"table":"salaries","column":"note","value":"TOPSECRET-A"}'
)
assert response.status_code == 403, response.text