diff --git a/app/models/communication/block.rb b/app/models/communication/block.rb
index 1369e18f480bf3ac5acbfd4bdc4871e74834f8ee..aec48f232423fc4ce72c62a8ea11c9427a87db52 100644
--- a/app/models/communication/block.rb
+++ b/app/models/communication/block.rb
@@ -24,6 +24,7 @@
 #  fk_rails_18291ef65f  (university_id => universities.id)
 #
 class Communication::Block < ApplicationRecord
+  include Sanitizable
   include WithUniversity
   include WithPosition
   include Accessible
diff --git a/app/models/communication/extranet.rb b/app/models/communication/extranet.rb
index d48d2a51c9f6cfcc2ed88f03caefa06031441ca4..da5242530e14600fef86bc74bc50f113569307b2 100644
--- a/app/models/communication/extranet.rb
+++ b/app/models/communication/extranet.rb
@@ -35,6 +35,7 @@
 class Communication::Extranet < ApplicationRecord
   self.filter_attributes += [:sso_cert]
 
+  # We don't include Sanitizable because too many complex attributes. We handle it below.
   include WithAbouts
   include WithLegal
   include WithSso
@@ -49,6 +50,8 @@ class Communication::Extranet < ApplicationRecord
   validates :logo, size: { less_than: 1.megabytes }
   validates :favicon, size: { less_than: 1.megabytes }
 
