database_query passed model-written SQL straight to a connection that commits. The
corpus reaches the model's context, and in a parliament anyone able to speak can get
text into the corpus, so that SQL has to be treated as untrusted input.
Two layers now apply. Statements must begin with SELECT or WITH, and multi-statement
payloads are rejected — that is how a write gets smuggled in behind a leading SELECT.
Then the query runs inside SET TRANSACTION READ ONLY, so PostgreSQL rejects writes
itself even if the first check is wrong. Verified both independently: five attack
shapes refused by the guard, and INSERT/DROP still refused when the guard is bypassed
entirely and the database layer is called directly.
share_insight gets the same treatment. It re-executes SQL stored in saved
conversations, which is no more trustworthy than freshly generated SQL.
Neither layer replaces running against a SELECT-only role, and SECURITY.md still says
so — it now describes what the application does rather than what it fails to do.
Tool rename completed as a clean break, no aliases, since there are three users and
six sessions to preserve: arango_search -> search_speeches (it has queried Postgres
since the Arango migration and the name was pure debt), search_motions ->
search_documents, fetch_motion -> fetch_document.
Also renamed fetch_documents -> fetch_speeches. It fetches speeches despite the name,
and sitting next to the new fetch_document it was a trap the model would fall into.
Note for anyone with an MCP client configured against the old names: riksdagen_mcp
exposes these, so the names it advertises have changed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AGPL-3.0-or-later, with a README note offering other terms on request for
newsrooms and public-interest projects.
README rewritten in English, replacing a 55 KB Swedish document that described a
Sweden-only tool. It states plainly what is and is not yet country-agnostic rather
than overselling: the schema still carries Swedish column names, and docs/SCHEMA.md
translates them until the rename lands.
docs/PORTING.md is the guide this whole effort exists for — what a Bulgarian, UK or
EU deployment actually has to write. It is honest about the parts that are real work
(the ingest adapter, data quality, the untranslated UI) and about the assumption
underneath the data model, so nobody invests in an adapter for a parliament that does
not fit.
SECURITY.md documents the database_query issue rather than leaving deployers to find
it, and gives the read-only role to run against. .env.example documents all 48
environment variables, which were previously discoverable only by grep.
CI runs ruff, applies schema.sql to a clean pgvector database, runs the tests, builds
the frontend, and scans for secrets. The schema step exists because that file drifted
from production once already. tsc is non-blocking until the nine inherited type
errors are fixed.
Makefile rewritten around a documented setup/dev/test/lint flow, and carries the
check-fork-divergence target the production fork uses to prove it differs from
upstream only under deploy/prod/.
Also removed the personal GitHub Copilot instructions file.
Verified: no secrets, no personal contact details, and no private IPs or hostnames
in shipped source; backend still imports.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>