Commit Graph

51 Commits

Author SHA1 Message Date
Dan Milne
d49e7ce4f5 Move CSP to nonces; remove unsafe-inline from script-src and style-src
unsafe-inline on script-src neutered CSP as an XSS defense on the login and OAuth
consent pages (the highest-value targets in an IdP). Switch to a per-response
nonce for both script-src and style-src and drop unsafe-inline entirely.

- Add a random per-response nonce generator and apply it to script-src/style-src.
- Remove :unsafe_inline from both directives.
- Nonce the one hand-written inline script (dark-mode detection in the layout).
- Convert the 2 static style="display:none" attributes to class="hidden" (their
  runtime toggle is done via element.style in JS, which CSP does not govern).

importmap-rails (2.2.3) already stamps the nonce onto its generated inline
importmap/module/preload tags, and Turbo (2.0.23) reads csp_meta_tag for its
injected <style>, so no other view changes were needed. Adds an integration test
asserting the enforcing header carries nonces, omits unsafe-inline, and that the
inline script's nonce matches the header.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 20:42:28 +10:00
Dan Milne
cd862c7cd7 Filter code params from logs (TOTP, backup, OAuth code, PKCE)
The TOTP and backup-code form field is named `code`, which was not covered by
the filter list, so live one-time codes landed in production logs. Adding :code
(partial match) also redacts the OAuth authorization `code` and PKCE
`code_verifier`/`code_challenge`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 20:21:41 +10:00
Dan Milne
84ed462f40 Require CLINCH_HOST in deployed environments; drop request-host fallback
determine_base_url fell back to request.host when CLINCH_HOST was unset. Rails
resolves request.host from X-Forwarded-Host behind a trusted proxy, so a spoofed
header could make the forward-auth login redirect point at an attacker origin
(host-header phishing).

- Add config/initializers/clinch_host.rb: fail fast at boot in any non-local
  environment when CLINCH_HOST is blank. It anchors the OIDC issuer, WebAuthn
  RP ID, and login redirect, so it must be explicit, never inferred.
- determine_base_url now uses CLINCH_HOST (guaranteed in production) with a safe
  localhost default for dev/test, and never reads the request host.
- Simplify the spoofed-host regression test now that the fallback is safe.

