Skip to content

Commit 8437403

Browse files
authored
Security fix (#327)
* prevent sql injection * ruboxcop * simplify orderable override * escape collation search * fix location detection * use filter-map
1 parent a0464aa commit 8437403

File tree

9 files changed

+44
-38
lines changed

9 files changed

+44
-38
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
CHANGELOG
22
=========
33

4+
v0.11.2
5+
-------
6+
7+
Compatibility:
8+
- Decidim v0.28.x
9+
10+
Features:
11+
- SQL vulnerability fix for admin accountability
12+
- Private fields proposal draft update fix
13+
414
v0.11.1
515
------
616

Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
decidim-decidim_awesome (0.11.1)
4+
decidim-decidim_awesome (0.11.2)
55
decidim-admin (>= 0.28.0, < 0.29)
66
decidim-core (>= 0.28.0, < 0.29)
77
deface (>= 1.5)

app/controllers/concerns/decidim/decidim_awesome/admin_accountability/admin/filterable_helper.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ def extra_dropdown_submenu_options_items(_filter, _i18n_scope)
1313
end
1414

1515
def applied_filters_tags(i18n_ctx)
16-
if global? && params[:admin_role_type].present?
16+
admin_role_type = PaperTrailVersion.safe_admin_role_type(params[:admin_role_type])
17+
if global? && admin_role_type.present?
1718
content_tag(:span, class: "label secondary") do
1819
concat "#{i18n_filter_label(:admin_role_type, filterable_i18n_scope_from_ctx(i18n_ctx))}: "
19-
concat t("decidim.decidim_awesome.admin.admin_accountability.admin_roles.#{params[:admin_role_type]}", default: params[:admin_role_type])
20+
concat t("decidim.decidim_awesome.admin.admin_accountability.admin_roles.#{admin_role_type}", default: admin_role_type)
2021
concat icon_link_to(
2122
"close-circle-line",
2223
url_for(export_params.except(:admin_role_type)),

app/controllers/concerns/decidim/decidim_awesome/proposals/orderable_override.rb

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ module OrderableOverride
1414

1515
private
1616

17+
alias_method :decidim_original_reorder, :reorder
18+
1719
# read order from session if available
1820
def order
1921
@order ||= detect_order(session[:order]) || default_order
@@ -31,38 +33,23 @@ def possible_orders
3133
end
3234
end
3335

34-
# rubocop:disable Metrics/CyclomaticComplexity
3536
def reorder(proposals)
37+
title_by_locale = Arel.sql(proposals.sanitize_sql(["decidim_proposals_proposals.title->>? #{collation}", locale]))
38+
title_by_machine_translation = Arel.sql(proposals.sanitize_sql(["decidim_proposals_proposals.title->'machine_translations'->>? #{collation}", locale]))
39+
title_by_default_locale = Arel.sql(proposals.sanitize_sql(["decidim_proposals_proposals.title->>? #{collation}", default_locale]))
3640
case order
3741
when "az"
38-
proposals.order(Arel.sql("CONCAT(decidim_proposals_proposals.title->>'#{locale}',
39-
decidim_proposals_proposals.title->'machine_translations'->>'#{locale}',
40-
decidim_proposals_proposals.title->>'#{default_locale}') #{collation} ASC"))
42+
proposals.order(title_by_locale => :asc, title_by_machine_translation => :asc, title_by_default_locale => :asc)
4143
when "za"
42-
proposals.order(Arel.sql("CONCAT(decidim_proposals_proposals.title->>'#{locale}',
43-
decidim_proposals_proposals.title->'machine_translations'->>'#{locale}',
44-
decidim_proposals_proposals.title->>'#{default_locale}') #{collation} DESC"))
44+
proposals.order(title_by_locale => :desc, title_by_machine_translation => :desc, title_by_default_locale => :desc)
4545
when "supported_first"
4646
proposals.joins(my_votes_join).group(:id).order(Arel.sql("COUNT(decidim_proposals_proposal_votes.id) DESC"))
4747
when "supported_last"
4848
proposals.joins(my_votes_join).group(:id).order(Arel.sql("COUNT(decidim_proposals_proposal_votes.id) ASC"))
49-
when "most_commented"
50-
proposals.left_joins(:comments).group(:id).order(Arel.sql("COUNT(decidim_comments_comments.id) DESC"))
51-
when "most_endorsed"
52-
proposals.order(endorsements_count: :desc)
53-
when "most_followed"
54-
proposals.left_joins(:follows).group(:id).order(Arel.sql("COUNT(decidim_follows.id) DESC"))
55-
when "most_voted"
56-
proposals.order(proposal_votes_count: :desc)
57-
when "random"
58-
proposals.order_randomly(random_seed)
59-
when "recent"
60-
proposals.order(published_at: :desc)
61-
when "with_more_authors"
62-
proposals.order(coauthorships_count: :desc)
49+
else
50+
decidim_original_reorder(proposals)
6351
end
6452
end
65-
# rubocop:enable Metrics/CyclomaticComplexity
6653

6754
def collation
6855
@collation ||= begin

app/controllers/decidim/decidim_awesome/admin/admin_accountability_controller.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module DecidimAwesome
55
module Admin
66
class AdminAccountabilityController < DecidimAwesome::Admin::ApplicationController
77
include NeedsAwesomeConfig
8-
include Decidim::DecidimAwesome::AdminAccountability::Admin::Filterable
8+
include AdminAccountability::Admin::Filterable
99

1010
helper_method :admin_actions, :collection, :export_params, :global?, :global_users_missing_date
1111

@@ -20,9 +20,9 @@ def index; end
2020
def export
2121
filters = export_params[:q]
2222

23-
Decidim::DecidimAwesome::ExportAdminActionsJob.perform_later(current_user,
24-
params[:format].to_s,
25-
admin_actions.ransack(filters).result.ids)
23+
ExportAdminActionsJob.perform_later(current_user,
24+
params[:format].to_s,
25+
admin_actions.ransack(filters).result.ids)
2626

2727
redirect_back fallback_location: decidim_admin_decidim_awesome.admin_accountability_path,
2828
notice: t("decidim.decidim_awesome.admin.admin_accountability.exports.notice")
@@ -39,11 +39,11 @@ def collection
3939
end
4040

4141
def space_role_actions
42-
@space_role_actions ||= Decidim::DecidimAwesome::PaperTrailVersion.space_role_actions(current_organization)
42+
@space_role_actions ||= PaperTrailVersion.space_role_actions(current_organization)
4343
end
4444

4545
def admin_role_actions
46-
@admin_role_actions ||= Decidim::DecidimAwesome::PaperTrailVersion.in_organization(current_organization).admin_role_actions(params[:admin_role_type])
46+
@admin_role_actions ||= PaperTrailVersion.in_organization(current_organization).admin_role_actions(PaperTrailVersion.safe_admin_role_type(params[:admin_role_type]))
4747
end
4848

4949
def export_params
@@ -61,7 +61,7 @@ def global_users_missing_date
6161
return unless global?
6262

6363
@global_users_missing_date ||= begin
64-
first_version = Decidim::DecidimAwesome::PaperTrailVersion.where(item_type: "Decidim::UserBaseEntity").last
64+
first_version = PaperTrailVersion.where(item_type: "Decidim::UserBaseEntity").last
6565
first_user = Decidim::User.first
6666
first_version.created_at if first_user && first_version && (first_version.created_at > first_user.created_at + 1.second)
6767
end

app/models/decidim/decidim_awesome/paper_trail_version.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ def self.safe_user_roles
99
DecidimAwesome.participatory_space_roles.filter(&:safe_constantize)
1010
end
1111

12+
def self.safe_admin_role_type(admin_role)
13+
Decidim.user_roles.find { |role| role == admin_role }
14+
end
15+
1216
scope :space_role_actions, lambda { |organization|
1317
role_changes = where(item_type: PaperTrailVersion.safe_user_roles, event: "create")
1418
user_ids_from_object_changes = role_changes.pluck(:object_changes).map { |change| change.match(/decidim_user_id:\n- ?\n- (\d+)/)[1].to_i }
@@ -33,7 +37,7 @@ def self.admin_role_actions(filter = nil)
3337
when "admin"
3438
base.where("object_changes LIKE '%\nadmin:\n- false\n- true%'")
3539
else
36-
base.where(Arel.sql("object_changes LIKE '%\nroles:\n- []\n- - #{filter}\n%'"))
40+
base.where("object_changes LIKE ?", "%\nroles:\n- []\n- - #{filter}\n%")
3741
end
3842
end
3943

lib/decidim/decidim_awesome/awesome.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -333,11 +333,11 @@ def self.hash_prepend!(hash, before_key, key, value)
333333

334334
def self.collation_for(locale)
335335
@collation_for ||= {}
336-
@collation_for[locale] ||= begin
337-
res = ActiveRecord::Base.connection.execute(Arel.sql("SELECT collname FROM pg_collation WHERE collname LIKE '#{locale}-x-icu' LIMIT 1")).first
338-
res ||= ActiveRecord::Base.connection.execute(Arel.sql("SELECT collname FROM pg_collation WHERE collname LIKE '#{locale[0..1]}%' LIMIT 1")).first
336+
@collation_for[locale] ||= ["#{locale}-x-icu", "#{locale[0..1]}%"].filter_map do |loc|
337+
sql = ApplicationRecord.sanitize_sql(["SELECT collname FROM pg_collation WHERE collname LIKE ? LIMIT 1", loc])
338+
res = ActiveRecord::Base.connection.execute(sql).first
339339
res["collname"] if res
340-
end
340+
end.first
341341
end
342342

343343
def self.enabled?(*config_vars)

lib/decidim/decidim_awesome/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
module Decidim
44
# This holds the decidim-decidim_awesome version.
55
module DecidimAwesome
6-
VERSION = "0.11.1"
6+
VERSION = "0.11.2"
77
COMPAT_DECIDIM_VERSION = [">= 0.28.0", "< 0.29"].freeze
88
end
99
end

spec/models/paper_trail_version_spec.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ module Decidim::DecidimAwesome
106106
expect(PaperTrailVersion.in_organization(external_organization).admin_role_actions("valuator")).to include(external_paper_trail_version)
107107
expect(PaperTrailVersion.in_organization(external_organization).admin_role_actions("admin")).to eq([])
108108
end
109+
110+
it "ignores invalid filters" do
111+
expect(PaperTrailVersion.in_organization(organization).admin_role_actions("%')")).to eq([])
112+
end
109113
end
110114
end
111115
end

0 commit comments

Comments
 (0)