Commits

Security audit, client update button, and the 502 that explained everything

An audit with a blocking hook uncovers silent failures; the

There are days when you program new things. And there are days when you spend your time looking closely at what you had already programmed, with a flashlight. October 1st was the second kind: a complete security audit of the project, a new button that updates client websites on its own, and a 502 error that taught me how a server's gateway works on the inside.

The inspection: finding the holes before others do

A security audit is like a building's technical inspection: you go system by system looking for cracks before someone takes advantage of them. In this case, the review was done by an AI agent with a special hook (Mimosa) that directly blocks any commit —any code delivery to the repository— if it detects serious unresolved problems.

And it found things. The most striking: the web analytics —the system that counts visits— had been broken for a while, failing on every visit, and no one had noticed. A failure like this is silent because what fails is the counter, not the website. There were also the AI keys too visible from the dashboard: the equivalent of leaving the alarm code stuck to the door.

Fixing it properly, not just silencing the warning

What matters about an audit isn't the list of problems, it's closing them with guarantees. So each repair came with its own:

  • Validate what comes from outside: when the AI downloads an image or analyzes a competitor's website, it now checks that the address is public and legitimate at every hop. Without this, a malicious address can make the server talk to its own internal services (an attack called SSRF, which is basically sneaking someone from the inside into the private zone).
  • Bomb-proof feeds: external RSS feeds are now processed with a library that disables the typical traps of the XML format (known as XXE attacks).
  • Automatic tests on every change: continuous integration — the CI, the robot that reviews the code every time you touch it — now also tests the API, not just builds images.
  • Masked keys: the dashboard no longer shows the real keys; it hides them behind a mask, like a password with asterisks.

All verified with 18 new tests. And at the end, the litmus test: a new release applied from the dashboard button to a real client website, and the analytics responding with a 204 —the HTTP code that says "received and OK"— in production.

The button that updates client websites

Here there was an annoying asymmetry: the product's core is updated with a button from the dashboard, but each client's public website was updated manually, via SSH. The day's work wrapped that up: the "Update public website" button now exists.

On the inside, it does an elegant chain: asks GitHub what the exact image of the latest version is (a digest, its digital fingerprint), downloads it, brings it up, and if anything fails, it undoes it. Without manually entering any server. Twelve new tests protect the entire flow.

The 502: the concierge with the old list

And the scare of the day. After applying an update, the dashboard was returning a 502 —the "broken door" code—. The containers were alive and responding from inside, but from outside you couldn't reach them.

The cause is textbook: the server's gateway (a Nginx) keeps a list of "which internal address each service goes to". And it turns out it only consults this list on startup. The update had recreated the containers with new addresses, but the concierge was still pointing to the old ones. Like calling a phone number that changed.

The fix has two layers: configure Nginx to re-query the addresses every 10 seconds (using Docker's internal DNS), and have the updater restart the gateway after each core update, to cover installations that still have the old configuration.

What one takes away

The day summarized the complete cycle I like about this project: audit, fix with tests, publish signed and verify in production with a real client. In the previous diary entry the product's public documentation was presented; this is the day we scrub it from the inside. And the next entry brings the first real rollback and a migration with mystery included.

Other diary entries