From 853ddc7cdac7218b58e348e7c0f54762985e68df Mon Sep 17 00:00:00 2001 From: evazion Date: Tue, 7 Feb 2017 23:03:35 -0600 Subject: [PATCH] tag.rb: validate tag names. Makes these tag names invalid when tagging posts: * Blank names (e.g. ___). * Non-ASCII characters (Japanese text). * Non-printable characters (e.g. control characters < 0x20). * Leading or trailing underscores, or consecutive underscores. Reason: `_foo__bar_` and `foo_bar` both render as `foo bar` in the tag list. * Leading `-` or `~`, or '*' (-foo, ~foo, foo*bar). Previously these were silently stripped, but that meant tagging a post with e.g. `*-foo` tagged the post with the invalid tag `-foo`. * Tag type prefixes (e.g. `character:character:hatsune_miku` no longer creates the literal tag `character:hatsune_miku`). * Metatags (order:score, user:evazion, etc). --- app/logical/tag_name_validator.rb | 30 ++++++++++++++++++++++++++++++ app/models/tag.rb | 4 +++- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 app/logical/tag_name_validator.rb diff --git a/app/logical/tag_name_validator.rb b/app/logical/tag_name_validator.rb new file mode 100644 index 000000000..872b9861d --- /dev/null +++ b/app/logical/tag_name_validator.rb @@ -0,0 +1,30 @@ +class TagNameValidator < ActiveModel::EachValidator + def validate_each(record, attribute, value) + case Tag.normalize_name(value) + when /\A_*\z/ + record.errors[attribute] << "'#{value}' cannot be blank" + when /\*/ + record.errors[attribute] << "'#{value}' cannot contain asterisks ('*')" + when /,/ + record.errors[attribute] << "'#{value}' cannot contain commas (',')" + when /\A~/ + record.errors[attribute] << "'#{value}' cannot begin with a tilde ('~')" + when /\A-/ + record.errors[attribute] << "'#{value}' cannot begin with a dash ('-')" + when /\A_/ + record.errors[attribute] << "'#{value}' cannot begin with an underscore" + when /_\z/ + record.errors[attribute] << "'#{value}' cannot end with an underscore" + when /__/ + record.errors[attribute] << "'#{value}' cannot contain consecutive underscores" + when /[^[[:graph:]]]/ + record.errors[attribute] << "'#{value}' cannot contain non-printable characters" + when /[^[[:ascii:]]]/ + record.errors[attribute] << "'#{value}' must consist of only ASCII characters" + when /\A(#{Tag::METATAGS}|#{Tag::SUBQUERY_METATAGS}):(.+)\z/i + record.errors[attribute] << "'#{value}' cannot begin with '#{$1}:'" + when /\A(#{Tag.categories.regexp}):(.+)\z/i + record.errors[attribute] << "'#{value}' cannot begin with '#{$1}:'" + end + end +end diff --git a/app/models/tag.rb b/app/models/tag.rb index 445c001f2..301b89f1b 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -6,6 +6,8 @@ class Tag < ActiveRecord::Base attr_accessible :is_locked, :as => [:moderator, :admin] has_one :wiki_page, :foreign_key => "title", :primary_key => "name" + validates :name, uniqueness: true, tag_name: true, on: :create + module ApiMethods def to_legacy_json return { @@ -179,7 +181,7 @@ class Tag < ActiveRecord::Base module NameMethods def normalize_name(name) - name.mb_chars.downcase.tr(" ", "_").gsub(/\A[-~]+/, "").gsub(/\*/, "").to_s + name.to_s.mb_chars.downcase.strip.tr(" ", "_").to_s end def find_or_create_by_name(name, options = {})