From 09972477cdb649d440645bfae22cfc5697af098a Mon Sep 17 00:00:00 2001 From: evazion Date: Mon, 23 Sep 2019 00:03:11 -0500 Subject: [PATCH] users: fix find_by_name for names with special characters. `User.find_by_name` used `where_ilike` to do a case-insensitve name search, but it didn't escape `*` or `\` characters first, so it didn't handle names containing these characters properly. --- app/models/application_record.rb | 4 ++++ app/models/user.rb | 2 +- test/unit/user_test.rb | 10 ++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/models/application_record.rb b/app/models/application_record.rb index 9736022e6..1cc0619dd 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -25,6 +25,10 @@ class ApplicationRecord < ActiveRecord::Base where.not("#{qualified_column_for(attr)} ILIKE ? ESCAPE E'\\\\'", value.mb_chars.to_escaped_for_sql_like) end + def where_iequals(attr, value) + where_ilike(attr, value.gsub(/\\/, '\\\\').gsub(/\*/, '\*')) + end + # https://www.postgresql.org/docs/current/static/functions-matching.html#FUNCTIONS-POSIX-REGEXP # "(?e)" means force use of ERE syntax; see sections 9.7.3.1 and 9.7.3.4. def where_regex(attr, value) diff --git a/app/models/user.rb b/app/models/user.rb index 7afe3efc6..0aac3ed9c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -142,7 +142,7 @@ class User < ApplicationRecord # XXX downcasing is the wrong way to do case-insensitive comparison for unicode (should use casefolding). def find_by_name(name) - where_ilike(:name, normalize_name(name)).first + where_iequals(:name, normalize_name(name)).first end def normalize_name(name) diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb index f0f0f2c6e..b6c5f461c 100644 --- a/test/unit/user_test.rb +++ b/test/unit/user_test.rb @@ -175,6 +175,16 @@ class UserTest < ActiveSupport::TestCase assert_nil(User.find_by_name("does_not_exist")) assert_nil(User.name_to_id("does_not_exist")) end + + should "work for names containing asterisks or backlashes" do + @user1 = create(:user, name: "user*1") + @user2 = create(:user, name: "user*2") + @user3 = create(:user, name: "user\*3") + + assert_equal(@user1.id, User.find_by_name("user*1").id) + assert_equal(@user2.id, User.find_by_name("user*2").id) + assert_equal(@user3.id, User.find_by_name("user\*3").id) + end end context "ip address" do