From 3360f91733a3aaba670c55427f9195da9c02d981 Mon Sep 17 00:00:00 2001 From: Tom Hughes Date: Sun, 25 Feb 2024 13:16:40 +0000 Subject: [PATCH] Require display names to have a minimum unicode width of 3 columns Fixes #4538 --- .rubocop_todo.yml | 2 +- Gemfile | 3 +++ Gemfile.lock | 1 + app/models/user.rb | 3 ++- app/validators/width_validator.rb | 11 +++++++++++ test/models/user_test.rb | 12 ++++++++++++ 6 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 app/validators/width_validator.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 6c917e218..fdc7c35a5 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -66,7 +66,7 @@ Metrics/BlockNesting: # Offense count: 26 # Configuration parameters: CountComments, CountAsOne. Metrics/ClassLength: - Max: 313 + Max: 314 # Offense count: 59 # Configuration parameters: AllowedMethods, AllowedPatterns. diff --git a/Gemfile b/Gemfile index 63d9c49e8..832b96c9a 100644 --- a/Gemfile +++ b/Gemfile @@ -136,6 +136,9 @@ gem "aws-sdk-s3" # Used to resize user images gem "image_processing" +# Used to validate widths +gem "unicode-display_width" + # Gems useful for development group :development do gem "better_errors" diff --git a/Gemfile.lock b/Gemfile.lock index 24beeb4d1..234090689 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -683,6 +683,7 @@ DEPENDENCIES sprockets-exporters_pack strong_migrations terser + unicode-display_width validates_email_format_of (>= 1.5.1) vendorer webmock diff --git a/app/models/user.rb b/app/models/user.rb index 6fa0f330e..125e5e973 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -98,7 +98,8 @@ class User < ApplicationRecord :normalized_uniqueness => { :case_sensitive => false } validates :display_name, :if => proc { |u| u.display_name_changed? }, :characters => { :url_safe => true }, - :whitespace => { :leading => false, :trailing => false } + :whitespace => { :leading => false, :trailing => false }, + :width => { :minimum => 3 } validate :display_name_cannot_be_user_id_with_other_id, :if => proc { |u| u.display_name_changed? } validates :email, :presence => true, :confirmation => true, :characters => true validates :email, :if => proc { |u| u.email_changed? }, diff --git a/app/validators/width_validator.rb b/app/validators/width_validator.rb new file mode 100644 index 000000000..a3a4d414c --- /dev/null +++ b/app/validators/width_validator.rb @@ -0,0 +1,11 @@ +class WidthValidator < ActiveModel::Validations::LengthValidator + module WidthAsLength + def length + Unicode::DisplayWidth.of(to_s) + end + end + + def validate_each(record, attribute, value) + super(record, attribute, value.extend(WidthAsLength)) + end +end diff --git a/test/models/user_test.rb b/test/models/user_test.rb index 42949504f..92e7d419c 100644 --- a/test/models/user_test.rb +++ b/test/models/user_test.rb @@ -67,6 +67,18 @@ class UserTest < ActiveSupport::TestCase assert_not_predicate user, :valid?, "should not allow nil value" end + def test_display_name_width + user = build(:user) + user.display_name = "123" + assert_predicate user, :valid?, "should allow 3 column name name" + user.display_name = "12" + assert_not_predicate user, :valid?, "should not allow 2 column name" + user.display_name = "1\u{200B}2" + assert_not_predicate user, :valid?, "should not allow 2 column name" + user.display_name = "\u{200B}\u{200B}\u{200B}" + assert_not_predicate user, :valid?, "should not allow 0 column name" + end + def test_display_name_valid # Due to sanitisation in the view some of these that you might not # expect are allowed -- 2.39.5