Commit 406255f3879cce9388cd488badb074701dc15d28
Exists in
theme-brasil-digital-from-staging
and in
9 other branches
Merge branch 'api' of gitlab.com:participa/noosfero into api
* 'api' of gitlab.com:participa/noosfero: added random as a possible value for make_order_with_parameters added sql injection protection to method make_order_with_parameters fix to sql injections vulnerabilities identified using brakeman
Showing
7 changed files
with
98 additions
and
13 deletions
Show diff stats
app/controllers/my_profile/manage_products_controller.rb
... | ... | @@ -206,7 +206,8 @@ class ManageProductsController < ApplicationController |
206 | 206 | end |
207 | 207 | |
208 | 208 | def certifiers_for_selection |
209 | - @qualifier = Qualifier.exists?(params[:id]) ? Qualifier.find(params[:id]) : nil | |
209 | + # updated to use hash as argument to exists? to avoid sql injection vunerabillity (http://brakemanscanner.org/docs/warning_types/sql_injection/) | |
210 | + @qualifier = Qualifier.exists?(:id => params[:id]) ? Qualifier.find(params[:id]) : nil | |
210 | 211 | render :update do |page| |
211 | 212 | page.replace_html params[:certifier_area], :partial => 'certifiers_for_selection' |
212 | 213 | end | ... | ... |
app/controllers/public/contact_controller.rb
... | ... | @@ -6,8 +6,9 @@ class ContactController < PublicController |
6 | 6 | def new |
7 | 7 | @contact = build_contact |
8 | 8 | if request.post? && params[:confirm] == 'true' |
9 | - @contact.city = (!params[:city].blank? && City.exists?(params[:city])) ? City.find(params[:city]).name : nil | |
10 | - @contact.state = (!params[:state].blank? && State.exists?(params[:state])) ? State.find(params[:state]).name : nil | |
9 | + # updated to use hash as argument to exists? to avoid sql injection vunerabillity (http://brakemanscanner.org/docs/warning_types/sql_injection/) | |
10 | + @contact.city = (!params[:city].blank? && City.exists?(:id => params[:city])) ? City.find(params[:city]).name : nil | |
11 | + @contact.state = (!params[:state].blank? && State.exists?(:id => params[:state])) ? State.find(params[:state]).name : nil | |
11 | 12 | if @contact.deliver |
12 | 13 | session[:notice] = _('Contact successfully sent') |
13 | 14 | redirect_to :action => 'new' | ... | ... |
app/models/product_category.rb
... | ... | @@ -13,8 +13,11 @@ class ProductCategory < Category |
13 | 13 | scope :by_environment, lambda { |environment| { |
14 | 14 | :conditions => ['environment_id = ?', environment.id] |
15 | 15 | }} |
16 | + | |
17 | + # updated scope method to avoid sql injection vunerabillity (http://brakemanscanner.org/docs/warning_types/sql_injection/) | |
18 | + # explicited to_i on level argument | |
16 | 19 | scope :unique_by_level, lambda { |level| { |
17 | - :select => "DISTINCT ON (filtered_category) split_part(path, '/', #{level}) AS filtered_category, categories.*" | |
20 | + :select => "DISTINCT ON (filtered_category) split_part(path, '/', #{level.to_i}) AS filtered_category, categories.*" | |
18 | 21 | }} |
19 | 22 | |
20 | 23 | def all_products | ... | ... |
app/models/task.rb
... | ... | @@ -288,9 +288,34 @@ class Task < ActiveRecord::Base |
288 | 288 | scope :canceled, :conditions => { :status => Task::Status::CANCELLED } |
289 | 289 | scope :closed, :conditions => { :status => [Task::Status::CANCELLED, Task::Status::FINISHED] } |
290 | 290 | scope :opened, :conditions => { :status => [Task::Status::ACTIVE, Task::Status::HIDDEN] } |
291 | - scope :of, lambda { |type| conditions = type ? "type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} } | |
292 | - scope :order_by, lambda { |attribute, ord| {:order => "#{attribute} #{ord}"} } | |
293 | - scope :like, lambda { |field, value| where("LOWER(#{field}) LIKE ?", "%#{value.downcase}%") if value} | |
291 | + | |
292 | + # updated scope method to avoid sql injection vunerabillity (http://brakemanscanner.org/docs/warning_types/sql_injection/) | |
293 | + def self.of type | |
294 | + if type | |
295 | + where "type LIKE ?", type | |
296 | + else | |
297 | + all | |
298 | + end | |
299 | + end | |
300 | + | |
301 | + # updated scope method to avoid sql injection vunerabillity (http://brakemanscanner.org/docs/warning_types/sql_injection/) | |
302 | + def self.order_by attribute_name, sort_order | |
303 | + if Task.column_names.include? attribute_name | |
304 | + # TODO future versions of rails accepts a hash as param to order method | |
305 | + # which helps to prevent sql injection in an shorter way | |
306 | + sort_order_filtered = ("ASC".eql? "#{sort_order}".upcase) ? 'asc' : 'desc' | |
307 | + sort_expression = Task.column_names.collect {|column_name| "#{column_name} #{sort_order_filtered}" if column_name.eql? attribute_name} | |
308 | + order(sort_expression.join) unless sort_expression.join.empty? | |
309 | + end | |
310 | + end | |
311 | + | |
312 | + # updated scope method to avoid sql injection vunerabillity (http://brakemanscanner.org/docs/warning_types/sql_injection/) | |
313 | + def self.like field, value | |
314 | + if value and Tasks.column_names.include? field | |
315 | + where("LOWER(?) LIKE ?", "#{field}", "%#{value.downcase}%") | |
316 | + end | |
317 | + end | |
318 | + | |
294 | 319 | scope :pending_all, lambda { |profile, filter_type, filter_text| |
295 | 320 | self.to(profile).without_spam.pending.of(filter_type).like('data', filter_text) |
296 | 321 | } | ... | ... |
lib/activities_counter_cache_job.rb
1 | 1 | class ActivitiesCounterCacheJob |
2 | + | |
3 | + # Changed to prevent sql injection vunerabillity (http://brakemanscanner.org/docs/warning_types/sql_injection/) | |
2 | 4 | def perform |
3 | - person_activities_counts = ActiveRecord::Base.connection.execute("SELECT profiles.id, count(action_tracker.id) as count FROM profiles LEFT OUTER JOIN action_tracker ON profiles.id = action_tracker.user_id WHERE (action_tracker.created_at >= '#{ActionTracker::Record::RECENT_DELAY.days.ago.to_s(:db)}') AND ( (profiles.type = 'Person' ) ) GROUP BY profiles.id;") | |
4 | - organization_activities_counts = ActiveRecord::Base.connection.execute("SELECT profiles.id, count(action_tracker.id) as count FROM profiles LEFT OUTER JOIN action_tracker ON profiles.id = action_tracker.target_id WHERE (action_tracker.created_at >= '#{ActionTracker::Record::RECENT_DELAY.days.ago.to_s(:db)}') AND ( (profiles.type = 'Community' OR profiles.type = 'Enterprise' OR profiles.type = 'Organization' ) ) GROUP BY profiles.id;") | |
5 | + person_activities_counts = ActiveRecord::Base.connection.execute("SELECT profiles.id, count(action_tracker.id) as count FROM profiles LEFT OUTER JOIN action_tracker ON profiles.id = action_tracker.user_id WHERE (action_tracker.created_at >= #{ActiveRecord::Base.connection.quote(ActionTracker::Record::RECENT_DELAY.days.ago.to_s(:db))}) AND ( (profiles.type = 'Person' ) ) GROUP BY profiles.id;") | |
6 | + organization_activities_counts = ActiveRecord::Base.connection.execute("SELECT profiles.id, count(action_tracker.id) as count FROM profiles LEFT OUTER JOIN action_tracker ON profiles.id = action_tracker.target_id WHERE (action_tracker.created_at >= #{ActiveRecord::Base.connection.quote(ActionTracker::Record::RECENT_DELAY.days.ago.to_s(:db))}) AND ( (profiles.type = 'Community' OR profiles.type = 'Enterprise' OR profiles.type = 'Organization' ) ) GROUP BY profiles.id;") | |
5 | 7 | activities_counts = person_activities_counts.entries + organization_activities_counts.entries |
6 | 8 | activities_counts.each do |count| |
7 | - ActiveRecord::Base.connection.execute("UPDATE profiles SET activities_count=#{count['count'].to_i} WHERE profiles.id=#{count['id']};") | |
9 | + update_sql = ActiveRecord::Base.__send__(:sanitize_sql, ["UPDATE profiles SET activities_count=? WHERE profiles.id=?;", count['count'].to_i, count['id'] ], '') | |
10 | + ActiveRecord::Base.connection.execute(update_sql) | |
8 | 11 | end |
9 | 12 | Delayed::Job.enqueue(ActivitiesCounterCacheJob.new, {:priority => -3, :run_at => 1.day.from_now}) |
10 | 13 | end |
14 | + | |
11 | 15 | end | ... | ... |
lib/noosfero/api/helpers.rb
... | ... | @@ -160,8 +160,27 @@ require 'grape' |
160 | 160 | conditions |
161 | 161 | end |
162 | 162 | |
163 | - def make_order_with_parameters(params) | |
164 | - params[:order] || "created_at DESC" | |
163 | + # changing make_order_with_parameters to avoid sql injection | |
164 | + def make_order_with_parameters(object, method, params) | |
165 | + order = "created_at DESC" | |
166 | + unless params[:order].blank? | |
167 | + if params[:order].include? '\'' or params[:order].include? '"' | |
168 | + order = "created_at DESC" | |
169 | + elsif ['RANDOM()', 'RANDOM'].include? params[:order].upcase | |
170 | + order = 'RANDOM()' | |
171 | + else | |
172 | + field_name, direction = params[:order].split(' ') | |
173 | + assoc = object.class.reflect_on_association(method.to_sym) | |
174 | + if !field_name.blank? and assoc | |
175 | + if assoc.klass.attribute_names.include? field_name | |
176 | + if direction.present? and ['ASC','DESC'].include? direction.upcase | |
177 | + order = "#{field_name} #{direction.upcase}" | |
178 | + end | |
179 | + end | |
180 | + end | |
181 | + end | |
182 | + end | |
183 | + return order | |
165 | 184 | end |
166 | 185 | |
167 | 186 | def by_reference(scope, params) |
... | ... | @@ -176,7 +195,7 @@ require 'grape' |
176 | 195 | |
177 | 196 | def select_filtered_collection_of(object, method, params) |
178 | 197 | conditions = make_conditions_with_parameter(params) |
179 | - order = make_order_with_parameters(params) | |
198 | + order = make_order_with_parameters(object,method,params) | |
180 | 199 | |
181 | 200 | objects = object.send(method) |
182 | 201 | objects = by_reference(objects, params) | ... | ... |
test/unit/api/helpers_test.rb
... | ... | @@ -161,6 +161,38 @@ class APIHelpersTest < ActiveSupport::TestCase |
161 | 161 | assert_nil make_conditions_with_parameter[:type] |
162 | 162 | end |
163 | 163 | |
164 | + #test_should_make_order_with_parameters_return_order_if attribute_is_found_at_object_association | |
165 | + should 'make_order_with_parameters return order if attribute is found at object association' do | |
166 | + environment = Environment.new | |
167 | + params = {:order => "name ASC"} | |
168 | + assert_equal "name ASC", make_order_with_parameters(environment, "articles", params) | |
169 | + end | |
170 | + | |
171 | + # test added to check for eventual sql injection vunerabillity | |
172 | + #test_should_make_order_with_parameters_return_default_order_if_attributes_not_exists | |
173 | + should 'make_order_with_parameters return default order if attributes not exists' do | |
174 | + environment = Environment.new | |
175 | + params = {:order => "CRAZY_FIELD ASC"} # quote used to check sql injection vunerabillity | |
176 | + assert_equal "created_at DESC", make_order_with_parameters(environment, "articles", params) | |
177 | + end | |
178 | + | |
179 | + should 'make_order_with_parameters return default order if sql injection detected' do | |
180 | + environment = Environment.new | |
181 | + params = {:order => "name' ASC"} # quote used to check sql injection vunerabillity | |
182 | + assert_equal "created_at DESC", make_order_with_parameters(environment, "articles", params) | |
183 | + end | |
184 | + | |
185 | + should 'make_order_with_parameters return RANDOM() if random is passed' do | |
186 | + environment = Environment.new | |
187 | + params = {:order => "random"} # quote used to check sql injection vunerabillity | |
188 | + assert_equal "RANDOM()", make_order_with_parameters(environment, "articles", params) | |
189 | + end | |
190 | + | |
191 | + should 'make_order_with_parameters return RANDOM() if random function is passed' do | |
192 | + environment = Environment.new | |
193 | + params = {:order => "random()"} # quote used to check sql injection vunerabillity | |
194 | + assert_equal "RANDOM()", make_order_with_parameters(environment, "articles", params) | |
195 | + end | |
164 | 196 | |
165 | 197 | should 'render not_found if endpoint is unavailable' do |
166 | 198 | Noosfero::API::API.stubs(:endpoint_unavailable?).returns(true) | ... | ... |