Skip to content

Commit 33a39ad

Browse files
committed
feat: Implement source type validation in Joatu controllers and enhance Exchange concern
1 parent 8f96fbd commit 33a39ad

File tree

5 files changed

+32
-3
lines changed

5 files changed

+32
-3
lines changed

app/controllers/better_together/joatu/joatu_controller.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@ def resource_params # rubocop:todo Metrics/CyclomaticComplexity, Metrics/MethodL
2525

2626
rp
2727
end
28+
29+
private
30+
31+
# Safely resolve a source_type parameter to a valid Joatu model class
32+
# Allow-list only classes that include the Exchange concern to prevent security issues
33+
def joatu_source_class(source_type_param)
34+
param_type = source_type_param.to_s
35+
36+
# Dynamically build allow-list from models that include the Exchange concern
37+
valid_source_types = BetterTogether::Joatu::Exchange.included_in_models
38+
39+
valid_source_types.find { |klass| klass.to_s == param_type }
40+
end
2841
end
2942
end
3043
end

app/controllers/better_together/joatu/offers_controller.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ def new # rubocop:todo Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedC
8282
resource_instance
8383
# If source params were provided, load and authorize the source so the view can safely render it
8484
if (source_type = params[:source_type].presence) && (source_id = params[:source_id].presence)
85-
source_klass = source_type.to_s.safe_constantize
85+
source_klass = joatu_source_class(source_type)
86+
return unless source_klass
87+
8688
@source = source_klass&.with_translations&.includes(:categories, :address, creator: :string_translations)&.find_by(id: source_id) # rubocop:disable Layout/LineLength,Style/SafeNavigationChainLength
8789
begin
8890
authorize @source if @source

app/controllers/better_together/joatu/requests_controller.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,9 @@ def new # rubocop:todo Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/Me
8787
resource_instance
8888
# If source params were provided, load and authorize the source so the view can safely render it
8989
if (source_type = params[:source_type].presence) && (source_id = params[:source_id].presence)
90-
source_klass = source_type.to_s.safe_constantize
90+
source_klass = joatu_source_class(source_type)
91+
return unless source_klass
92+
9193
@source = source_klass&.with_translations&.includes(:categories, :address, creator: :string_translations)&.find_by(id: source_id) # rubocop:disable Layout/LineLength,Style/SafeNavigationChainLength
9294
begin
9395
authorize @source if @source

app/controllers/better_together/joatu/response_links_controller.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,13 @@ def create # rubocop:todo Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics
2121
alert: 'Invalid source'
2222
end
2323

24-
source = source_type.constantize.with_translations.includes(:categories, :address, creator: :string_translations).find_by(id: source_id) # rubocop:disable Layout/LineLength
24+
source_klass = joatu_source_class(source_type)
25+
unless source_klass
26+
return redirect_back fallback_location: joatu_hub_path,
27+
alert: 'Invalid source type'
28+
end
29+
30+
source = source_klass.with_translations.includes(:categories, :address, creator: :string_translations).find_by(id: source_id) # rubocop:disable Layout/LineLength
2531
return redirect_back fallback_location: joatu_hub_path, alert: 'Source not found' unless source
2632

2733
# Only allow creating responses against sources that are open or already matched

app/models/concerns/better_together/joatu/exchange.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ def permitted_attributes(id: false, destroy: false)
6060
end
6161
end
6262

63+
def self.included_in_models
64+
included_module = self
65+
Rails.application.eager_load! if Rails.env.development? # Ensure all models are loaded
66+
ActiveRecord::Base.descendants.select { |model| model.included_modules.include?(included_module) }
67+
end
68+
6369
# Return matching counterpart records (requests for offers, offers for requests)
6470
def find_matches
6571
BetterTogether::Joatu::Matchmaker.match(self)

0 commit comments

Comments
 (0)