FIX: Apply watched words to user fields

Currently we don’t apply watched words to custom user fields nor user
profile fields.
This led to users being able to use blocked words in their bio, location
or some custom user fields.

This patch addresses this issue by adding some validations so it’s not
possible anymore to save the User model or the UserProfile model if they
contain blocked words.
This commit is contained in:
Loïc Guitaut 2022-04-28 11:00:02 +02:00 committed by Loïc Guitaut
parent 26c5002144
commit ba148e082d
7 changed files with 78 additions and 14 deletions

View File

@ -114,6 +114,7 @@ class User < ActiveRecord::Base
validates :name, user_full_name: true, if: :will_save_change_to_name?, length: { maximum: 255 }
validates :ip_address, allowed_ip_address: { on: :create, message: :signup_not_allowed }
validates :primary_email, presence: true
validates :custom_fields_values, watched_words: true
validates_associated :primary_email, message: -> (_, user_email) { user_email[:value]&.errors[:email]&.first }
after_initialize :add_trust_level
@ -1791,6 +1792,10 @@ class User < ActiveRecord::Base
)
SQL
end
def custom_fields_values
custom_fields.values.join(" ")
end
end
# == Schema Information

View File

@ -7,9 +7,11 @@ class UserProfile < ActiveRecord::Base
belongs_to :granted_title_badge, class_name: "Badge"
belongs_to :featured_topic, class_name: 'Topic'
validates :bio_raw, length: { maximum: 3000 }
validates :bio_raw, length: { maximum: 3000 }, watched_words: true
validates :website, url: true, allow_blank: true, if: Proc.new { |c| c.new_record? || c.website_changed? }
validates :user, presence: true
validates :location, watched_words: true
before_save :cook
after_save :trigger_badges
after_save :pull_hotlinked_image

View File

@ -12,12 +12,9 @@ describe Jobs::ActivationReminderEmails do
expect { described_class.new.execute({}) }
.to change { ActionMailer::Base.deliveries.size }.by(1)
.and change { user.email_tokens.count }.by(1)
expect(user.custom_fields['activation_reminder']).to eq("t")
.and change { user.reload.custom_fields[:activation_reminder] }.to "t"
expect { described_class.new.execute({}) }.to change { ActionMailer::Base.deliveries.size }.by(0)
user.activate
expect(user.reload.custom_fields['activation_reminder']).to eq(nil)
expect { user.activate }.to change { user.reload.custom_fields[:activation_reminder] }.to nil
end
it 'should not email active users' do

View File

@ -100,7 +100,7 @@ describe Jobs::BulkInvite do
expect(User.where(staged: true).find_by_email('test@discourse.org')).to eq(nil)
expect(user.user_fields[user_field.id.to_s]).to eq('value 1')
expect(user.user_fields[user_field_color.id.to_s]).to eq('Blue')
expect(staged_user.user_fields[user_field.id.to_s]).to eq('value 2')
expect(staged_user.reload.user_fields[user_field.id.to_s]).to eq('value 2')
expect(staged_user.user_fields[user_field_color.id.to_s]).to eq(nil)
new_staged_user = User.where(staged: true).find_by_email('test2@discourse.org')
expect(new_staged_user.user_fields[user_field.id.to_s]).to eq('value 3')

View File

@ -1,6 +1,45 @@
# frozen_string_literal: true
describe UserProfile do
RSpec.describe UserProfile do
describe "Validations" do
subject(:profile) { user.user_profile }
fab!(:user) { Fabricate(:user) }
fab!(:watched_word) { Fabricate(:watched_word, word: "bad") }
describe "#location" do
context "when it contains watched words" do
before { profile.location = "bad location" }
it "is not valid" do
profile.valid?
expect(profile.errors[:base].size).to eq(1)
expect(profile.errors.messages[:base]).to include(/you can't post the word/)
end
end
context "when it does not contain watched words" do
it { is_expected.to be_valid }
end
end
describe "#bio_raw" do
context "when it contains watched words" do
before { profile.bio_raw = "bad bio" }
it "is not valid" do
profile.valid?
expect(profile.errors[:base].size).to eq(1)
expect(profile.errors.messages[:base]).to include(/you can't post the word/)
end
end
context "when it does not contain watched words" do
it { is_expected.to be_valid }
end
end
end
it 'is created automatically when a user is created' do
user = Fabricate(:evil_trout)
expect(user.user_profile).to be_present
@ -224,7 +263,5 @@ describe UserProfile do
expect(user.card_background_upload).to eq(nil)
end
end
end
end

View File

@ -1,9 +1,9 @@
# frozen_string_literal: true
describe User do
RSpec.describe User do
fab!(:group) { Fabricate(:group) }
let(:user) { Fabricate(:user, last_seen_at: 1.day.ago) }
subject(:user) { Fabricate(:user, last_seen_at: 1.day.ago) }
def user_error_message(*keys)
I18n.t(:"activerecord.errors.models.user.attributes.#{keys.join('.')}")
@ -136,6 +136,29 @@ describe User do
end
end
end
describe "#user_fields" do
fab!(:user_field) { Fabricate(:user_field) }
fab!(:watched_word) { Fabricate(:watched_word, word: "bad") }
before { user.set_user_field(user_field.id, value) }
context "when user fields contain watched words" do
let(:value) { "bad user field value" }
it "is not valid" do
user.valid?
expect(user.errors[:base].size).to eq(1)
expect(user.errors.messages[:base]).to include(/you can't post the word/)
end
end
context "when user fields do not contain watched words" do
let(:value) { "good user field value" }
it { is_expected.to be_valid }
end
end
end
describe '#count_by_signup_date' do

View File

@ -289,9 +289,9 @@ describe UserAnonymizer do
"user_field_#{field2.id}": "bar",
"another_field": "456"
}
user.save
expect { make_anonymous }.to change { user.custom_fields }
expect(user.reload.custom_fields).to eq("some_field" => "123", "another_field" => "456")
expect { make_anonymous }.to change { user.reload.custom_fields }.to "some_field" => "123", "another_field" => "456"
end
end
end