diff --git a/CHANGELOG.md b/CHANGELOG.md
index 425c098505..d6f1b7bcb3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,14 @@
 
 All notable changes to this project will be documented in this file.
 
+## [4.1.4] - 2023-07-07
+
+### Fixed
+
+- Fix branding:generate_app_icons failing because of disallowed ICO coder ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25794))
+- Fix crash in admin interface when viewing a remote user with verified links ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25796))
+- Fix processing of media files with unusual names ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25788))
+
 ## [4.1.3] - 2023-07-06
 
 ### Added
@@ -29,7 +37,7 @@ All notable changes to this project will be documented in this file.
 - Fix multiple inefficiencies in automatic post cleanup worker ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24607), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24785), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24840))
 - Fix performance of streaming by parsing message JSON once ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/25278), [ThisIsMissEm](https://github.com/mastodon/mastodon/pull/25361))
 - Fix CSP headers when `S3_ALIAS_HOST` includes a path component ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25273))
-- Fix `tootctl accounts approve --number N` not aproving N earliest registrations ([danielmbrasil](https://github.com/mastodon/mastodon/pull/24605))
+- Fix `tootctl accounts approve --number N` not approving N earliest registrations ([danielmbrasil](https://github.com/mastodon/mastodon/pull/24605))
 - Fix reports not being closed when performing batch suspensions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24988))
 - Fix being able to vote on your own polls ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25015))
 - Fix race condition when reblogging a status ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25016))
diff --git a/Gemfile.lock b/Gemfile.lock
index b2d75e9d4a..985e36c20c 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -432,7 +432,7 @@ GEM
       net-protocol
     net-ssh (7.1.0)
     nio4r (2.5.9)
-    nokogiri (1.15.2)
+    nokogiri (1.15.3)
       mini_portile2 (~> 2.8.2)
       racc (~> 1.4)
     oj (3.15.0)
@@ -628,7 +628,7 @@ GEM
       fugit (~> 1.1, >= 1.1.6)
     safety_net_attestation (0.4.0)
       jwt (~> 2.0)
-    sanitize (6.0.1)
+    sanitize (6.0.2)
       crass (~> 1.0.2)
       nokogiri (>= 1.12.0)
     scenic (1.7.0)
diff --git a/app/javascript/flavours/glitch/styles/components/misc.scss b/app/javascript/flavours/glitch/styles/components/misc.scss
index c8c227e0cb..ef9044050d 100644
--- a/app/javascript/flavours/glitch/styles/components/misc.scss
+++ b/app/javascript/flavours/glitch/styles/components/misc.scss
@@ -80,11 +80,7 @@
   }
 
   &.button-secondary {
-    font-size: 16px;
-    line-height: 36px;
-    height: auto;
     color: $ui-button-secondary-color;
-    text-transform: none;
     background: transparent;
     padding: 6px 17px;
     border: 1px solid $ui-button-secondary-border-color;
diff --git a/app/lib/text_formatter.rb b/app/lib/text_formatter.rb
index 3570632dd9..04b34cf193 100644
--- a/app/lib/text_formatter.rb
+++ b/app/lib/text_formatter.rb
@@ -60,7 +60,7 @@ class TextFormatter
       suffix      = url[prefix.length + 30..-1]
       cutoff      = url[prefix.length..-1].length > 30
 
-      <<~HTML.squish
+      <<~HTML.squish.html_safe # rubocop:disable Rails/OutputSafety
         <a href="#{h(url)}" target="_blank" rel="#{rel.join(' ')}" translate="no"><span class="invisible">#{h(prefix)}</span><span class="#{cutoff ? 'ellipsis' : ''}">#{h(display_url)}</span><span class="invisible">#{h(suffix)}</span></a>
       HTML
     rescue Addressable::URI::InvalidURIError, IDN::Idna::IdnaError
diff --git a/app/models/concerns/attachmentable.rb b/app/models/concerns/attachmentable.rb
index f93ee4c919..c0ee1bdce7 100644
--- a/app/models/concerns/attachmentable.rb
+++ b/app/models/concerns/attachmentable.rb
@@ -24,7 +24,7 @@ module Attachmentable
     def self.has_attached_file(name, options = {}) # rubocop:disable Naming/PredicateName
       super(name, options)
 
-      send(:"before_#{name}_validate") do
+      send(:"before_#{name}_validate", prepend: true) do
         attachment = send(name)
         check_image_dimension(attachment)
         set_file_content_type(attachment)
diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb
index 1853604395..9cf643a292 100644
--- a/lib/mastodon/version.rb
+++ b/lib/mastodon/version.rb
@@ -13,7 +13,7 @@ module Mastodon
     end
 
     def patch
-      3
+      4
     end
 
     def flags
diff --git a/lib/tasks/branding.rake b/lib/tasks/branding.rake
index d1c1c9dede..d97c97c99e 100644
--- a/lib/tasks/branding.rake
+++ b/lib/tasks/branding.rake
@@ -40,7 +40,7 @@ namespace :branding do
     output_dest     = Rails.root.join('app', 'javascript', 'icons')
 
     rsvg_convert = Terrapin::CommandLine.new('rsvg-convert', '-w :size -h :size --keep-aspect-ratio :input -o :output')
-    convert = Terrapin::CommandLine.new('convert', ':input :output')
+    convert = Terrapin::CommandLine.new('convert', ':input :output', environment: { 'MAGICK_CONFIGURE_PATH' => nil })
 
     favicon_sizes      = [16, 32, 48]
     apple_icon_sizes   = [57, 60, 72, 76, 114, 120, 144, 152, 167, 180, 1024]
diff --git a/spec/fixtures/files/attachment-jpg.123456_abcd b/spec/fixtures/files/attachment-jpg.123456_abcd
new file mode 100644
index 0000000000..f1d40539ac
Binary files /dev/null and b/spec/fixtures/files/attachment-jpg.123456_abcd differ
diff --git a/spec/requests/api/v2/media_spec.rb b/spec/requests/api/v2/media_spec.rb
new file mode 100644
index 0000000000..89384d0ca3
--- /dev/null
+++ b/spec/requests/api/v2/media_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe 'Media API', paperclip_processing: true do
+  let(:user)    { Fabricate(:user) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
+  let(:scopes)  { 'write' }
+  let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
+
+  describe 'POST /api/v2/media' do
+    it 'returns http success' do
+      post '/api/v2/media', headers: headers, params: { file: fixture_file_upload('attachment-jpg.123456_abcd', 'image/jpeg') }
+      expect(File.exist?(user.account.media_attachments.first.file.path(:small))).to be true
+      expect(response).to have_http_status(200)
+    end
+  end
+end