Commit af86ac8d1f15b627c0c9ec68928332916e0a54fd

Authored by Leandro Santos
2 parents 8307cfb7 27f11bf7
Exists in staging and in 1 other branch production

merging with master

.travis.yml
... ... @@ -5,15 +5,19 @@ notifications:
5 5 template:
6 6 - "%{repository_slug} %{branch} %{commit} %{commit_subject} - %{result} %{build_url}"
7 7  
8   -# trusty constainers take more time to start
9   -#dist: trusty
  8 +# Ensure Container-based environment, as others can have some random failures
  9 +# specially with different Firefox versions and selenium tests.
  10 +# E.g. https://travis-ci.org/noosfero/noosfero/jobs/122918772#L1308
  11 +#
  12 +# Also container-based environments have the fatest boot times and
  13 +# are the only one with cache available for public projects.
  14 +# See https://docs.travis-ci.com/user/ci-environment/#Virtualization-environments
  15 +sudo: false
  16 +cache: bundler
10 17  
11 18 language: ruby
12 19 rvm:
13   - - 2.2
14   - # ruby 2.3 works but isn't stable on travis
15   -
16   -cache: bundler
  20 + - 2.3.0
17 21  
18 22 addons:
19 23 apt:
... ...
app/controllers/my_profile/tasks_controller.rb
... ... @@ -18,16 +18,16 @@ class TasksController < MyProfileController
18 18 @task_types = Task.pending_types_for(profile)
19 19 @task_tags = [OpenStruct.new(:name => _('All'), :id => nil) ] + Task.all_tags
20 20  
21   - @tasks = Task.pending_all(profile, @filter_type, @filter_text).order_by('created_at', 'asc')
  21 + @tasks = Task.pending_all(profile, @filter_type, @filter_text).order_by('created_at', 'asc').paginate(:per_page => Task.per_page, :page => params[:page])
22 22 @tasks = @tasks.where(:responsible_id => @filter_responsible.to_i != -1 ? @filter_responsible : nil) if @filter_responsible.present?
23 23 @tasks = @tasks.tagged_with(@filter_tags, any: true) if @filter_tags.present?
24 24 @tasks = @tasks.paginate(:per_page => Task.per_page, :page => params[:page])
25   -
26 25 @failed = params ? params[:failed] : {}
27 26  
28 27 @responsible_candidates = profile.members.by_role(profile.roles.reject {|r| !r.has_permission?('perform_task') && !r.has_permission?('view_tasks')}) if profile.organization?
29 28  
30 29 @view_only = !current_person.has_permission?(:perform_task, profile)
  30 +
31 31 end
32 32  
33 33 def processed
... ...
app/helpers/comment_helper.rb
... ... @@ -66,7 +66,7 @@ module CommentHelper
66 66  
67 67 def link_for_edit(comment)
68 68 if comment.can_be_updated_by?(user)
69   - {:link => expirable_comment_link(comment, :edit, _('Edit'), url_for(:profile => profile.identifier, :controller => :comment, :action => :edit, :id => comment.id),:class => 'modal')}
  69 + {:link => expirable_comment_link(comment, :edit, _('Edit'), url_for(:profile => profile.identifier, :controller => :comment, :action => :edit, :id => comment.id), :modal => true)}
70 70 end
71 71 end
72 72  
... ...
app/helpers/sanitize_helper.rb 0 → 100644
... ... @@ -0,0 +1,25 @@
  1 +module SanitizeHelper
  2 +
  3 + def sanitize_html(text, type= :full_sanitize)
  4 + sanitizer(type).sanitize(text, scrubber: permit_scrubber)
  5 + end
  6 +
  7 + def sanitize_link(text)
  8 + sanitizer(:white_list).sanitize(text, scrubber:permit_scrubber)
  9 + end
  10 +
  11 +protected
  12 +
  13 + def permit_scrubber
  14 + scrubber = Rails::Html::PermitScrubber.new
  15 + scrubber.tags = Rails.application.config.action_view.sanitized_allowed_tags
  16 + scrubber.attributes = Rails.application.config.action_view.sanitized_allowed_attributes
  17 + scrubber
  18 + end
  19 +
  20 + def sanitizer type = :full_sanitize
  21 + return HTML::WhiteListSanitizer.new if type == :white_list
  22 + HTML::FullSanitizer.new
  23 + end
  24 +
  25 +end
