CVE-2018-20851 (published 2019-07-10T14:15Z)

Helpy before 2.2.0 allows agents to edit admins.

Severity: MEDIUM
CVSS v2 base score: 6.5 (AV:N/AC:L/Au:S/C:P/I:P/A:P)
CVSS v3 base score: 8.8, HIGH (CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/helpyio/helpy
commit_details: repo_url: https://github.com/helpyio/helpy
author: scott
author_date: 2018-10-13 10:20:28-06:00
msg: Prevent agents from editing admins, other permission hardening
repository: repo_url: https://github.com/helpyio/helpy
repo_name: helpyio/helpy
description: Helpy is a modern, open source helpdesk customer support application. Features include knowledgebase, community discussions and support tickets integrated with email.
repo_language: Ruby
owner: helpyio
file_changes: filename: user.rb
change_type: ModificationType.MODIFY
diff: @@ -204,6 +204,12 @@ class User < ActiveRecord::Base
     true
   end
 
+  # Is this user editable by the current logged in agent?
+  def can_be_edited? current_user
+    return true if current_user.is_admin?
+    !self.is_agent?
+  end
+
   def self.notifiable_on_public
     agents.where(notify_on_public: true).reorder('id asc')
   end

programming_language: Ruby
filename: _topic_options.html.erb
change_type: ModificationType.MODIFY
diff: @@ -11,7 +11,7 @@
       <li class="dropdown-header"><%= @topic.user.name %></li>
       <li><%= link_to t(:tickets, default: 'Tickets'), remote: true %></li>
       <li><%= link_to t(:open_new_discussion, default: 'Open Discussion'), new_admin_topic_path(user_id: @topic.user.id), remote: true, class: '' %></li>
-      <li><%= link_to t(:edit_user, default: 'Edit User'), edit_admin_user_path(@topic.user), remote: false %></li>
+      <li><%= link_to t(:edit_user, default: 'Edit User'), edit_admin_user_path(@topic.user), remote: false if @topic.user.can_be_edited? current_user %></li>
     </ul>
   </span>
 </div>

programming_language: CoffeeScript
filename: _user.html.erb
change_type: ModificationType.MODIFY
diff: @@ -24,7 +24,7 @@
       <ul class="dropdown-menu dropdown-menu-right" role="menu">
         <li><%= link_to t(:open_new_discussion, default: 'Open Discussion'), new_admin_topic_path(user_id: user.id), remote: true, class: '' %></li>
         <li><%= link_to t(:discussions, default: 'Discussions'), admin_user_path(user), remote: true %></li>
-        <li><%= link_to t(:edit_user, default: 'Edit User'), edit_admin_user_path(user, mode: 'edit'), remote: false %></li>
+        <li><%= link_to t(:edit_user, default: 'Edit User'), edit_admin_user_path(user, mode: 'edit'), remote: false if user.can_be_edited? current_user%></li>
       </ul>
     </div>
   </td>

programming_language: Ruby
filename: _user_context_menu.html.erb
change_type: ModificationType.MODIFY
diff: @@ -2,7 +2,7 @@
   <%= link_to content_tag(:small, '', class: 'fas fa-ellipsis-v ticket-ellipsis btn'), '#', class: 'dropdown-toggle', data: { toggle: 'dropdown' }%>
   <ul class="dropdown-menu dropdown-menu-right" role="menu">
     <li><%= link_to t(:open_new_discussion, default: 'Open Discussion'), new_admin_topic_path(user_id: user.id), remote: true, class: '' %></li>
-    <li><%= link_to t(:edit_user, default: 'Edit User'), edit_admin_user_path(user), remote: false %></li>
+    <li><%= link_to t(:edit_user, default: 'Edit User'), edit_admin_user_path(user), remote: false if user.can_be_edited? current_user %></li>
     <li><%= link_to t(:scrub_user, default: 'Anonymize User'), admin_scrub_user_path(user), remote: true,
                     method: :post,
                     data: { confirm: "Are you sure you want to anonymize #{user.name}? This will anonymize the user and retain all of their public discussions." } if current_user.is_admin? && user.can_scrub_and_delete? %></li>

programming_language: Ruby
filename: _user_info_with_tickets.html.erb
change_type: ModificationType.MODIFY
diff: @@ -11,45 +11,45 @@
 
       <div id="user-contact-info" class="user-info">
         <div class="tiny-header">
-          <%= best_in_place user, :name, as: :input, url: admin_user_path(user) %>
+          <%= best_in_place_if !user.is_agent?, user, :name, as: :input, url: admin_user_path(user) %>
         </div>
         <span class="small">
           <% if user.account_number.present? %>
             <%= t(:account_number, default: 'Account:') %>
-            <%= best_in_place user, :account_number, as: :input, url: admin_user_path(user),
+            <%= best_in_place_if !user.is_agent?, user, :account_number, as: :input, url: admin_user_path(user),
             place_holder: t('activerecord.attributes.user.account_number', default: 'Account Number') if user.account_number.present? %>
             <br/>
           <% end %>
-          <%= best_in_place user, :company, as: :input, url: admin_user_path(user),
+          <%= best_in_place_if !user.is_agent?, user, :company, as: :input, url: admin_user_path(user),
           place_holder: t('click_to_edit', what: t('activerecord.attributes.user.company'), default: 'click to edit %{what}') %><br/>
-          <%= best_in_place user, :email, as: :input, url: admin_user_path(user) %><br/>
+          <%= best_in_place_if !user.is_agent?, user, :email, as: :input, url: admin_user_path(user) %><br/>
 
         </span>
       </div>
       <div id="user-contact-info" class="user-info">
         <div class="tiny-header"><%= t(:contact_info, default: "Contact") %>:</div>
-        <%= best_in_place user, :street, as: :input, url: admin_user_path(user),
+        <%= best_in_place_if !user.is_agent?, user, :street, as: :input, url: admin_user_path(user),
         place_holder: t('click_to_edit', what: t('activerecord.attributes.user.street'), default: 'click to edit %{what}') %><br/>
-        <%= best_in_place user, :city, as: :input, url: admin_user_path(user),
+        <%= best_in_place_if !user.is_agent?, user, :city, as: :input, url: admin_user_path(user),
         place_holder: t('click_to_edit', what: t('activerecord.attributes.user.city'), default: 'click to edit %{what}') %>
         <br/>
-        <%= best_in_place user, :state, as: :input, url: admin_user_path(user),
+        <%= best_in_place_if !user.is_agent?, user, :state, as: :input, url: admin_user_path(user),
         place_holder: t('click_to_edit', what: t('activerecord.attributes.user.state'), default: 'click to edit %{what}') %>
-        <%= best_in_place user, :zip, as: :input, url: admin_user_path(user),
+        <%= best_in_place_if !user.is_agent?, user, :zip, as: :input, url: admin_user_path(user),
         place_holder: t('click_to_edit', what: t('activerecord.attributes.user.zip'), default: 'click to edit %{what}') %><br/>
       </div>
       <div id="user-contact-info" class="user-info">
         <div class="tiny-header"><%= t(:phone, default: "Phone") %>:</div>
-        <%= t(:work_phone, default: "work") %>: <%= best_in_place user, :work_phone,
+        <%= t(:work_phone, default: "work") %>: <%= best_in_place_if !user.is_agent?, user, :work_phone,
         as: :input, url: admin_user_path(user), place_holder: t('click_to_edit', what: '', default: 'click to edit %{what}') %><br/>
-        <%= t(:home_home, default: "home") %>: <%= best_in_place user, :home_phone,
+        <%= t(:home_home, default: "home") %>: <%= best_in_place_if !user.is_agent?, user, :home_phone,
         as: :input, url: admin_user_path(user), place_holder: t('click_to_edit', what: '', default: 'click to edit %{what}') %><br/>
-        <%= t(:cell_phone, default: "cell") %>: <%= best_in_place user, :cell_phone,
+        <%= t(:cell_phone, default: "cell") %>: <%= best_in_place_if !user.is_agent?, user, :cell_phone,
         as: :input, url: admin_user_path(user), place_holder: t('click_to_edit', what: '', default: 'click to edit %{what}') %><br/>
       </div>
       <div id="user-contact-info" class="user-info">
         <div class="tiny-header"><%= t(:notes, default: "Notes") %>:</div>
-        <%= best_in_place user, :notes, as: :textarea, url: admin_user_path(user),
+        <%= best_in_place_if !user.is_agent?, user, :notes, as: :textarea, url: admin_user_path(user),
         place_holder: t('click_to_edit', what: 'notes', default: 'click to edit %{what}'),
         confirm: true %><br/>
       </div>

programming_language: Ruby
filename: _admin_header.html.erb
change_type: ModificationType.MODIFY
diff: @@ -86,7 +86,7 @@ a.navbar-brand {
               <ul class="nav navbar-nav navbar-left" data-hook='admin-nav-left'>
                 <%= upper_nav_item(t(:inbox, default: "Inbox"), admin_topics_path,["topics"], ['index','show'], "fas fa-inbox") if (forums? || tickets?) && current_user.is_agent? %>
                 <%= upper_nav_item(t(:reports, default: 'Reports'), admin_reports_path(interval: 7), ["reports"], ['index', 'groups', 'team'], "fas fa-chart-bar") if tickets? && current_user.is_admin? %>
-                <%= upper_nav_item(t(:users, default: 'Customers'), admin_users_path(role: 'user'), ["users"], ["index","show","edit","update"], "fas fa-users") if current_user.is_admin? %>
+                <%= upper_nav_item(t(:users, default: 'Customers'), admin_users_path(role: 'user'), ["users"], ["index","show","edit","update"], "fas fa-users") if current_user.is_agent? %>
                 <%= helpcenter_menu if (knowledgebase? || forums?) && current_user.is_agent? %>
                 <%= content_tag(:li, link_to(t(:content, default: "Content"), admin_categories_path), class:'kblink') if knowledgebase? && current_user.role == 'editor' %>
                 <%#= content_tag(:li, link_to(t(:app_store, default: "App Store"), "http://helpy.io/store/"), class: "hidden-sm hidden-xs") if current_user.is_agent? %>

programming_language: HTML