Commit Graph
569 Commits
Author SHA1 Message Date
Linus Rath 6f1cad1c98 fix: close theme CSS sanitizer bypasses and enforce :root/.dark selectors
The theme sanitizer was a short denylist. It missed protocol-relative
`url(//host)`, schemes and keywords spelled with CSS escapes
(`url(\68ttps:...)`, `@\69mport`), `image-set("https://...")` and
`@font-face` sources, so a theme could load remote resources from the
app document. `validateThemeSelectors` was never called, so theme CSS
could also target `input[type=password]` or restyle real UI. The admin
routes and the zip validator only sanitized when the safety check
failed, and CSS returned by the `onThemeBeforeApply` plugin hook was
injected as-is.

- every `url()` except a `#fragment` and every `image-set()` is removed
  with its arguments; a banned construct spelled with escapes drops the
  whole stylesheet
- theme CSS keeps only `:root` and `.dark` rules (no nested rules),
  plus `@font-face`, `@keyframes` and `@media`/`@supports` wrappers
- sanitizing is unconditional everywhere, including plugin-transformed
  CSS; compiled token values may not contain escapes or `url()`
2026-09-26 14:36:02 +02:00
Linus Rath 1f19796cc2 fix: ignore admin status from a server the admin never configured (GHSA-j867-89p4-v8hm)
The Stalwart admin check only bailed while `allowCustomJmapEndpoint` was
on. A context cookie minted while it was on keeps naming the
user-chosen server, and the envelope has no expiry, so after the admin
turned the switch off that server could still answer the admin probe
and mint a Bulwark admin session. The check now also requires the
cookie's server to be an admin-configured one.
2026-09-26 14:20:36 +02:00
Linus Rath 76154187fa fix: only let a Stalwart superuser into the admin dashboard, not a tenant admin (GHSA-v6hr-cmxm-pv73)
The Stalwart admin check probed `x:Account/query`. Stalwart's default
tenant-admin role carries `sysAccount*`, so an administrator of a single
tenant passed and, in the default `auto` mode, was signed into the
Bulwark dashboard, which configures the whole instance for every
tenant. The `auto|password|off` switch added for #870 left the default
at `auto`, so the advisory never held on a default install.

The probe is now `x:Tenant/query`, which needs `sysTenantQuery`: by
default only the server-wide superuser holds it.
2026-09-26 14:19:58 +02:00
Linus Rath dd796d7325 fix: serve WOPI file downloads as inert attachments
GetFile answered on the webmail origin with the Content-Type stored in
the WOPI token, which for a mail attachment comes straight from the
launch request and for a Files node is a user-settable property. No
CSP, nosniff or disposition was added, since `/api/*` skips the proxy.
A user could mint a token for their own blob typed `text/html` and have
a victim open the GetFile URL, running script in the webmail origin.

GetFile now always sends `application/octet-stream` with
`Content-Disposition: attachment`, `nosniff` and a sandboxing CSP, and
refuses browser loads (any `Sec-Fetch-Dest` other than `empty`). The
editor fetches it server-to-server, so it is unaffected.
2026-09-26 14:16:42 +02:00
Linus Rath 9b9b4f7ef1 fix: reject cross-origin writes to the JMAP passthrough and other cookie-authed routes (GHSA-9mvj-98f5-9q6g)
The origin gate in the proxy only covered `/api/auth/*`. The JMAP
passthrough, `/api/settings`, `/api/wopi/launch`,
`/api/plugin-approval-status`, `/api/fetch-ical`, `/api/translate`,
`/api/caldav/discover`, `/api/calendar-agenda` and `/api/webdav` all act
with the session cookie and had no origin check. A page on a same-site
sibling origin receives SameSite=Lax cookies, so it could run arbitrary
JMAP as the signed-in user, including sharing mailboxes to a third
account.

