The short answer.
The site has no accounts and stores no passwords, so the audit looked at what remains: text a visitor can put on a page, shared limits one visitor can use up, the analytics relay, and the pipeline that builds and deploys the site. Six pull requests fixed what it found, five on 7 November and one on 8 November. This entry lists them.
Search text is escaped everywhere.
The helper that formats interface strings returned its result as trusted HTML, including any text passed into it. A search with no match printed the visitor's own words through it. Text arguments are now escaped before formatting, and only the interface string itself is trusted. A test searches for <b>x</b> in both languages and checks that it comes back as text. 16 pages rendered byte for byte the same before and after.
Redirects stay on this site.
An address built to start with two slashes, or a slash and a backslash, could turn one of our redirects into a link to another host. Redirect targets now collapse those. The redirect from old scan links goes only to HTTPS addresses on the scan archive's host, and the image relay accepts only names shaped like the source's picture names. All 946 image names in the data pass.
One address cannot spend everyone's AI answers.
AI answers draw on a daily allowance shared by all visitors. The per-device limit did not cover every path that spends it: calls to the API could draw the allowance down from one address. Each address now has its own daily share, 150 units by default, about three times what one device's own limit costs. Past it, the answer box shows its limited view. Search itself keeps working.
The MCP server queues, then refuses.
A burst of tool calls could take every database connection and leave none for the website's pages. Tool calls now reserve units before they run: 4 for a search across every kind of text, 1 for anything else, 6 in total. A call that finds no room within 2 seconds gets a 503 with Retry-After: 2, and assistants try again.
The analytics relay forwards only our own traffic.
The site relays its analytics through its own address. The relay forwarded any path and any project token to the analytics provider. It now forwards only the endpoints our pages use, for our project, and answers 404 to anything else. It drops cookies and authorization headers on the way out and Set-Cookie on the way back. The Google tag script is relayed the same way, so the page's content security policy no longer trusts a host that serves any Tag Manager container.
The pipeline deploys only what was merged.
Deploy workflows start when another workflow finishes. They now also require that the earlier run was a push or a manual run in this repository, never a pull request from a fork. The job that merges the nightly data pull request re-reads the exact commit it is about to merge and refuses any file outside data/ and db/model/.
Models read outside text in an empty folder.
The nightly build asks language models to read law text and judgment scans that come from a third-party site. Each such run now starts in a new empty folder with a minimal environment. Shell, file-write and web tools are denied by the tool's own settings, not by a sentence in the prompt.