Merge pull request #2882 from evazion/fix-tag-name-validation
Enforce stricter rules for tag names
This commit is contained in:
30
app/logical/tag_name_validator.rb
Normal file
30
app/logical/tag_name_validator.rb
Normal file
@@ -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
|
||||
@@ -16,6 +16,7 @@ class Post < ActiveRecord::Base
|
||||
before_validation :remove_parent_loops
|
||||
validates_uniqueness_of :md5, :on => :create
|
||||
validates_inclusion_of :rating, in: %w(s q e), message: "rating must be s, q, or e"
|
||||
validate :tag_names_are_valid
|
||||
validate :post_is_not_its_own_parent
|
||||
validate :updater_can_change_rating
|
||||
before_save :update_tag_post_counts
|
||||
@@ -1307,13 +1308,6 @@ class Post < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def post_is_not_its_own_parent
|
||||
if !new_record? && id == parent_id
|
||||
errors[:base] << "Post cannot have itself as a parent"
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def parent_exists?
|
||||
Post.exists?(parent_id)
|
||||
end
|
||||
@@ -1689,6 +1683,40 @@ class Post < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ValidationMethods
|
||||
def post_is_not_its_own_parent
|
||||
if !new_record? && id == parent_id
|
||||
errors[:base] << "Post cannot have itself as a parent"
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def updater_can_change_rating
|
||||
if rating_changed? && is_rating_locked?
|
||||
# Don't forbid changes if the rating lock was just now set in the same update.
|
||||
if !is_rating_locked_changed?
|
||||
errors.add(:rating, "is locked and cannot be changed. Unlock the post first.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def tag_names_are_valid
|
||||
# only validate new tags; allow invalid names for tags that already exist.
|
||||
added_tags = tag_array - tag_array_was
|
||||
new_tags = added_tags - Tag.where(name: added_tags).pluck(:name)
|
||||
|
||||
new_tags.each do |name|
|
||||
tag = Tag.new
|
||||
tag.name = name
|
||||
tag.valid?
|
||||
|
||||
tag.errors.messages.each do |attribute, messages|
|
||||
errors[:tag_string] << "tag #{attribute} #{messages.join(';')}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
include FileMethods
|
||||
include ImageMethods
|
||||
@@ -1709,6 +1737,7 @@ class Post < ActiveRecord::Base
|
||||
extend SearchMethods
|
||||
include PixivMethods
|
||||
include IqdbMethods
|
||||
include ValidationMethods
|
||||
include Danbooru::HasBitFlags
|
||||
|
||||
BOOLEAN_ATTRIBUTES = %w(
|
||||
@@ -1765,15 +1794,6 @@ class Post < ActiveRecord::Base
|
||||
save
|
||||
end
|
||||
|
||||
def updater_can_change_rating
|
||||
if rating_changed? && is_rating_locked?
|
||||
# Don't forbid changes if the rating lock was just now set in the same update.
|
||||
if !is_rating_locked_changed?
|
||||
errors.add(:rating, "is locked and cannot be changed. Unlock the post first.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def update_column(name, value)
|
||||
ret = super(name, value)
|
||||
notify_pubsub
|
||||
|
||||
@@ -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 = {})
|
||||
@@ -227,13 +229,13 @@ class Tag < ActiveRecord::Base
|
||||
def scan_query(query)
|
||||
tagstr = normalize(query)
|
||||
list = tagstr.scan(/-?source:".*?"/) || []
|
||||
list + tagstr.gsub(/-?source:".*?"/, "").scan(/\S+/).uniq
|
||||
list + tagstr.gsub(/-?source:".*?"/, "").scan(/[^[:space:]]+/).uniq
|
||||
end
|
||||
|
||||
def scan_tags(tags, options = {})
|
||||
tagstr = normalize(tags)
|
||||
list = tagstr.scan(/source:".*?"/) || []
|
||||
list += tagstr.gsub(/source:".*?"/, "").gsub(/[%,]/, "").scan(/\S+/).uniq
|
||||
list += tagstr.gsub(/source:".*?"/, "").scan(/[^[:space:]]+/).uniq
|
||||
if options[:strip_metatags]
|
||||
list = list.map {|x| x.sub(/^[-~]/, "")}
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user