The proxy now gates every `/api/` route except the WOPI file endpoints
the office editor calls server-to-server, and each of these handlers
checks again. The proxy also decides on the decoded path, so an escaped
spelling such as `/api/%61uth/session` no longer skips the gate.
2026-09-26 14:08:44 +02:00
Linus Rath 11eb0bfb16 fix: keep the Stalwart master password out of impersonated sessions
An impersonation handoff sealed `<target>%<master>` and the master
password into the session cookie, and `PUT /api/auth/session` hands that
cookie's contents to the browser on every restore. Anyone who received
a handoff could read a credential that signs in as every mailbox and
into Stalwart administration, with no expiry and no audit trail.

The impersonate route now uses the master credential server-side, once,
to create an app password on the target mailbox that expires after
8 hours. Only that app password reaches the cookies. Signing out
revokes it through a sealed grant cookie, and PUT refuses any session
that still holds the configured master password, so cookies minted
before this change are dropped instead of returned.
2026-09-26 14:05:05 +02:00
Linus Rath 3823d2da17 fix: sign in and show addresses on internationalized domains #1100 2026-09-24 21:15:22 +02:00
Linus Rath e33ab899b0 fix: leave out the id token when sign-in cookies would overflow nginx's header buffer #1096 2026-09-24 18:23:11 +02:00
Linus Rath f97218375e fix: end the identity provider session on sign-out #905 2026-09-23 20:44:46 +02:00
Linus Rath f8592bc6b7 fix: accept oauth endpoints on the configured issuer's own host #1028 2026-09-23 19:26:07 +02:00
Linus Rath 7e5b996ee6 feat: open office attachments in mail read-only in the document editor #1047 2026-09-23 19:01:47 +02:00
Linus Rath dc9d609c43 fix: re-authenticate against the account's own server when linking the mobile app 2026-09-23 18:35:15 +02:00
Linus Rath 691cde6468 fix: explain why calendar subscriptions fail with not authenticated #1073 2026-09-23 18:31:27 +02:00
Linus Rath a4eac38834 feat: add login page toggles, including hiding the version, to admin settings #1068 2026-09-23 18:21:21 +02:00
Aaron Guise 887b9e7f92 feat(notifications): add toggleable inbox-only push notifications ✨ 2026-09-23 11:07:58 +12:00
Linus Rath 06a2e37cd9 fix: load app styles on the global error page 2026-09-22 19:39:39 +02:00
Linus Rath 3c59e811a5 chore: remove unused files and dependencies 2026-09-22 19:36:33 +02:00
Linus Rath 9893b0a8ad fix: list every file when the account exceeds maxObjectsInGet #1069 2026-09-22 00:17:44 +02:00
Linus Rath 106bc07f04 feat: add OpenID Connect login to the Stalwart Lite bundle 2026-09-21 23:45:08 +02:00
Linus Rath 77ef60caae feat: resolve deep links to the local instance 2026-09-20 19:59:37 +02:00
Linus Rath d7548a4bab refactor: migrate icon set from Lucide to Tabler Icons 2026-09-19 13:32:41 +02:00
Linus Rath ab67d9b07e feat: add "Flat fields" theme 2026-09-19 13:01:07 +02:00
Linus Rath fdfcb1711e feat: add Bulwark Lite build for Stalwart Applications 2026-09-19 00:53:53 +02:00
Linus Rath 5de98bc04b feat: add Bulwark Lite static export build 2026-09-17 23:54:33 +02:00
Linus Rath e055ee8201 fix: verify credentials upstream before minting identity cookies (GHSA-wxcm-j4jc-9fxq) 2026-09-17 20:48:42 +02:00
Linus Rath b7f53a3a8b fix: bind a purpose into encrypted payloads (GHSA-cqqx-mjcf-mh55) 2026-09-17 20:16:51 +02:00
Linus Rath e908aee994 fix: reject cross-site requests to /api/auth/* to stop session fixation (GHSA-qvr9-m8cq-7wvg) 2026-09-17 20:01:35 +02:00
Linus Rath 565d5c1095 feat: choose exact-address or same-domain matching for reply from received address #1000 2026-09-09 19:27:08 +02:00
Linus Rath c395226926 fix: reject wrong passwords server-side so the browser never shows its Basic Auth dialog #969 2026-09-09 19:21:40 +02:00
Linus Rath 8d8ef7e2f1 fix: unreadable native <select> option lists in dark themes #999
The browser paints a native <select>'s option list itself, so the
bg-muted / text-foreground classes on the control never reach it. Themes
whose --color-muted is translucent (Aurora Glass dark uses
rgba(255,255,255,0.05)) composited to a light popup with near-white
text, leaving every unselected option almost invisible.

Pin option background/colour to the popover tokens, which every built-in
theme defines as an opaque surface, and set color-scheme: dark on
selects under .dark so Safari (which ignores option colours) renders a
dark popup too. Fixes all ~40 native selects at once.

Adds a stylesheet test that locks the rules and asserts every built-in
theme's --color-popover stays opaque in each variant.
2026-09-09 18:30:59 +02:00
Linus Rath 07bbde4786 fix: make the iCal subscription size limit configurable and show the real error #692 2026-09-07 22:34:47 +02:00
Linus Rath 928e2e3794 feat: add a server-side switch for the Stalwart JMAP passthrough #904 2026-09-07 22:34:47 +02:00
Linus Rath e98109b80d fix: require a session for the translate API #903 2026-09-07 22:34:47 +02:00
Linus Rath e095805851 fix: use the selected server's issuer for SSO discovery #952 2026-09-07 22:34:47 +02:00
Linus Rath b4f12b6786 fix: resolve push previews for shared and group mailboxes #839 2026-09-07 22:34:47 +02:00
Linus Rath af234f64a4 fix: drop max_age=0 from OIDC re-authentication requests #938 2026-09-07 22:34:47 +02:00
Linus Rath 2c104f8191 Merge pull request #939 from paulhenry46/plugin_trad_fix
fix: add translations for plugin fetched from marketplace
2026-09-07 17:54:48 +02:00
Almost-Senseless-Coder 5771a213dd feat(auth): Send prompt=select_account to IdM when adding additional accounts. 2026-09-06 10:19:01 +02:00
Linus Rath c075b79cbb feat: global search palette and search tab in the Pro shell #641 2026-09-01 20:55:13 +02:00
Linus Rath 0db6bfdc64 feat: global search core: query parser, ranking, cross-account providers #641 2026-09-01 20:55:12 +02:00
Paulhenry Saux ee35d71582 fix: add translations for plugind from marketplace 2026-09-01 12:06:03 +02:00
Linus Rath 618636d55d fix: copy mock blob bytes into a plain ArrayBuffer for BodyInit compatibility 2026-08-31 23:47:17 +02:00
Linus Rath 1e9b6ac497 fix: FileNode + blob round-trip in dev mock JMAP, WOPI support for same-origin dev server 2026-08-31 23:45:27 +02:00
Linus Rath ca7fbbe416 feat: WOPI office document editing in Files (Collabora/OnlyOffice/EuroOffice) #425 2026-08-31 23:16:19 +02:00
Michael Richter 84617d6186 feat: OAuth handler for plugins; extended contacts and addressbook API 2026-08-31 09:55:06 +02:00
Linus Rath 39dcc6b219 fix: invalidate persisted update status after an upgrade 2026-08-29 12:46:39 +02:00
Linus Rath 4fe5701b3b feat: default sidebar apps for all users #931 2026-08-29 12:31:17 +02:00
Linus Rath 19756e4337 fix: pin resolved IP for stored custom JMAP endpoints and branding URLs 2026-08-26 23:02:19 +02:00
Linus Rath 3288589e52 fix: pin resolved IP at connect time to block DNS-rebinding SSRF (GHSA-24w9-8r42-8jwm) 2026-08-26 22:11:06 +02:00
Linus Rath 63fa2d2d93 fix: stop notifying for spam by using the JMAP emailPush delivery filter 2026-08-26 21:45:12 +02:00