Vectrum
Sign In
12 lines · 2.8 KB

Security

  • Serve the web UI over HTTPS if it's reachable beyond localhost, and set PUBLIC_URL. Host and X-Forwarded-* headers are client-controlled, so they never decide cookie security, and only stand in for PUBLIC_URL in development.
  • Branch rules are checked for every way a branch changes: a pre-receive hook asks the server (over a local socket, with a token per push) about every pushed ref, and the web editor, agents' tools and merges check the same rules. The hook fails closed.
  • Webhooks refuse loopback, private, link-local and similar addresses (checked again on every delivery, and redirects aren't followed), unless the admin allows them.
  • Ref names are never passed to git where they could be read as options: pushes of refs with a component starting with - are refused by a pre-receive hook, such refs are ignored when reading, and every revision argument follows --end-of-options.
  • Private repositories show a 404 page to anyone without access, so their existence isn't revealed.
  • OAuth for agents follows the MCP authorization spec: public clients only, PKCE (S256) required, exact redirect URIs (any port for localhost), one-time codes that expire in 5 minutes, access tokens that last an hour, and refresh tokens that rotate on every use (presenting an old one revokes the grant). Only hashes of codes and tokens are stored. The consent page can't be framed, and it shows where it will send you, because anyone can register an application under any name.
  • Release files are always served as application/octet-stream attachments with nosniff and a sandboxing CSP. Uploads signed in with the browser session must come from the site's own origin; scripts use tokens.
  • Raw files are served with nosniff and a sandboxing CSP, so repository HTML/SVG can't run scripts in the app's origin.
  • Links in emails (confirming an address, resetting a password) carry 256-bit random tokens; only their SHA-256 hashes are stored, with the address they went to, so they stop working when the email changes. Reset links work for an hour, once, and are revoked by any password change. At most one link of a kind goes to an account per minute. Asking for a reset link answers the same for unknown accounts and doesn't wait for the email. Links always use PUBLIC_URL, never the request's Host header. Notifications only go to confirmed addresses, as anyone can sign up with someone else's.
  • Two-factor authentication (TOTP, RFC 6238) is checked after the password, before any session exists: the password's session token waits on the server for 5 minutes and 5 codes. Wrong codes also count per account (10 in 15 minutes locks codes out for that long). A code works once (also no older one after it), recovery codes are stored as hashes and deleted when used, and the secret is a hidden field only the server reads. Turning 2FA off takes a code.