... ...
app/models/article.rb
1 1  
2 2 class Article < ActiveRecord::Base
3 3  
  4 + include SanitizeHelper
  5 +
4 6 attr_accessible :name, :body, :abstract, :profile, :tag_list, :parent,
5 7 :allow_members_to_edit, :translation_of_id, :language,
6 8 :license_id, :parent_id, :display_posts_in_current_language,
... ... @@ -54,6 +56,7 @@ class Article &lt; ActiveRecord::Base
54 56 track_actions :create_article, :after_create, :keep_params => [:name, :url, :lead, :first_image], :if => Proc.new { |a| a.is_trackable? && !a.image? }
55 57  
56 58 # xss_terminate plugin can't sanitize array fields
  59 + # sanitize_tag_list is used with SanitizeHelper
57 60 before_save :sanitize_tag_list
58 61  
59 62 before_create do |article|
... ... @@ -875,11 +878,6 @@ class Article &lt; ActiveRecord::Base
875 878 tag_name.gsub(/[<>]/, '')
876 879 end
877 880  
878   - def sanitize_html(text)
879   - sanitizer = HTML::FullSanitizer.new
880   - sanitizer.sanitize(text)
881   - end
882   -
883 881 def parent_archived?
884 882 if self.parent_id_changed? && self.parent && self.parent.archived?
885 883 errors.add(:parent_folder, N_('is archived!!'))
... ...
app/models/link_list_block.rb
1 1 class LinkListBlock < Block
2 2  
  3 + include SanitizeHelper
  4 +
