diff options
Diffstat (limited to 'app/validators/html_safety_validator.rb')
-rw-r--r-- | app/validators/html_safety_validator.rb | 36 |
1 files changed, 36 insertions, 0 deletions
diff --git a/app/validators/html_safety_validator.rb b/app/validators/html_safety_validator.rb new file mode 100644 index 00000000000..29e7d445697 --- /dev/null +++ b/app/validators/html_safety_validator.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +# HtmlSafetyValidator +# +# Validates that a value does not contain HTML +# or other unsafe content that could lead to XSS. +# Relies on Rails HTML Sanitizer: +# https://github.com/rails/rails-html-sanitizer +# +# Example: +# +# class Group < ActiveRecord::Base +# validates :name, presence: true, html_safety: true +# end +# +class HtmlSafetyValidator < ActiveModel::EachValidator + def validate_each(record, attribute, value) + return if value.blank? || safe_value?(value) + + record.errors.add(attribute, self.class.error_message) + end + + def self.error_message + _("cannot contain HTML/XML tags, including any word between angle brackets (<,>).") + end + + private + + # The `FullSanitizer` encodes ampersands as the HTML entity name. + # This isn't particularly necessary for preventing XSS so the ampersand + # is pre-encoded to avoid it being flagged in the comparison. + def safe_value?(text) + pre_encoded_text = text.gsub('&', '&') + Rails::Html::FullSanitizer.new.sanitize(pre_encoded_text) == pre_encoded_text + end +end |