Simon Willison pushed two Datasette releases on the same day. Both carry the same security fix: a trailing newline in a requested table name could bypass table permissions and expose private rows. The issue was reported by dpfkdlemtp and tracked as GHSA-h547-rmjf-5m2m.

That’s the whole bug. Add a \n to the end of a table name, and the permission check no longer recognizes the table it is supposed to be guarding, while whatever resolves the actual data still does. Two pieces of code looked at the same string and disagreed about what it meant.

Parser confusion never went out of style

This class of bug has been around since at least the 1990s and it has not gotten less common. Anywhere a name gets normalized in one layer and compared literally in another, there is room for a string that means two things at once. Trailing whitespace, Unicode normalization, percent-encoding, case folding, null bytes. Web frameworks fixed the famous instances decades ago, and then everyone built new routing layers on top of them and reintroduced the same gap in application code.

Datasette is a particularly interesting place for it to land, because permission checks on table names are the core of its security model. The whole design is “here is a SQLite database, published to the web, with rules about what strangers can see.” If the identifier feeding the rule engine and the identifier feeding the query engine can drift apart, the model has a hole in it that no amount of careful policy configuration will close.

I don’t think this reflects badly on the project. Small, well-scoped tools get audited by people who actually read the code, which is how you get a report like this instead of a breach writeup eighteen months later. Big opaque platforms fail the same way; you just never hear about the newline.

The 1.0 alpha keeps moving

The 1.0a40 release bundles the fix alongside real feature work. Plugins can now launch and manage background tasks through a new datasette.add_background_task() method, contributed by Alex Garcia. Datasette has also migrated to httpx2, which affects things like the internal datasette.client.get() method. There’s a long list of bug fixes, many coming out of a recent triage effort.

The background task hook is the piece worth watching. Plugins that can run work outside a request cycle are plugins that can poll, sync, index, and schedule. That opens up a lot of what people have been hacking around with external cron jobs, and it also means plugin authors now own a category of failure they didn’t before. A background task that crashes silently, or leaks a connection, or keeps running after the thing that spawned it went away, is not fun to debug in a long-lived process.

Alpha 40 is a lot of alphas. Datasette 1.0 has been in the oven for years, and the pace of these releases suggests Willison would rather ship a correct 1.0 than a fast one. Meanwhile, anyone still on the 0.65 line should take 0.65.5, which exists purely so they don’t have to jump to an alpha to get the permission fix.

If you run Datasette with private tables and any kind of public or semi-public access, update now. If you write plugins, read the httpx2 notes before you assume your HTTP calls still behave.