Verified: production boot aborts with a clear message when CLINCH_HOST is blank,
and boots normally when set.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 08:04:42 +10:00
Dan Milne
2843790cef Apps index access column + summary + admin access checker
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / scan_container (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
The Applications index used to render "All users" whenever an app had
no allowed_groups; under default-deny that's the opposite of the truth.
Replaced with a "No one" badge and, when groups are present, a
"N users · M groups" cell so the access reality is visible at a glance.

Added a small stats strip above the apps table: applications, users
with access, and groups granting access. Backed by preloaded counts in
the controller to avoid N+1.

Added /admin/access — a small "Access check" tool that takes a user
and an application and reports whether the user can reach it, with the
granting group(s) when allowed, and the specific reason when not
(inactive app/user, no allowed groups, or no shared group). Wired into
the admin sidebar.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-07 18:38:56 +10:00
Dan Milne
0e9ec71013 Link the user show page from the admin users index
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / scan_container (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
The admin users index only exposed Edit / Delete actions per row, so
the Accessible-applications panel on the user show page was unreachable
without typing the URL by hand. Adds a View action and turns the email
into a link to the show page — mirroring how the applications and
groups indexes already work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-07 18:26:55 +10:00
Dan Milne
fe68f6e81e Use Tailwind dark: toggles for dark-mode icons
The previous <picture media="(prefers-color-scheme: dark)"> only fires
when the OS is in dark mode. Clinch toggles dark mode with a class on
<html> via dark_mode_controller and Tailwind's @custom-variant dark
(&:where(.dark, .dark *)), so the picture source never swapped when
users clicked the in-app theme toggle. Render both <img> tags and
use Tailwind's dark:hidden / hidden dark:block so the swap follows
whatever strategy Tailwind is configured for.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-07 17:19:36 +10:00
Dan Milne
c5ab7dc2a5 Compact icon uploader shared between light and dark icon fields
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / scan_container (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
Extracts the icon dropzone into a reusable partial so the dark mode
icon gets the same upload / drag-and-drop / paste affordances as the
light icon. Slims the dropzone to a single-row layout (small cloud
icon plus Upload / drag-and-drop / paste hint) and a tiny format hint
below, instead of the previous tall vertically-centred block.
2026-06-07 17:13:52 +10:00
Dan Milne
bfad9c4e9d Generated monogram fallback + optional dark-mode icon per application
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / scan_container (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
When an application has no icon attached, render a deterministic
monogram SVG instead of the generic picture-frame placeholder. Initials
are picked from capital letters in the name (ShelfLife -> SL); fall
back to the first two letters when fewer than two capitals exist
(Audiobookshelf -> AU). Background colour is hashed from the name for
stable per-app identity across visits.

Adds an optional second icon attachment, icon_dark, alongside the main
icon. When present, render a <picture> with a prefers-color-scheme:
dark source so the browser swaps automatically; when absent, the main
icon is used in both modes. The SVG sanitization, content-type fix,
and size/format validation now run over both attachments uniformly.

Bumps to 0.14.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-07 17:02:53 +10:00
Dan Milne
5b41db2c6a Fix FileNotFoundError when uploading an SVG icon
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / scan_container (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
The sanitize_svg_icon before_validation callback called icon.download,
but Active Storage uploads pending blobs in before_save — so at
before_validation time the file only existed in the request tempfile,
not at the configured storage path. Read from the pending attachable
(UploadedFile / IO hash) instead. Guards against the recursive callback
that icon.attach would otherwise trigger by tracking the cleaned
attachable by object identity. Bumps to 0.13.1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-07 16:43:24 +10:00
Dan Milne
03dfdbd83a Default-deny access control with group flags and access enumeration
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / scan_container (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
Replaces the implicit "empty allowed_groups means public" rule with
explicit default-deny across both OIDC and ForwardAuth. Adds two boolean
flags on Group — auto_assign (Keycloak-style auto-join on user create)
and admin (members can reach the admin panel) — and drops the
users.admin column entirely. Adds "Users with access" and "Accessible
applications" panels with via-group badges on the application/user show
pages.

BEHAVIOR CHANGE: a ForwardAuth app with no allowed_groups previously
bypassed authentication entirely; it now returns 403 like any other
unauthorized request. The data migration seeds an "everyone" group and
attaches it to all previously group-less apps to preserve behavior on
existing installs. An "admins" group is seeded and backfilled from any
user with the old admin column.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-07 15:53:27 +10:00
Dan Milne
6b58b685c4 Bump version to 0.12.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 21:18:35 +10:00
Dan Milne
bbfb564e1c Show Clinch, Rails and Ruby versions in sidebar footer; bump to 0.11.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 23:39:11 +10:00
Dan Milne
9663110938 Bump version to 0.10.2
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / scan_container (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2026-05-26 18:32:25 +10:00
Dan Milne
9648b64043 Bump version to 0.10.1
Patch release covering the Ruby 4.0.3, Rails 8.1.3, and transitive
gem updates landed since 0.10.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 00:09:27 +10:00
Dan Milne
2768104c1e Bump version to 0.10.0
Substantial scope since 0.9.0: API keys for forward auth, SecurityMailer
alerts on 8 account-security events, dark mode, Remember-me with proper
browser-session cookie semantics, SvgScrubber for icon XSS, OIDC
auth-code replay revocation, forward-auth caching + rate limiting, and
fixes for broken invitation / password-reset emails.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 00:02:40 +10:00
Dan Milne
2235924f37 Harden OIDC, add SVG sanitization, improve form UX and security defaults
Remove PKCE plain method support (S256 only), enforce openid scope requirement,
filter to supported scopes, strip reserved claims from custom claims as
defense-in-depth, sanitize SVG icons with Loofah, add global input padding,
switch session cookies to SameSite=Lax, use Session.active scope, and remove
unsafe-eval from CSP.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 21:06:51 +10:00
Dan Milne
5505f99287 Add rate limiting and in-memory caching for forward auth endpoint
Rate limit failed attempts (50/min per IP) with 429 + Retry-After.
Cache forward auth applications in a dedicated MemoryStore (8MB LRU)
to avoid loading all apps from SQLite on every request. Debounce
last_activity_at writes to at most once per minute per session.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 11:15:54 +11:00
Dan Milne
65c19fa732 Upgrade to Ruby 4.0.1, bump version to 0.9.0
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / scan_container (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
Replace CGI.parse (removed in Ruby 4.0) with Rack::Utils.parse_query
in application controller, sessions controller, and OIDC tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 21:52:11 +11:00
Dan Milne
fd8785a43d Add API keys / bearer tokens for forward auth
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / scan_container (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
Enables server-to-server authentication for forward auth applications
(e.g., video players accessing WebDAV) where browser cookies aren't
available. API keys use clk_ prefixed tokens stored as HMAC hashes.

Bearer token auth is checked before cookie auth in /api/verify.
Invalid tokens return 401 JSON (no redirect). Requests without
bearer tokens fall through to existing cookie flow unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 21:45:40 +11:00
Dan Milne
a6480b0860 Verion Bump
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / scan_container (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2026-01-05 13:08:22 +11:00
Dan Milne
4a48012a82 Add claims support 2026-01-05 12:00:29 +11:00
Dan Milne
abbb11a41d Return only scopes requested, add tests ( OpenID conformance test )
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / scan_container (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2026-01-02 14:55:06 +11:00
Dan Milne
07cddf5823 Version bump
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / scan_container (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2026-01-02 12:57:28 +11:00
Dan Milne
fed7c3cedb Some beta-checklist updates 2026-01-02 11:53:41 +11:00
Dan Milne
7f834fb7fa Version bump 2026-01-01 15:27:19 +11:00
Dan Milne
93a0edb0a2 StandardRB fixes
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2026-01-01 13:29:44 +11:00
Dan Milne
364e6e21dd Fixes for tests and AR Encryption
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-12-31 16:08:05 +11:00
Dan Milne
9d352ab8ec Fix tests - add missing files 2025-12-31 16:01:31 +11:00
Dan Milne
d1d4ac745f Version bump 2025-12-31 15:48:52 +11:00
Dan Milne
9530c8284f Version bump
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-12-31 10:35:27 +11:00
Dan Milne
bb5aa2e6d6 Add rails encryption for totp - allow configuration of encryption secrets from env, or derive them from SECRET_KEY_BASE. Don't leak email address via web_authn, rate limit web_authn, escape oidc state value, require password for changing email address, allow settings the hmac secret for token prefix generation 2025-12-31 10:33:56 +11:00
Dan Milne
cc7beba9de PKCE is now default enabled. You can now create public / no-secret apps OIDC apps 2025-12-31 09:22:18 +11:00
Dan Milne
32235f9647 version bump
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-12-30 11:58:31 +11:00
Dan Milne
71d59e7367 Remove plain text token from everywhere
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-12-30 11:58:11 +11:00
Dan Milne
99c3ac905f Add a token prefix column, generate the token_prefix and the token_digest, removing the plaintext token from use. 2025-12-30 09:45:16 +11:00
Dan Milne
898fd69a5d Add permissions initializer and missing image paste controller
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-12-29 13:27:30 +11:00
Dan Milne
9cf01f7c7a Bump versoin 2025-12-28 14:43:26 +11:00
Dan Milne
283feea175 Update depenencies, bump versoin 2025-11-30 23:13:25 +11:00
Dan Milne
f8543f98cc Add a subdirectory for active storage
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-11-27 19:12:09 +11:00
Dan Milne
6be23c2c37 Add backchannel logout, per application logout.
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-11-27 16:38:27 +11:00
Dan Milne
67d86e5835 Add Icons for apps 2025-11-25 19:11:22 +11:00
Dan Milne
67f28faaca Improve some front end views. More descriptive error condition reporting. Updates to CLINCH_HOST for better WEBAUTHN 2025-11-12 16:24:05 +11:00
Dan Milne
8e0b2c28eb CSP fixes 2025-11-08 20:01:07 +11:00
Dan Milne
631b2b53bb Fix CSP reporting endpoitn. Fix the SER for CSP
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-11-04 23:22:15 +11:00
Dan Milne
6049429a41 Fix mobile view menu popout. Add an option SENTRY_DSN support, which uses rails event reporting
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-11-04 23:16:28 +11:00
Dan Milne
2b15aa2c40 Add sentry, set csp reporting API 2025-11-04 22:58:32 +11:00
Dan Milne
044b9239d6 Ok - this time add the new controllers we stripped out of inline and add back the csp
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-11-04 18:55:20 +11:00
Dan Milne
fb14ce032f Strip out more inline javascript code. Encrypt backup codes and treat the backup codes attribute as a json array 2025-11-04 18:46:11 +11:00
Dan Milne
57abc0b804 Add webauthn
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-11-04 16:20:11 +11:00
Dan Milne
ddcb297c74 Add comprhensive csp polices and reporting endpoint. Add environment support require for protecting against rebinding attacks on ip addresses
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-10-29 15:37:53 +11:00