+  before_validation :sanitize_fields
+
   scope :ordered, -> { order(:name) }
   scope :for_search_term, -> (term) {
     where("
@@ -100,4 +103,16 @@ class Communication::Extranet < ApplicationRecord
   def to_s
     "#{name}"
   end
+
+  private
+
+  def sanitize_fields
+    self.color = Osuny::Sanitizer.sanitize(self.color, 'string')
+    self.cookies_policy = Osuny::Sanitizer.sanitize(self.cookies_policy, 'text')
+    self.host = Osuny::Sanitizer.sanitize(self.host, 'string')
+    self.name = Osuny::Sanitizer.sanitize(self.name, 'string')
+    self.privacy_policy = Osuny::Sanitizer.sanitize(self.privacy_policy, 'text')
+    self.registration_contact = Osuny::Sanitizer.sanitize(self.registration_contact, 'string')
+    self.terms = Osuny::Sanitizer.sanitize(self.terms, 'text')
+  end
 end
diff --git a/app/models/communication/website.rb b/app/models/communication/website.rb
index 52a48b87fc60d8ecb08e652ddf6c199fdd42cbf7..2ef448ae985ec89cab6710d43e1074d1f8b3fd6e 100644
--- a/app/models/communication/website.rb
+++ b/app/models/communication/website.rb
@@ -64,6 +64,8 @@ class Communication::Website < ApplicationRecord
   validates :languages, length: { minimum: 1 }
   validate :languages_must_include_default_language
 
+  before_validation :sanitize_fields
+
   scope :ordered, -> { order(:name) }
   scope :in_production, -> { where(in_production: true) }
   scope :for_theme_version, -> (version) { where(theme_version: version) }
@@ -107,6 +109,15 @@ class Communication::Website < ApplicationRecord
 
   protected
 
+  def sanitize_fields
+    self.git_branch = Osuny::Sanitizer.sanitize(self.git_branch, 'string')
+    self.git_endpoint = Osuny::Sanitizer.sanitize(self.git_endpoint, 'string')
+    self.name = Osuny::Sanitizer.sanitize(self.name, 'string')
+    self.plausible_url = Osuny::Sanitizer.sanitize(self.plausible_url, 'string')
+    self.repository = Osuny::Sanitizer.sanitize(self.repository, 'string')
+    self.url = Osuny::Sanitizer.sanitize(self.url, 'string')
+  end
+
   def languages_must_include_default_language
     errors.add(:languages, :must_include_default) unless language_ids.include?(default_language_id)
   end
diff --git a/app/models/communication/website/git_file.rb b/app/models/communication/website/git_file.rb
index d055984b8607a6f96ff6d599fe3ffe61170f2cfd..ca3bbce5eacdb87321d25c90a5fd1390b26b8bad 100644
--- a/app/models/communication/website/git_file.rb
+++ b/app/models/communication/website/git_file.rb
@@ -21,6 +21,8 @@
 #  fk_rails_8505d649e8  (website_id => communication_websites.id)
 #
 class Communication::Website::GitFile < ApplicationRecord
+  # We don't include Sanitizable as this model is never handled by users directly.
+
   belongs_to :website, class_name: 'Communication::Website'
   belongs_to :about, polymorphic: true
 
diff --git a/app/models/communication/website/permalink.rb b/app/models/communication/website/permalink.rb
index 88f821fd28202d11e3338cce0ff2accac97f517b..5329fcc0c3e690ea1beb632dbb1b5214e6c52991 100644
--- a/app/models/communication/website/permalink.rb
+++ b/app/models/communication/website/permalink.rb
@@ -40,6 +40,7 @@ class Communication::Website::Permalink < ApplicationRecord
     "University::Person::Teacher" => Communication::Website::Permalink::Teacher
   }
 
+  # We don't include Sanitizable as this model is never handled by users directly.
   include WithUniversity
 
   belongs_to :university
diff --git a/app/models/concerns/sanitizable.rb b/app/models/concerns/sanitizable.rb
index 1d9b3906576f0dfbcd5ebcd97b9a20d3598404aa..83f0cdaea91da72821b1f68abb464f38a7559251 100644
--- a/app/models/concerns/sanitizable.rb
+++ b/app/models/concerns/sanitizable.rb
@@ -11,6 +11,9 @@ module Sanitizable
                                                       .select { |attr_name, attr_type|
                                                         [:string, :text].include?(attr_type) && public_send(attr_name).present?
                                                       }
+                                                      .reject { |attr_name, _|
+                                                        attr_name.ends_with?('_type') # Reject polymorphic type
+                                                      }
 
       attributes_to_sanitize.each do |attr_name, attr_type|
         public_send "#{attr_name}=", Osuny::Sanitizer.sanitize(public_send(attr_name), attr_type)
diff --git a/app/models/education/cohort.rb b/app/models/education/cohort.rb
index e37605bda37291b7b373eff62351d342f0b56c83..482c32aad582b6e2eecf5e8178bb67df8a4237d5 100644
--- a/app/models/education/cohort.rb
+++ b/app/models/education/cohort.rb
@@ -26,6 +26,7 @@
 #  fk_rails_c2d725cabd  (academic_year_id => education_academic_years.id)
 #
 class Education::Cohort < ApplicationRecord
+  include Sanitizable
   include WithUniversity
 
   belongs_to  :school,
diff --git a/app/models/education/diploma.rb b/app/models/education/diploma.rb
index f01e1db3bc274743ec9918ff0d368f3c262fcd35..7d78ab0898e83836b935508755fd7bd1804eb60b 100644
--- a/app/models/education/diploma.rb
+++ b/app/models/education/diploma.rb
@@ -23,6 +23,7 @@
 #  fk_rails_6cb2e9fa90  (university_id => universities.id)
 #
 class Education::Diploma < ApplicationRecord
+  include Sanitizable
   include WithBlocks
   include WithGit
   include WithPermalink
diff --git a/app/models/education/school.rb b/app/models/education/school.rb
index 14dbac9262e182359b73054c93cb1fca756bf071..78ab3e1fc5fbae0c810d23ff5a7feca6ab1d30a7 100644
--- a/app/models/education/school.rb
+++ b/app/models/education/school.rb
@@ -24,6 +24,7 @@
 #  fk_rails_e01b37a3ad  (university_id => universities.id)
 #
 class Education::School < ApplicationRecord
+  include Sanitizable
   include WithGit
   include Aboutable
   include WithPrograms # must come before WithAlumni and WithTeam
diff --git a/app/models/language.rb b/app/models/language.rb
index 36cf5338a0bc6d3f337c79dcac2fdaacb195158f..212b40dce877387634041b6a844b49bd7c2b8836 100644
--- a/app/models/language.rb
+++ b/app/models/language.rb
@@ -10,6 +10,7 @@
 #  updated_at        :datetime         not null
 #
 class Language < ApplicationRecord
+  include Sanitizable
 
   has_many :users
   has_and_belongs_to_many :communication_websites,
diff --git a/app/models/research/hal/author.rb b/app/models/research/hal/author.rb
index 726c98e5450b49ece0fa9ab86cfecbc5f5f0049e..1a8e33ec278e2ddc8fc100f30213099a57743f94 100644
--- a/app/models/research/hal/author.rb
+++ b/app/models/research/hal/author.rb
@@ -17,6 +17,8 @@
 #  index_research_hal_authors_on_docid  (docid)
 #
 class Research::Hal::Author < ApplicationRecord
+  include Sanitizable
+
   has_and_belongs_to_many :publications,
                           foreign_key: 'research_hal_publication_id',
                           association_foreign_key: 'research_hal_author_id'
@@ -76,7 +78,7 @@ class Research::Hal::Author < ApplicationRecord
     researchers << researcher
     researcher.import_research_hal_publications!
   end
-  
+
   def disconnect_researcher(researcher)
     researchers.delete researcher
     researcher.import_research_hal_publications!
diff --git a/app/models/research/hal/publication.rb b/app/models/research/hal/publication.rb
index 75d03b57a5ab888c0e34e7a871a9d206f9ab2933..577fd844be578a2ef853c59e1a13297f4cd143af 100644
--- a/app/models/research/hal/publication.rb
+++ b/app/models/research/hal/publication.rb
@@ -20,9 +20,10 @@
 #  index_research_hal_publications_on_docid  (docid)
 #
 class Research::Hal::Publication < ApplicationRecord
+  include Sanitizable
   include WithGit
   include WithSlug
-  
+
   DOI_PREFIX = 'http://dx.doi.org/'.freeze
 
   has_and_belongs_to_many :researchers,
diff --git a/app/models/research/journal.rb b/app/models/research/journal.rb
index 21149af299ea637529ad441c4c9af6eb6960c15a..7919fd8eb63f04ac59dfae1eac008a4ff836dcc7 100644
--- a/app/models/research/journal.rb
+++ b/app/models/research/journal.rb
@@ -20,6 +20,7 @@
 #  fk_rails_96097d5f10  (university_id => universities.id)
 #
 class Research::Journal < ApplicationRecord
+  include Sanitizable
   include Aboutable
   include WithUniversity
   include WithGit
diff --git a/app/models/research/journal/paper/kind.rb b/app/models/research/journal/paper/kind.rb
index da961c04fcc36061726831a3d8b4d67e2e6324d2..f5895fd6f9b80d2246956fbc07cf648413edd341 100644
--- a/app/models/research/journal/paper/kind.rb
+++ b/app/models/research/journal/paper/kind.rb
@@ -21,6 +21,7 @@
 #  fk_rails_8e6f992b9d  (university_id => universities.id)
 #
 class Research::Journal::Paper::Kind < ApplicationRecord
+  include Sanitizable
   include WithUniversity
   include WithGit
   include WithSlug
diff --git a/app/models/research/laboratory.rb b/app/models/research/laboratory.rb
index 2daa8f561dd6972026cfc0bba330ae23815192f5..d5e261d490e9555e69485746cbc70de878735f35 100644
--- a/app/models/research/laboratory.rb
+++ b/app/models/research/laboratory.rb
@@ -21,6 +21,7 @@
 #  fk_rails_f61d27545f  (university_id => universities.id)
 #
 class Research::Laboratory < ApplicationRecord
+  include Sanitizable
   include WithGit
   include Aboutable
 
diff --git a/app/models/university.rb b/app/models/university.rb
index 15691d83123a5909d84f87a8c0616201f32a9c23..321f21333ed82efcb40e446851f6b5da3df545f0 100644
--- a/app/models/university.rb
+++ b/app/models/university.rb
@@ -38,6 +38,7 @@
 class University < ApplicationRecord
   self.filter_attributes += [:sso_cert]
 
+  # We don't include Sanitizable because too many complex attributes. We handle it below.
   include WithPeopleAndOrganizations
   include WithCommunication
   include WithEducation
@@ -59,6 +60,7 @@ class University < ApplicationRecord
   validates :sms_sender_name, presence: true, length: { maximum: 11 }
   validates :logo, size: { less_than: 1.megabytes }
 
+  before_validation :sanitize_fields
   after_destroy :destroy_remaining_blobs
 
   scope :ordered, -> { order(:name) }
@@ -88,6 +90,20 @@ class University < ApplicationRecord
 
   private
 
+  def sanitize_fields
+    self.address = Osuny::Sanitizer.sanitize(self.address, 'string')
+    self.city = Osuny::Sanitizer.sanitize(self.city, 'string')
+    self.country = Osuny::Sanitizer.sanitize(self.country, 'string')
+    self.identifier = Osuny::Sanitizer.sanitize(self.identifier, 'string')
+    self.invoice_amount = Osuny::Sanitizer.sanitize(self.invoice_amount, 'string')
+    self.mail_from_address = Osuny::Sanitizer.sanitize(self.mail_from_address, 'string')
+    self.mail_from_name = Osuny::Sanitizer.sanitize(self.mail_from_name, 'string')
+    self.name = Osuny::Sanitizer.sanitize(self.name, 'string')
+    self.sms_sender_name = Osuny::Sanitizer.sanitize(self.sms_sender_name, 'string')
+    self.sso_button_label = Osuny::Sanitizer.sanitize(self.sso_button_label, 'string')
+    self.zipcode = Osuny::Sanitizer.sanitize(self.zipcode, 'string')
+  end
+
   def destroy_remaining_blobs
     active_storage_blobs.delete_all
   end
diff --git a/app/models/university/person/experience.rb b/app/models/university/person/experience.rb
index a5f0b47c74ddc53c34fe856bf59a0bbe8e730985..60c2a43632ea9fdd7e62cadab1a7e9af2c5f6b8e 100644
--- a/app/models/university/person/experience.rb
+++ b/app/models/university/person/experience.rb
@@ -25,6 +25,7 @@
 #  fk_rails_923d0b71fd  (university_id => universities.id)
 #
 class University::Person::Experience < ApplicationRecord
+  include Sanitizable
   include WithUniversity
 
   attr_accessor :organization_name
diff --git a/app/models/university/person/involvement.rb b/app/models/university/person/involvement.rb
index 988be24c96c7fa28a9a6eb6c18f87487d5db8a04..e80bd448a3bb315b90bcf4d0b3db213f35d31bff 100644
--- a/app/models/university/person/involvement.rb
+++ b/app/models/university/person/involvement.rb
@@ -25,6 +25,7 @@
 #  fk_rails_5c704f6338  (university_id => universities.id)
 #
 class University::Person::Involvement < ApplicationRecord
+  include Sanitizable
   include WithUniversity
   include WithPosition
 
diff --git a/app/models/university/role.rb b/app/models/university/role.rb
index 3e740ac5248e93ec6b98b178f8b8bf190d65c355..f631de252775ef4cb247f10b593063388e49a9c6 100644
--- a/app/models/university/role.rb
+++ b/app/models/university/role.rb
@@ -21,6 +21,7 @@
 #  fk_rails_8e52293a38  (university_id => universities.id)
 #
 class University::Role < ApplicationRecord
+  include Sanitizable
   include WithUniversity
   include WithPosition
 
diff --git a/app/models/user.rb b/app/models/user.rb
index 569270a99f6938fb281b3a5944e528b265872dbb..769136f69d5daba09c2b5ee5ca790e49111f1aeb 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -56,6 +56,8 @@
 #  fk_rails_bd6f7212a9  (university_id => universities.id)
 #
 class User < ApplicationRecord
+  # We don't include Sanitizable because too many complex attributes.
+  # The sanitization is handled in User::WithAuthentication's sanitize_fields method.
   include WithAdminTheme
   include WithAvatar
   include WithRegistrationContext
diff --git a/app/models/user/with_authentication.rb b/app/models/user/with_authentication.rb
index 86a880834032231725a0fb57185d33e46bc141b7..443bbb02e0abd22074123342f2373dad51e61b2b 100644
--- a/app/models/user/with_authentication.rb
+++ b/app/models/user/with_authentication.rb
@@ -94,13 +94,11 @@ module User::WithAuthentication
     end
 
     def sanitize_fields
-      full_sanitizer = Rails::Html::FullSanitizer.new
-
       # Only text allowed, and remove '=' to prevent excel formulas
-      self.email = full_sanitizer.sanitize(self.email)&.gsub('=', '')
-      self.first_name = full_sanitizer.sanitize(self.first_name)&.gsub('=', '')
-      self.last_name = full_sanitizer.sanitize(self.last_name)&.gsub('=', '')
-      self.mobile_phone = full_sanitizer.sanitize(self.mobile_phone)&.gsub('=', '')
+      self.email = Osuny::Sanitizer.sanitize(self.email, 'string')&.gsub('=', '')
+      self.first_name = Osuny::Sanitizer.sanitize(self.first_name, 'string')&.gsub('=', '')
+      self.last_name = Osuny::Sanitizer.sanitize(self.last_name, 'string')&.gsub('=', '')
+      self.mobile_phone = Osuny::Sanitizer.sanitize(self.mobile_phone, 'string')&.gsub('=', '')
     end
 
     def password_required?
diff --git a/app/views/action_text/content/_layout.html.erb b/app/views/action_text/content/_layout.html.erb
deleted file mode 100644
index 898a5066770e08c389a01f8428b91e6b2424c95f..0000000000000000000000000000000000000000
--- a/app/views/action_text/content/_layout.html.erb
+++ /dev/null
@@ -1 +0,0 @@
-<%= render_action_text_content(content) %>
diff --git a/app/views/active_storage/blobs/_blob.html.erb b/app/views/active_storage/blobs/_blob.html.erb
deleted file mode 100644
index e823ece4caee828b2e1677c38b1cca44cb4fbbe2..0000000000000000000000000000000000000000
--- a/app/views/active_storage/blobs/_blob.html.erb
+++ /dev/null
@@ -1,22 +0,0 @@
-<figure class="attachment attachment--<%= blob.variable? ? "preview" : "file" %> attachment--<%= blob.filename.extension %>">
-  <% if blob.image? %>
-    <%= kamifusen_tag blob, width: 800 %>
-  <% elsif blob.video? %>
-    <video controls>
-      <source src="<%= rails_blob_path(blob) %>" type="<%= blob.content_type %>">
-    </video>
-  <% else %>
-    <%= link_to polymorphic_path(blob), target: :blank do %>
-      <p>
-        <span class="attachment__name"><%= blob.filename %></span>
-        <span class="attachment__size"><%= number_to_human_size blob.byte_size %></span>
-      </p>
-    <% end %>
-  <% end %>
-
-  <% if caption = blob.try(:caption) %>
-    <figcaption class="attachment__caption">
-      <%= caption %>
-    </figcaption>
-  <% end %>
-</figure>
diff --git a/config/application.rb b/config/application.rb
index fa9ee9cc7659a90eecb37623b4694e88c5589804..d7ff6b3577240e9c36bd8336a8b9cf434a54f2be 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -9,7 +9,7 @@ require "active_storage/engine"
 require "action_controller/railtie"
 require "action_mailer/railtie"
 require "action_mailbox/engine"
-require "action_text/engine"
+# require "action_text/engine"
 require "action_view/railtie"
 # require "action_cable/engine"
 require "sprockets/railtie"