github Notifuse/notifuse v32.3

9 hours ago
  • Security: Broadcast data-feed endpoints (broadcasts.refreshGlobalFeed, broadcasts.testRecipientFeed) are no longer a server-side request forgery (SSRF) vector and now require broadcasts:write. The data-feed fetcher used a plain HTTP client with no address validation, so any authenticated workspace member — including a read-only member — could make the server fetch an arbitrary URL (internal services, the private network, or the cloud instance metadata endpoint) and read back the JSON response. The fetcher now uses the SSRF-safe client already used for favicon detection (dial-time rejection of private/loopback/link-local/reserved ranges, redirect re-validation, DNS-rebinding protection), and both service methods enforce the same write permission as broadcast creation. Trusted self-hosted deployments that intentionally fetch feeds from their internal network can opt out with BROADCAST_DATA_FEED_ALLOW_PRIVATE_HOSTS=true.
  • Security: All broadcast operations now enforce workspace permissions. Previously only create/get/refresh/test were permission-checked, so any workspace member — including a read-only member — could update, delete, schedule, pause, resume, cancel, send, and select A/B winners for broadcasts. Mutating operations now require broadcasts:write and listing/test-results require broadcasts:read; unauthorized requests receive 403 Forbidden.
  • Fix: The task scheduler now executes due tasks in-process when the internal scheduler is enabled (TASK_SCHEDULER_ENABLED), instead of dispatching them over HTTP to its own /api/tasks.execute endpoint. In single-instance deployments where the app cannot reach its own public URL (e.g. a pod that is itself the load balancer's backend), the self-call failed with connection refused and left send_broadcast and other tasks stuck pending; HTTP fan-out is still used when the scheduler is disabled (external cron).
  • Fix: Selecting a different email node in the automation editor now refreshes the config panel — the shared template selector (TemplateSelectorInput) cached the first template it resolved and ignored later changes to its controlled value, so switching between email nodes kept showing (and appearing to edit) the first node's template (#353).
  • Fix: Test emails sent from the template editor now honor the template's Reply-To — the transactional.testTemplate path built the message from the modal's options only and never fell back to the template's reply_to, so test emails arrived without a Reply-To header (real automation/broadcast/transactional sends were already unaffected); an explicit Reply-To from the modal's Advanced options still takes precedence (#355).
  • Fix: Workspace members with the workspace write permission ("full access") can now manage contact custom field labels. Previously both the Settings → Custom Fields controls and the underlying save were gated to workspace owners only, so full-access members had no way to add or edit field labels (#354). Custom field labels are now managed via a dedicated, permission-checked endpoint POST /api/workspaces.setCustomFieldLabels (granular workspace:write instead of owner role), mirroring the template-blocks pattern. As a side effect, workspaces.update no longer writes custom field labels — so an owner saving general settings can no longer clobber labels set by a member.
  • Fix: Workspace members with the blog write permission can now manage blog settings — enabling the blog and editing its title, SEO, pagination, and feed configuration. Previously both the Settings → Blog editor and the underlying save were gated to workspace owners only, so a delegated "blog manager" granted blog:write could publish posts and themes but could not enable the blog or change its settings. Blog settings are now managed via a dedicated, permission-checked endpoint POST /api/workspaces.setBlogSettings (granular blog:write instead of owner role), mirroring the custom-field-labels pattern. As a side effect, workspaces.update no longer writes blog settings — so an owner saving general settings can no longer clobber blog config set by a member.
  • Fix: Broadcasts to a double opt-in list no longer reach contacts who never confirmed — recipients whose contact_list status is pending are now excluded from both the recipient count and the send (#344).
  • Fix: Typing into a button's text editor in the email builder no longer puts each character on its own line — StarterKit's TrailingNode was enabled in the button's paragraph-less inline schema, where it falls back to hardBreak and appended a <br> after every keystroke; it is now disabled for the inline editor (#352).
  • Improvement: {{ workspace.base_url }} / {{ workspace.website_url }} now render in the template preview — the /api/templates.compile endpoint injects the workspace object server-side (filling only missing keys, so historical message snapshots are preserved), so any API consumer gets it, not just the console, and the Preview tab no longer renders website_url as empty (#342).
  • Refactor: Extracted shared WorkspaceSettings.ResolveEndpoint and BuildWorkspaceTemplateVars helpers, replacing ~8 duplicated copies of the tracking-endpoint resolution and workspace template-object construction across the send and preview paths.

Don't miss a new notifuse release

NewReleases is sending notifications on new releases.