diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-09-20 16:18:24 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-09-20 16:18:24 +0300 |
commit | 0653e08efd039a5905f3fa4f6e9cef9f5d2f799c (patch) | |
tree | 4dcc884cf6d81db44adae4aa99f8ec1233a41f55 /app/models/concerns/sanitizable.rb | |
parent | 744144d28e3e7fddc117924fef88de5d9674fe4c (diff) |
Add latest changes from gitlab-org/gitlab@14-3-stable-eev14.3.0-rc42
Diffstat (limited to 'app/models/concerns/sanitizable.rb')
-rw-r--r-- | app/models/concerns/sanitizable.rb | 52 |
1 files changed, 52 insertions, 0 deletions
diff --git a/app/models/concerns/sanitizable.rb b/app/models/concerns/sanitizable.rb new file mode 100644 index 00000000000..05756beb404 --- /dev/null +++ b/app/models/concerns/sanitizable.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +# == Sanitizable concern +# +# This concern adds HTML sanitization and validation to models. The intention is +# to help prevent XSS attacks in the event of a by-pass in the frontend +# sanitizer due to a configuration issue or a vulnerability in the sanitizer. +# This approach is commonly referred to as defense-in-depth. +# +# Example: +# +# module Dast +# class Profile < ApplicationRecord +# include Sanitizable +# +# sanitizes! :name, :description + +module Sanitizable + extend ActiveSupport::Concern + + class_methods do + def sanitize(input) + return unless input + + # We return the input unchanged to avoid escaping pre-escaped HTML fragments. + # Please see gitlab-org/gitlab#293634 for an example. + return input unless input == CGI.unescapeHTML(input.to_s) + + CGI.unescapeHTML(Sanitize.fragment(input)) + end + + def sanitizes!(*attrs) + instance_eval do + before_validation do + attrs.each do |attr| + input = public_send(attr) # rubocop: disable GitlabSecurity/PublicSend + + public_send("#{attr}=", self.class.sanitize(input)) # rubocop: disable GitlabSecurity/PublicSend + end + end + + validates_each(*attrs) do |record, attr, input| + # We reject pre-escaped HTML fragments as invalid to avoid saving them + # to the database. + unless input.to_s == CGI.unescapeHTML(input.to_s) + record.errors.add(attr, 'cannot contain escaped HTML entities') + end + end + end + end + end +end |