CVE-2022-39226 (published 2022-09-29T20:15Z)

Discourse is an open source discussion platform. In versions prior to 2.8.9 on the `stable` branch and prior to 2.9.0.beta10 on the `beta` and `tests-passed` branches, a malicious actor can add large payloads of text into the Location and Website fields of a user profile, which causes issues for other users when loading that profile. A fix to limit the length of user input for these fields is included in version 2.8.9 on the `stable` branch and version 2.9.0.beta10 on the `beta` and `tests-passed` branches. There are no known workarounds.

CVSS v3 base score: 4.3, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L)

Weakness information:
cwe_id: CWE-770
cwe_name: Allocation of Resources Without Limits or Throttling
description: The product allocates a reusable resource or group of resources on behalf of an actor without imposing any restrictions on the size or number of resources that can be allocated, in violation of the intended security policy for that actor.
extended_description: Code frequently has to work with limited resources, so programmers must be careful to ensure that resources are not consumed too quickly, or too easily.  Without use of quotas, resource limits, or other protection mechanisms, it can be easy for an attacker to consume many resources by rapidly making many requests, or causing larger resources to be used than is needed. When too many resources are allocated, or if a single resource is too large, then it can prevent the code from working correctly, possibly leading to a denial of service.
         

Fix information:
repo_url: https://github.com/discourse/discourse
commit_details: repo_url: https://github.com/discourse/discourse
author: Martin Brennan
author_date: 2022-09-21 12:07:06+10:00
msg: SECURITY: Limit user profile field length (#18302)

Adds limits to location and website fields at model and DB level
to match the bio_raw field limits. A limit cannot be added at the
DB level for bio_raw because it is a postgres text field.

Co-authored-by: Alan Guo Xiang Tan <gxtan1990@gmail.com>
repository: repo_url: https://github.com/discourse/discourse
repo_name: discourse/discourse
description: A platform for community discussion. Free, open, simple.
repo_language: Ruby
owner: discourse
file_changes: filename: user_profile.rb
change_type: ModificationType.MODIFY
diff: @@ -13,9 +13,9 @@ class UserProfile < ActiveRecord::Base
   has_many :user_profile_views, dependent: :destroy
 
   validates :bio_raw, length: { maximum: 3000 }, watched_words: true
-  validates :website, url: true, allow_blank: true, if: :validate_website?
+  validates :website, url: true, length: { maximum: 3000 }, allow_blank: true, if: :validate_website?
+  validates :location, length: { maximum: 3000 }, watched_words: true
   validates :user, presence: true
-  validates :location, watched_words: true
 
   validate :website_domain_validator, if: :validate_website?
 
@@ -188,8 +188,8 @@ end
 # Table name: user_profiles
 #
 #  user_id                      :integer          not null, primary key
-#  location                     :string
-#  website                      :string
+#  location                     :string(3000)
+#  website                      :string(3000)
 #  bio_raw                      :text
 #  bio_cooked                   :text
 #  dismissed_banner_key         :integer

programming_language: Ruby
filename: 20220920044310_enforce_user_profile_max_limits.rb
change_type: ModificationType.ADD
diff: @@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class EnforceUserProfileMaxLimits < ActiveRecord::Migration[7.0]
+  def change
+    execute "UPDATE user_profiles SET location = LEFT(location, 3000) WHERE location IS NOT NULL"
+    execute "UPDATE user_profiles SET website = LEFT(website, 3000) WHERE website IS NOT NULL"
+
+    change_column :user_profiles, :location, :string, limit: 3000
+    change_column :user_profiles, :website, :string, limit: 3000
+  end
+end

programming_language: Ruby
filename: user_profile_spec.rb
change_type: ModificationType.MODIFY
diff: @@ -42,6 +42,15 @@ RSpec.describe UserProfile do
         end
       end
 
+      context "when it is > 3000 characters" do
+        before { profile.location = "a" * 3500 }
+
+        it "is not valid" do
+          expect(profile.valid?).to eq(false)
+          expect(profile.errors.full_messages).to include(/Location is too long \(maximum is 3000 characters\)/)
+        end
+      end
+
       context "when it does not contain watched words" do
         it { is_expected.to be_valid }
       end
@@ -63,6 +72,15 @@ RSpec.describe UserProfile do
         end
       end
 
+      context "when it is > 3000 characters" do
+        before { profile.bio_raw = "a" * 3500 }
+
+        it "is not valid" do
+          expect(profile.valid?).to eq(false)
+          expect(profile.errors.full_messages).to include(/About Me is too long \(maximum is 3000 characters\)/)
+        end
+      end
+
       context "when it does not contain watched words" do
         it { is_expected.to be_valid }
       end
@@ -129,6 +147,11 @@ RSpec.describe UserProfile do
         user_profile.website = 'user - https://forum.example.com/user'
         expect { user_profile.save! }.to raise_error(ActiveRecord::RecordInvalid)
       end
+
+      it "does not allow > 3000 characters" do
+        user_profile.website = "a" * 3500
+        expect(user_profile).to_not be_valid
+      end
     end
 
     describe 'after save' do

programming_language: Ruby