Skip to content

fix(sparql): require data:write for SPARQL UPDATE - #139

Merged
EthanThatOneKid merged 1 commit into
mainfrom
sparql-update-scope
Oct 18, 2026
Merged

EthanThatOneKid merged 1 commit into
mainfrom
sparql-update-scope

Conversation

@EthanThatOneKid

Copy link
Copy Markdown
Contributor

What

POST /worlds/{worldId}/sparql authorized every request with data:read, so a key issued with ["data:read"] could run INSERT DATA, DELETE WHERE, DROP ALL and so on, and mutate or wipe a world. Fixes #138.

  • The route now parses the query with the engine's own parser (@wazoo/sparql-engine/parser, default options, same as WazooSparqlEngine). It requires data:write for an update and data:read for a query. The decision is made before execution, not from the result kind.
  • An unparseable query returns 400 INVALID_ARGUMENT without reaching the engine.
  • The namespace and world check still runs first, so a foreign key gets 403 without any feedback about its query.

Tests

tests/abuse.test.ts, under "scope enforcement":

  • A data:read key gets 403 naming data:write for each of 7 update forms (INSERT DATA, DELETE DATA, DELETE WHERE, INSERT … WHERE, DELETE/INSERT … WHERE, CLEAR, DROP), and the SDK is never called.
  • A data:read key can still run SELECT and ASK.
  • A data:read+data:write key and the admin key can run updates.
  • An unparseable query returns 400 and is never executed.

I ran the new tests against the unfixed route: the 7 update cases and the unparseable case fail there. With the fix, the full suite passes (226 tests) and typecheck is clean.

Not observed

  • I didn't run this against a deployed environment. deploy-qa and health-qa run only on push to main.
  • I didn't change the OpenAPI description. It still says the route accepts "SELECT, ASK, or UPDATE" without naming scopes. Documenting the scope split belongs in docs.wazoo.dev.
  • Downstream: no contract or schema change. Clients that send updates with a read-only key will now get 403, which is the intended behavior.

🤖 Generated with Claude Code

The SPARQL route authorized every request with data:read, so a key
issued with only data:read could run INSERT DATA, DELETE WHERE or
DROP ALL and mutate or wipe a world. Parse the query before
authorizing and require data:write for updates, data:read otherwise.
An unparseable query is rejected as INVALID_ARGUMENT before it runs.
The namespace and world check still runs before parsing.

Fixes #138

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@EthanThatOneKid
EthanThatOneKid merged commit a36580d into main Oct 18, 2026
7 checks passed
EthanThatOneKid added a commit to wazootech/docs.wazoo.dev that referenced this pull request Oct 18, 2026
* docs: stop claiming Worlds is an append-only ledger

Worlds stores current state: updates delete and insert quads, and no
history, timestamp or per-quad provenance is kept. Reword every Worlds
claim of an append-only or chronological ledger, immutable or per-quad
provenance, and queryable past states to what is true, and drop the
"When Worlds fits" bullets that only sold history. Claims that describe
other products (Graphiti's bi-temporal edges, Kafka's logs) stay.

update.mdx also called SPARQL on the data plane read-only; it accepts
SPARQL UPDATE, which requires data:write (wazootech/worlds-api#139).

Fixes #191

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(update): name the planned append-only change log

History is not retained yet; point to the planned change log, which is the part that will be append-only (wazootech/worlds-sdk-ts#221). The world's current state stays mutable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@EthanThatOneKid
EthanThatOneKid deleted the sparql-update-scope branch October 18, 2026 21:55
EthanThatOneKid added a commit that referenced this pull request Oct 18, 2026
The route description now says SELECT and ASK need data:read and SPARQL UPDATE needs data:write (enforced since #139), and that an unparseable query returns 400. Snapshot regenerated with sync:openapi.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SPARQL UPDATE is authorized with data:read, so read-only keys can mutate worlds

1 participant