Skip to content

Runlight 0.1.1

Choose a tag to compare

@github-actions github-actions released this 10 Oct 23:52
· 127 commits to main since this release

This release fixes the findings of a full security and code review of every implementation, plugin, and release workflow.

Security

  • Sign-in no longer sends people to another site when the next path resolves to one starting //, such as /..//example.com.
  • Sign-in limits and two-factor code limits hold under parallel requests in the PHP, Python, Ruby, Java, and .NET versions, which kept their counts in the database and could be raced past. One-time sign-in links, recovery codes, and two-factor codes are now single use across workers on SQLite too.
  • Sign-in counts tries per address across accounts before checking a password, checks only a few passwords at once, and answers busy with a 503 past that, so a flood of sign-ins cannot exhaust memory or CPU.
  • Counts kept per address no longer hold anything an IP address could be worked out from after a day: the key they are hashed with changes daily and old keys are deleted.
  • Tries on a decoy two-factor step no longer count against the real account, so nobody can keep someone else locked out.
  • Invites are deleted when the person who sent them is removed or demoted, and accepting one checks that its sender may still invite.
  • OAuth codes and connect requests are taken atomically, so a code works once.
  • The Elixir, Ruby, and .NET versions turn off MySQL's NO_BACKSLASH_ESCAPES mode on every connection, as the TypeScript library does. On a server with that mode on, a tracking hit could otherwise have injected SQL.
  • Deeply nested JSON can no longer crash the .NET version, and a JSON object with many keys can no longer tie up the Rust version.
  • The Rails engine checks a form posted with _method as the POST it is, so it cannot get past the JSON-only rule for changes.
  • The dashboard no longer opens to anyone because MIX_ENV is unset (Elixir) or the token is None (Python, which now uses runlight.OPEN for an open dashboard). Ruby asks for a shared secret when accounts are on outside development.
  • Imported short links pass the same domain and destination checks as new ones, so an import cannot take over the site's own address.
  • Visitor ids from a CSV import are keyed with a secret made for that import and never stored, so a visitor column holding IP addresses or emails cannot be recovered.
  • Share links and embedded dashboards keep the number of people here now but no longer get the feed of single visits.
  • The assistant's address and the SMTP server refuse link-local and cloud metadata addresses. Embedded dashboards need an https admin origin on its own port, except on localhost. Report links never use an address sent by the person setting them up.
  • Outgoing requests are capped while they are read, drop credentials when a redirect changes origin, refuse https to http, and ignore proxy settings that would get around the check on where they connect.
  • SQLite databases turn on secure_delete, so deleted daily salts do not linger in the file.
  • The PHP drop-in refuses to start when its data folder could be downloaded, and its README shows how to deny it in nginx and Caddy. Files Runlight makes are readable by the server's user alone.

Fixed

  • A session cookie that is not valid percent-encoding reads as signed out instead of failing with a 500.
  • MCP batches hold at most 20 messages.
  • The Go server can no longer crash when work arrives while it waits for background work, and it carries its own time zone data, as the .NET version now does.
  • Python closes its database connections, and Java caps its connection pool at ten. A cancelled request in Rust no longer leaves a transaction or lock open in the pool.
  • Ruby no longer freezes waiting for a database lock under Rails' query cache, and a Runlight failure no longer breaks the app's own pages.
  • The assistant's limits are checked and counted together in the Go, Java, .NET, and Rust versions, and Rust counts them per person.
  • The WordPress plugin reports AI agents after the page has been sent, with the right address on sites in a subfolder. Its settings page, and Drupal's, check the Runlight address after they load.
  • The Next.js instructions in the dashboard show middleware.ts for Next.js 15 and earlier.
  • Releases check that the branch matches GitHub before tagging and push the branch and tag together. The Docker image installs exactly what the lockfile names and is started once before it is published.