discourse/app/models/theme_setting.rb
Alan Guo Xiang Tan 3e331b1725
DEV: Set a bytesize limit for ThemeSetting#json_value (#25761)
Why this change?

Firstly, note that this is not a security commit because this feature is
still in development and should not be used anywhere.

The reason we want to set a limit here is to greatly reduce the
possibility of a DoS attack in the future via `ThemeSetting` where
someone would set an arbituary large json string in
`ThemeSetting#json_value` and causing the server to run out of resources
trying to serialize/deserialize the value.

What does this change do?

Adds an ActiveRecord validation to ensure that the bytesize of the json
string being stored is smaller than or equal to 0.5mb. We believe 0.5mb
is a decent limit for now but we can review the limit in the future if
we believe it is too small.
2024-02-21 08:09:37 +08:00

85 lines
2.1 KiB
Ruby

# frozen_string_literal: true
class ThemeSetting < ActiveRecord::Base
belongs_to :theme
has_many :upload_references, as: :target, dependent: :destroy
TYPES_ENUM =
Enum.new(integer: 0, float: 1, string: 2, bool: 3, list: 4, enum: 5, upload: 6, objects: 7)
MAXIMUM_JSON_VALUE_SIZE_BYTES = 0.5 * 1024 * 1024 # 0.5 MB
validates_presence_of :name, :theme
before_validation :objects_type_enabled
validates :data_type, inclusion: { in: TYPES_ENUM.values }
validate :json_value_size, if: -> { self.data_type == TYPES_ENUM[:objects] }
validates :name, length: { maximum: 255 }
after_save :clear_settings_cache
after_destroy :clear_settings_cache
after_save do
if self.data_type == ThemeSetting.types[:upload] && saved_change_to_value?
UploadReference.ensure_exist!(upload_ids: [self.value], target: self)
end
end
def clear_settings_cache
# All necessary caches will be cleared on next ensure_baked!
theme.settings_field&.invalidate_baked!
end
def self.types
TYPES_ENUM
end
def self.guess_type(value)
case value
when Integer
types[:integer]
when Float
types[:float]
when String
types[:string]
when TrueClass, FalseClass
types[:bool]
end
end
private
def objects_type_enabled
if self.data_type == ThemeSetting.types[:objects] &&
!SiteSetting.experimental_objects_type_for_theme_settings
self.data_type = nil
end
end
def json_value_size
if json_value.to_json.size > MAXIMUM_JSON_VALUE_SIZE_BYTES
errors.add(
:json_value,
I18n.t(
"theme_settings.errors.json_value.too_large",
max_size_megabytes: MAXIMUM_JSON_VALUE_SIZE_BYTES / 1024 / 1024,
),
)
end
end
end
# == Schema Information
#
# Table name: theme_settings
#
# id :bigint not null, primary key
# name :string(255) not null
# data_type :integer not null
# value :text
# theme_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# json_value :jsonb
#