3 5 attr_accessible :links
4 6  
5 7 ICONS = [
... ... @@ -85,9 +87,4 @@ class LinkListBlock &lt; Block
85 87 end
86 88 end
87 89  
88   - def sanitize_link(text)
89   - sanitizer = HTML::WhiteListSanitizer.new
90   - sanitizer.sanitize(text)
91   - end
92   -
93 90 end
... ...
app/models/scrap.rb
1 1 class Scrap < ActiveRecord::Base
2 2  
  3 + include SanitizeHelper
  4 +
3 5 attr_accessible :content, :sender_id, :receiver_id, :scrap_id
4 6  
5 7 SEARCHABLE_FIELDS = {
... ... @@ -41,8 +43,7 @@ class Scrap &lt; ActiveRecord::Base
41 43 end
42 44  
43 45 def strip_all_html_tags
44   - sanitizer = HTML::WhiteListSanitizer.new
45   - self.content = sanitizer.sanitize(self.content, :tags => [])
  46 + self.content = sanitize_html(self.content)
46 47 end
47 48  
48 49 def action_tracker_target
... ...
app/models/task.rb
... ... @@ -347,6 +347,7 @@ class Task &lt; ActiveRecord::Base
347 347 where [environment_condition, profile_condition].compact.join(' OR ')
348 348 }
349 349  
  350 +
350 351 def self.pending_types_for(profile)
351 352 Task.to(profile).pending.select('distinct type').map { |t| [t.class.name, t.title] }
352 353 end
... ...
app/models/textile_article.rb
1 1 class TextileArticle < TextArticle
  2 + include SanitizeHelper
2 3  
3 4 def self.short_description
4 5 _('Text article with Textile markup language')
... ... @@ -31,10 +32,9 @@ class TextileArticle &lt; TextArticle
31 32 protected
32 33  
33 34 def convert_to_html(textile)
34   - @@sanitizer ||= HTML::WhiteListSanitizer.new
35 35 converter = RedCloth.new(textile|| '')
36 36 converter.hard_breaks = false
37   - @@sanitizer.sanitize(converter.to_html)
  37 + sanitize_html(converter.to_html, :white_list)
38 38 end
39 39  
40 40 end
... ...
app/views/tasks/index.html.erb
... ... @@ -21,18 +21,18 @@
21 21 </div>
22 22 <% end %>
23 23  
24   -<%= form_tag '#', :method => 'get' do %>
25   - <%= field_set_tag _('Filter'), :class => 'filter_fields' do %>
26   - <p>
27   - <%= labelled_select(_('Type of task')+': ', :filter_type, :first, :last, @filter_type, type_collection, {:id => 'filter-type'}) %>
28   - </p>
29   - <p>
30   - <%= labelled_text_field(_("Text filter")+': ', :filter_text, nil, {:id => 'filter-text',:value => @filter_text}) %>
31   - </p>
32   - <% if profile.organization? %>
33   - <p>
34   - <%= labelled_select(_('Assigned to')+': ', :filter_responsible, :id, :name, @filter_responsible, [OpenStruct.new(:name => _('All'), :id => nil), OpenStruct.new(:name => _('Unassigned'), :id => -1)] + @responsible_candidates, :class => 'filter_responsible') %>
35   - </p>
  24 +<%= form_tag '#', :method => 'post' do %>
  25 +
  26 + <%= field_set_tag _('Filter'), :class => 'filter_fields' do %>
  27 + <p>
  28 + <%= labelled_select(_('Type of task')+': ', :filter_type, :first, :last, @filter_type, type_collection, {:id => 'filter-type'}) %>
  29 + </p>
  30 + <p>
  31 + <%= labelled_text_field(_("Text filter")+': ', :filter_text, nil, {:id => 'filter-text-autocomplete',:value => @filter_text}) %>
  32 + </p>
  33 + <p>
  34 + <%= submit_button(:search, _('Search')) %>
  35 + </p>
36 36 <% end %>
37 37 <p>
38 38 <%= labelled_select(_('Tags')+': ', :filter_tags, :id, :name, @filter_tags, @task_tags, {:id => 'filter-add-tag'}) %>
... ... @@ -44,7 +44,6 @@
44 44 </p>
45 45 <% end %>
46 46 <% end %>
47   -
48 47 <% if @tasks.empty? %>
49 48 <p>
50 49 <em><%= _('No pending tasks for %s') % profile.name %></em>
... ... @@ -64,7 +63,7 @@
64 63 <p>
65 64 <%= labelled_select(_("Set all to: "), 'set-decisions', 'first', 'last', nil, [['',""],['accept',_("Accept")],['reject',_("Reject")],['skip',_("Skip")]], :id => "up-set-all-tasks-to") %>
66 65 </p>
67   - <% end %>
  66 + <% end %>
68 67  
69 68 <div class="task_boxes">
70 69 <% @tasks.each do |task| %>
... ... @@ -73,11 +72,11 @@
73 72 </div>
74 73  
75 74 <% unless @view_only %>
76   - <p>
77   - <%= labelled_select(_("Set all to: "), 'set-decisions', 'first', 'last', nil, [['',""],['accept',_("Accept")],['reject',_("Reject")],['skip',_("Skip")]], :id => "down-set-all-tasks-to") %>
78   - </p>
  75 + <p>
  76 + <%= labelled_select(_("Set all to: "), 'set-decisions', 'first', 'last', nil, [['',""],['accept',_("Accept")],['reject',_("Reject")],['skip',_("Skip")]], :id => "down-set-all-tasks-to") %>
  77 + </p>
79 78 <% end %>
80   - </ul>
  79 + </ul>
81 80  
82 81 <script>
83 82 jQuery('.tag-list').inputosaurus({
... ...
config/application.rb
... ... @@ -15,6 +15,21 @@ module Noosfero
15 15  
16 16 require 'noosfero/plugin'
17 17  
  18 + # The plugin xss_terminator(located in vendor/plugins/xss_terminator) and the helper
  19 + # SanitizeHelper(located in app/helpers/sanitize_helper.rb) use
  20 + # ALLOWED_TAGS and ALLOWED_ATTRIBUTES to make a sanitize with html.
  21 +
  22 + ALLOWED_TAGS = %w(object embed param table tr th td applet comment iframe audio video source
  23 + strong em b i p code pre tt samp kbd var sub sup dfn cite big small address hr br div span h1
  24 + h2 h3 h4 h5 h6 ul ol li dl dt dd abbr acronym a img blockquote del ins a)
  25 +
  26 + ALLOWED_ATTRIBUTES = %w(name href cite class title src xml:lang height datetime alt abbr width
  27 + vspace hspace heigth value type data style target codebase archive data-macro align border
  28 + classid code flashvars scrolling frameborder controls autoplay colspan id rowspan)
  29 +
  30 + config.action_view.sanitized_allowed_tags = ALLOWED_TAGS
  31 + config.action_view.sanitized_allowed_attributes = ALLOWED_ATTRIBUTES
  32 +
18 33 require 'noosfero/multi_tenancy'
19 34 config.middleware.use Noosfero::MultiTenancy::Middleware
20 35  
... ...
config/initializers/sanitizer.rb
... ... @@ -1,14 +0,0 @@
1   -require 'loofah/helpers'
2   -
3   -ActionView::Base.full_sanitizer = Loofah::Helpers::ActionView::FullSanitizer.new
4   -ActionView::Base.white_list_sanitizer = Loofah::Helpers::ActionView::WhiteListSanitizer.new
5   -
6   -Loofah::HTML5::WhiteList::ALLOWED_ELEMENTS_WITH_LIBXML2.merge %w[
7   - img object embed param table tr th td applet comment iframe audio video source
8   -]
9   -
10   -Loofah::HTML5::WhiteList::ALLOWED_ATTRIBUTES.merge %w[
11   - align border alt vspace hspace width heigth value type data
12   - style target codebase archive classid code flashvars scrolling frameborder controls autoplay colspan
13   -]
14   -
lib/sanitize_params.rb
... ... @@ -2,40 +2,40 @@ module SanitizeParams
2 2  
3 3 protected
4 4  
5   - # Check each request parameter for
6   - # improper HTML or Script tags
7   - def sanitize_params
8   - sanitize_params_hash(request.params)
9   - end
  5 + # Check each request parameter for
  6 + # improper HTML or Script tags
  7 + def sanitize_params
  8 + sanitize_params_hash(params)
  9 + end
10 10  
11   - # Given a params list sanitize all
12   - def sanitize_params_hash(params)
13   - params.each { |k, v|
14   - if v.is_a?(String)
15   - params[k] = sanitize_param v
16   - elsif v.is_a?(Array)
17   - params[k] = sanitize_array v
18   - elsif v.kind_of?(Hash)
19   - params[k] = sanitize_params_hash(v)
20   - end
21   - }
22   - end
  11 + # Given a params list sanitize all
  12 + def sanitize_params_hash(params)
  13 + params.each { |k, v|
  14 + if v.is_a?(String)
  15 + params[k] = sanitize_param v
  16 + elsif v.is_a?(Array)
  17 + params[k] = sanitize_array v
  18 + elsif v.kind_of?(Hash)
  19 + params[k] = sanitize_params_hash(v)
  20 + end
  21 + }
  22 + end
23 23  
24   - # If the parameter was an array,
25   - # try to sanitize each element in the array
26   - def sanitize_array(array)
27   - array.map! { |e|
28   - if e.is_a?(String)
29   - sanitize_param e
30   - end
31   - }
32   - return array
33   - end
  24 + # If the parameter was an array,
  25 + # try to sanitize each element in the array
  26 + def sanitize_array(array)
  27 + array.map! { |e|
  28 + if e.is_a?(String)
  29 + sanitize_param e
  30 + end
  31 + }
  32 + return array
  33 + end
34 34  
35   - # Santitize a single value
36   - def sanitize_param(value)
37   - allowed_tags = %w(a acronym b strong i em li ul ol h1 h2 h3 h4 h5 h6 blockquote br cite sub sup ins p)
38   - ActionController::Base.helpers.sanitize(value, tags: allowed_tags, attributes: %w(href title))
39   - end
  35 + # Santitize a single value
  36 + def sanitize_param(value)
  37 + allowed_tags = %w(a acronym b strong i em li ul ol h1 h2 h3 h4 h5 h6 blockquote br cite sub sup ins p)
  38 + ActionController::Base.helpers.sanitize(value, tags: allowed_tags, attributes: %w(href title))
  39 + end
40 40  
41   -end
  41 +end
... ...
test/functional/search_controller_test.rb
... ... @@ -788,15 +788,18 @@ class SearchControllerTest &lt; ActionController::TestCase
788 788 injection = '<iMg SrC=x OnErRoR=document.documentElement.innerHTML=1>SearchParam'
789 789 get :tag, :tag => injection
790 790 tag = assigns(:tag)
791   - assert !tag.upcase.include?('IMG') && tag.include?('SearchParam')
  791 + assert !tag.upcase.include?('IMG')
  792 + assert tag.include?('SearchParam')
792 793 end
793 794  
794   - should 'not allow query injection array' do
795   - injection = ['<iMg SrC=x OnErRoR=document.documentElement.innerHTML=1>', '<script>document.innerHTML = \'x\'</script>']
  795 + should 'not allow query injection in array' do
  796 + injection = ['<iMg SrC=x OnErRoR=document.documentElement.innerHTML=1>',
  797 + '<script>document.innerHTML = \'x\'</script>']
796 798 get :tag, :tag => injection
797 799 tag = assigns(:tag)
798   - tag.each { |t|
799   - assert !t.upcase.include?('IMG') && !t.upcase.include?('SCRIPT')
  800 + tag.each { |t|
  801 + assert !t.upcase.include?('IMG')
  802 + assert !t.upcase.include?('SCRIPT')
800 803 }
801 804 end
802 805  
... ...
test/functional/tasks_controller_test.rb
... ... @@ -4,6 +4,7 @@ require &#39;tasks_controller&#39;
4 4 class TasksControllerTest < ActionController::TestCase
5 5  
6 6 self.default_params = {profile: 'testuser'}
  7 +
7 8 def setup
8 9 @controller = TasksController.new
9 10 @request = ActionController::TestRequest.new
... ... @@ -28,12 +29,12 @@ class TasksControllerTest &lt; ActionController::TestCase
28 29 end
29 30  
30 31 should 'get filtered tasks to autocomplete text field' do
31   -
  32 +
32 33 #Create a admin user and a simple user
33 34 profile_admin = create_user('admin_tester').person
34 35 Environment.default.add_admin(profile_admin)
35 36 user = fast_create(Person,:name => 'FakeUser')
36   -
  37 +
37 38 #Create a task of type 'ModerateUserRegistration'
38 39 task_data = {
39 40 :target => Environment.default,
... ... @@ -41,20 +42,20 @@ class TasksControllerTest &lt; ActionController::TestCase
41 42 :data => {:user_id => user.id,:name => user.name}
42 43 }
43 44 ModerateUserRegistration.create!(task_data)
44   -
  45 +
45 46 #Use admin user to your profile with a pending task above
46 47 @controller.stubs(:profile).returns(profile_admin)
47 48 login_as profile_admin.identifier
48   -
  49 +
49 50 #Perform a http request to 'search_task' action with params
50 51 post :search_tasks, :filter_type =>'ModerateUserRegistration', :filter_text => 'Fak'
51   -
  52 +
52 53 assert_response :success
53   -
  54 +
54 55 #Check if json response matches with a 'FakeUser'
55 56 json_response = ActiveSupport::JSON.decode(@response.body)
56 57 value = json_response[0]['value']
57   -
  58 +
58 59 assert_equal value, 'FakeUser'
59 60 end
60 61  
... ... @@ -468,13 +469,13 @@ class TasksControllerTest &lt; ActionController::TestCase
468 469 t2 = CleanHouse.create!(:requestor => requestor, :target => profile)
469 470 t3 = FeedDog.create!(:requestor => requestor, :target => profile)
470 471  
471   - get :index, :filter_type => t1.type, :filter_text => 'test'
  472 + post :index, :filter_type => t1.type, :filter_text => 'test'
472 473  
473 474 assert_includes assigns(:tasks), t1
474 475 assert_not_includes assigns(:tasks), t2
475 476 assert_not_includes assigns(:tasks), t3
476 477  
477   - get :index
  478 + post :index
478 479  
479 480 assert_includes assigns(:tasks), t1
480 481 assert_includes assigns(:tasks), t2
... ...
vendor/plugins/xss_terminate/lib/xss_terminate.rb
1 1 module XssTerminate
2   - ALLOWED_CORE_ATTRIBUTES = %w(name href cite class title src xml:lang height datetime alt abbr width)
3   - ALLOWED_CUSTOM_ATTRIBUTES = %w(data-macro)
4 2  
5 3 def self.sanitize_by_default=(value)
6 4 @@sanitize_by_default = value
... ... @@ -40,30 +38,33 @@ module XssTerminate
40 38  
41 39 module InstanceMethods
42 40  
43   - def sanitize_allowed_attributes
44   - ALLOWED_CORE_ATTRIBUTES | ALLOWED_CUSTOM_ATTRIBUTES
45   - end
46   -
47 41 def sanitize_field(sanitizer, field, serialized = false)
48 42 field = field.to_sym
49 43 if serialized
50 44 puts field
51 45 self[field].each_key { |key|
52 46 key = key.to_sym
53   - self[field][key] = sanitizer.sanitize(self[field][key], scrubber: Rails::Html::PermitScrubber.new, encode_special_chars: false, attributes: sanitize_allowed_attributes)
  47 + self[field][key] = sanitizer.sanitize(self[field][key], encode_special_chars: false, scrubber: permit_scrubber )
54 48 }
55 49 else
56 50 if self[field]
57   - self[field] = sanitizer.sanitize(self[field], scrubber: Rails::Html::PermitScrubber.new, encode_special_chars: false, attributes: sanitize_allowed_attributes)
  51 + self[field] = sanitizer.sanitize(self[field], encode_special_chars: false, scrubber: permit_scrubber )
58 52 else
59 53 value = self.send("#{field}")
60 54 return unless value
61   - value = sanitizer.sanitize(value, scrubber: Rails::Html::PermitScrubber.new, encode_special_chars: false, attributes: sanitize_allowed_attributes)
  55 + value = sanitizer.sanitize(value, encode_special_chars: false, scrubber: permit_scrubber)
62 56 self.send("#{field}=", value)
63 57 end
64 58 end
65 59 end
66 60  
  61 + def permit_scrubber
  62 + scrubber = Rails::Html::PermitScrubber.new
  63 + scrubber.tags = Rails.application.config.action_view.sanitized_allowed_tags
  64 + scrubber.attributes = Rails.application.config.action_view.sanitized_allowed_attributes
  65 + scrubber
  66 + end
  67 +
67 68 def sanitize_columns(with = :full)
68 69 columns_serialized = self.class.serialized_attributes.keys
69 70 only = eval "xss_terminate_#{with}_options[:only]"
... ... @@ -75,27 +76,20 @@ module XssTerminate
75 76 end
76 77  
77 78 def sanitize_fields_with_full
78   - sanitizer = Rails::Html::FullSanitizer.new
79   - columns, columns_serialized = sanitize_columns(:full)
80   - columns.each do |column|
81   - sanitize_field(sanitizer, column.to_sym, columns_serialized.include?(column))
82   - end
  79 + sanitize_fields_with(Rails::Html::FullSanitizer.new,:full)
83 80 end
84 81  
85 82 def sanitize_fields_with_white_list
86   - sanitizer = Rails::Html::WhiteListSanitizer.new
87   - columns, columns_serialized = sanitize_columns(:white_list)
88   - columns.each do |column|
89   - sanitize_field(sanitizer, column.to_sym, columns_serialized.include?(column))
90   - end
91   - end
  83 + sanitize_fields_with(Rails::Html::WhiteListSanitizer.new,:white_list)
  84 + end
92 85  
93 86 def sanitize_fields_with_html5lib
94   - sanitizer = HTML5libSanitize.new
95   - columns = sanitize_columns(:html5lib)
96   - columns.each do |column|
97   - sanitize_field(sanitizer, column.to_sym, columns_serialized.include?(column))
98   - end
  87 + sanitize_fields_with(HTML5libSanitize.new,:html5lib)
  88 + end
  89 +
  90 + def sanitize_fields_with sanitizer, type
  91 + columns, columns_serialized = sanitize_columns(type)
  92 + columns.each {|column| sanitize_field(sanitizer, column.to_sym, columns_serialized.include?(column))}
99 93 end
100 94  
101 95 end
... ...