CVE-2022-23549: Other (NVD-CWE-Other)
value: Discourse is an option source discussion platform. Prior to version 2.8.14 on the `stable` branch and version 2.9.0.beta16 on the `beta` and `tests-passed` branches, users can create posts with raw body longer than the `max_length` site setting by including html comments that are not counted toward the character limit. This issue is patched in versions 2.8.14 and 2.9.0.beta16. There are no known workarounds.
CWE description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
CVSS v3 base score: 6.5
Published: 2023-01-05T19:15Z
Repository: https://github.com/discourse/discourse
Language: Ruby
Files changed: lib/validators/stripped_length_validator.rb, spec/requests/posts_controller_spec.rb
Commit date: 2023-01-05 06:08:43 +0800
Commit message: SECURITY: Check the length of raw post body (#19733)

Co-authored-by: Jarek Radosz <jradosz@gmail.com>
Diff: diff --git a/lib/validators/stripped_length_validator.rb b/lib/validators/stripped_length_validator.rb
index b2ff7f82efdf..631feb9d58f9 100644
--- a/lib/validators/stripped_length_validator.rb
+++ b/lib/validators/stripped_length_validator.rb
@@ -1,15 +1,19 @@
 # frozen_string_literal: true
 
 class StrippedLengthValidator < ActiveModel::EachValidator
   def self.validate(record, attribute, value, range)
-    if !value.nil?
-      value = get_sanitized_value(value)
-      record.errors.add attribute, (I18n.t('errors.messages.too_short', count: range.begin)) if value.length < range.begin
-      record.errors.add attribute, (I18n.t('errors.messages.too_long_validation', max: range.end, length: value.length)) if value.length > range.end
+    if value.nil?
+      record.errors.add attribute, I18n.t('errors.messages.blank')
+    elsif value.length > range.end
+      record.errors.add attribute, I18n.t('errors.messages.too_long_validation', max: range.end, length: value.length)
     else
-      record.errors.add attribute, (I18n.t('errors.messages.blank'))
+      value = get_sanitized_value(value)
+
+      if value.length < range.begin
+        record.errors.add attribute, I18n.t('errors.messages.too_short', count: range.begin)
+      end
     end
   end
 
   def validate_each(record, attribute, value)
     # the `in` parameter might be a lambda when the range is dynamic
diff --git a/spec/requests/posts_controller_spec.rb b/spec/requests/posts_controller_spec.rb
index 5c91d83e07d2..ca08564a9862 100644
--- a/spec/requests/posts_controller_spec.rb
+++ b/spec/requests/posts_controller_spec.rb
@@ -1048,10 +1048,24 @@ RSpec.describe PostsController do
 
           expect(response).not_to be_successful
           parsed = response.parsed_body
           expect(parsed["action"]).not_to eq("enqueued")
         end
+
+        it "doesn't enqueue replies when the post is too long (including a html comment)" do
+          SiteSetting.max_post_length = 10
+          raw = "A post <!-- " + ("a" * 3000) + "-->"
+
+          post "/posts.json", params: {
+            raw: raw,
+            title: "this is the test title for the topic"
+          }
+
+          expect(response).not_to be_successful
+          parsed = response.parsed_body
+          expect(parsed["action"]).not_to eq("enqueued")
+        end
       end
 
       it 'silences correctly based on auto_silence_first_post_regex' do
         SiteSetting.auto_silence_first_post_regex = "I love candy|i eat s[1-5]"
 

Vulnerable code:     if !value.nil?
      value = get_sanitized_value(value)
      record.errors.add attribute, (I18n.t('errors.messages.too_short', count: range.begin)) if value.length < range.begin
      record.errors.add attribute, (I18n.t('errors.messages.too_long_validation', max: range.end, length: value.length)) if value.length > range.end
      record.errors.add attribute, (I18n.t('errors.messages.blank'))
Fixed code:     if value.nil?
      record.errors.add attribute, I18n.t('errors.messages.blank')
    elsif value.length > range.end
      record.errors.add attribute, I18n.t('errors.messages.too_long_validation', max: range.end, length: value.length)
      value = get_sanitized_value(value)

      if value.length < range.begin
        record.errors.add attribute, I18n.t('errors.messages.too_short', count: range.begin)
      end

        it "doesn't enqueue replies when the post is too long (including a html comment)" do
          SiteSetting.max_post_length = 10
          raw = "A post <!-- " + ("a" * 3000) + "-->"

          post "/posts.json", params: {
            raw: raw,
            title: "this is the test title for the topic"
          }

          expect(response).not_to be_successful
          parsed = response.parsed_body
          expect(parsed["action"]).not_to eq("enqueued")
        end