CVE-2017-16804: Exposure of Sensitive Information to an Unauthorized Actor (CWE-200)
value: In Redmine before 3.2.7 and 3.3.x before 3.3.4, the reminders function in app/models/mailer.rb does not check whether an issue is visible, which allows remote authenticated users to obtain sensitive information by reading e-mail reminder messages.
CWE description: The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.
CVSS v3 base score: 4.3
Published: 2017-11-13T20:29Z
Repository: https://github.com/redmine/redmine
Language: Ruby
Files changed: app/models/mailer.rb, test/unit/mailer_test.rb
Commit date: 2017-05-13 09:38:05 +0000
Commit message: Send reminders about visible issues only (#25713).

Patch by Felix Schäfer.

git-svn-id: http://svn.redmine.org/redmine/trunk@16557 e93f8b46-1217-0410-a6f0-8f06a7374b81

Diff: diff --git a/app/models/mailer.rb b/app/models/mailer.rb
index fe0279240b..f6b16b9f2f 100644
--- a/app/models/mailer.rb
+++ b/app/models/mailer.rb
@@ -404,11 +404,14 @@ class Mailer < ActionMailer::Base
         end
       end
     end
 
     issues_by_assignee.each do |assignee, issues|
-      reminder(assignee, issues, days).deliver if assignee.is_a?(User) && assignee.active?
+      if assignee.is_a?(User) && assignee.active? && issues.present?
+        visible_issues = issues.select {|i| i.visible?(assignee)}
+        reminder(assignee, visible_issues, days).deliver if visible_issues.present?
+      end
     end
   end
 
   # Activates/desactivates email deliveries during +block+
   def self.with_deliveries(enabled = true, &block)
diff --git a/test/unit/mailer_test.rb b/test/unit/mailer_test.rb
index b4983f925a..bc5fa9ea2c 100644
--- a/test/unit/mailer_test.rb
+++ b/test/unit/mailer_test.rb
@@ -661,10 +661,29 @@ class MailerTest < ActiveSupport::TestCase
       mail = last_email
       assert mail.bcc.include?('dlopper@somenet.foo')
     end
   end
 
+  def test_reminders_should_only_include_issues_the_user_can_see
+    with_settings :default_language => 'en' do
+      user = User.find(3)
+      member = Member.create!(:project_id => 2, :principal => user, :role_ids => [1])
+      Issue.create!(:project_id => 2, :tracker_id => 1, :status_id => 1,
+                      :subject => 'Issue dlopper should not see', :assigned_to_id => 3,
+                      :due_date => 5.days.from_now,
+                      :author_id => 2)
+      member.destroy
+      ActionMailer::Base.deliveries.clear
+
+      Mailer.reminders(:days => 42)
+      assert_equal 1, ActionMailer::Base.deliveries.size
+      mail = last_email
+      assert mail.bcc.include?('dlopper@somenet.foo')
+      assert_mail_body_no_match 'Issue dlopper should not see', mail
+    end
+  end
+
   def test_security_notification
     set_language_if_valid User.find(1).language
     with_settings :emails_footer => "footer without link" do
       User.current.remote_ip = '192.168.1.1'
       assert Mailer.security_notification(User.find(1), message: :notice_account_password_updated).deliver

Vulnerable code:       reminder(assignee, issues, days).deliver if assignee.is_a?(User) && assignee.active?
Fixed code:       if assignee.is_a?(User) && assignee.active? && issues.present?
        visible_issues = issues.select {|i| i.visible?(assignee)}
        reminder(assignee, visible_issues, days).deliver if visible_issues.present?
      end
  def test_reminders_should_only_include_issues_the_user_can_see
    with_settings :default_language => 'en' do
      user = User.find(3)
      member = Member.create!(:project_id => 2, :principal => user, :role_ids => [1])
      Issue.create!(:project_id => 2, :tracker_id => 1, :status_id => 1,
                      :subject => 'Issue dlopper should not see', :assigned_to_id => 3,
                      :due_date => 5.days.from_now,
                      :author_id => 2)
      member.destroy
      ActionMailer::Base.deliveries.clear

      Mailer.reminders(:days => 42)
      assert_equal 1, ActionMailer::Base.deliveries.size
      mail = last_email
      assert mail.bcc.include?('dlopper@somenet.foo')
      assert_mail_body_no_match 'Issue dlopper should not see', mail
    end
  end