All Posts

Page 1 of 22

Re-enable SVG after Rails blocks untrusted libvips loaders

I patched Virtualtrails for GHSA-xr9x-r78c-5hrm (CVE-2026-66066), the Active Storage arbitrary file read via libvips. The fix has Active Storage call Vips.block_untrusted(true) at boot, which turns off libvips loaders marked “unfuzzed” (unsafe for untrusted content). That includes SVG. On Virtualtrails I render share cards as SVG and convert them to PNG with libvips. After the upgrade, that conversion failed with a...

When VPN DNS works in dig but not in curl on macOS

I occasionally use openfortivpn on macOS. The VPN comes up fine, but internal hostnames still fail in apps. curl can’t resolve them. Browsers can’t either. Meanwhile dig internal.example.com returns the right answer. openfortivpn has added VPN nameservers to /etc/resolv.conf, but on macOS that file isn’t what curl, browsers, or ping use. dig still reads it, which is why lookups can...

Bind XPath variables in Nokogiri instead of string interpolation

When you look up XML nodes by attribute value in Nokogiri, it’s tempting to interpolate the value straight into the XPath: doc.at_xpath("//item[@id='#{id}']") That works when id is predictable. It’s shakier when the value comes from a user upload, an API payload, or anything else you don’t control. A value like foo" or @id="bar can break out of the string literal...

A generic Stimulus controller for toggling fieldsets

I was building an achievement editor in Virtualtrails where route creators can place a milestone either as a distance along the route, or as a latitude/longitude pair that gets converted server-side. The form needs to offer both options, but only one should be active at a time — and only the active panel’s fields should be submitted. My first iteration...

Handle Cloudflare Challenge pages when making Turbo requests

How to handle Cloudflare Challenge pages when making Turbo requests

Turbo Drive intercepts link clicks and form submissions, fetching the next page with fetch() and swapping the <body> in place rather than doing a full browser reload. That’s great for smoothness, but it means the browser never performs a real navigation — and some things depend on a real navigation to work. Cloudflare challenge pages are one of those things....

How to test new Github Actions workflows

I recently build out a data processing pipeline that uses Github Actions, and so needed to add several new workflows. I couldn’t for the life of me figure out how to test them. They were going to be initiated either as part of a pipeline (using workflow_call), or manually, using workflow_dispatch. In most cases, each workflow had inputs which needed...

Talk on Plain Language Law at the AI Engineers meetup in Wellington, NZ

Last night I had a great opportuntity to talk about Plain Language Law, a site that I built using my own time and personal development time provided by Ackama. Plain Language Law is a site that turns New Zealand legislation into plain language using LLMs. This was the first meetup I’d presented at, so I really appreciated the invite, and...

Handy access to OS environment variables on Debian

This morning, while checking (yet again) how to install Postgres inside a Debian container, I stumbled across a file that I didn’t know existed - /etc/os-release - this file contains a bunch of handy variables which can be sourced to chuck them into your environment as environment variables: PRETTY_NAME="Debian GNU/Linux 12 (bookworm)" NAME="Debian GNU/Linux" VERSION_ID="12" VERSION="12 (bookworm)" VERSION_CODENAME=bookworm ID=debian HOME_URL="https://www.debian.org/"...

How to set up Rails directories for MCP servers

I’ve recently started on a new Rails application which will define at least one MCP servers, and potentially 2-3. To support this, I wanted to set up a new namespace in my app/ folder to contain all the MCP ‘stuff’ - I also do the same thing for GraphQL mutations, resolvers, queries etc. Zeitwerk has made autoloading and eager loading...

← Previous Page: 1 of 22