CVE-2013-7225 (Ruby, published 2014-01-02T14:59Z)
Description: value: Multiple SQL injection vulnerabilities in app/controllers/home_controller.rb in Fat Free CRM before 0.12.1 allow remote authenticated users to execute arbitrary SQL commands via (1) the homepage timeline feature or (2) the activity feature.
Severity: MEDIUM (CVSS v3 base score: )
Weakness: CWE-89 \u2014 Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.
Repository: https://github.com/fatfreecrm/fat_free_crm
Files changed:
app/controllers/home_controller.rb
spec/controllers/home_controller_spec.rb
Commit message: Fixed sql injection in timeline method.


Vulnerable code:
    unless params[:type].empty?
      model = params[:type].camelize.constantize
      item = model.find(params[:id])
      item.update_attribute(:state, params[:state])
    else
      comments, emails = params[:id].split("+")
      Comment.update_all("state = '#{params[:state]}'", "id IN (#{comments})") unless comments.blank?
      Email.update_all("state = '#{params[:state]}'", "id IN (#{emails})") unless emails.blank?
		it "should not display completed tasks" do
			task_1 = FactoryGirl.create(:task, :user_id => current_user.id, :name => "Your first task", :bucket => "due_asap", :assigned_to => current_user.id)
			task_2 = FactoryGirl.create(:task, :user_id => current_user.id, :name => "Completed task", :bucket => "due_asap", :completed_at => 1.days.ago, :completed_by => current_user.id, :assigned_to => current_user.id)
			get :index
			assigns[:my_tasks].should == [task_1]
		end
  
  
  
    
    
    
    

Fixed code:
    state = params[:state].to_s
    if %w(Collapsed Expanded).include?(state)
      if (model_type = params[:type].to_s).present?
        if %w(comment email).include?(model_type)
          model = model_type.camelize.constantize
          item = model.find(params[:id])
          item.update_attribute(:state, state)
        end
      else
        comments, emails = params[:id].split("+")
        Comment.where(:id => comments.split(',')).update_all(:state => state) unless comments.blank?
        Email.where(:id => emails.split(',')).update_all(:state => state) unless emails.blank?
      end
    it "should not display completed tasks" do
      task_1 = FactoryGirl.create(:task, :user_id => current_user.id, :name => "Your first task", :bucket => "due_asap", :assigned_to => current_user.id)
      task_2 = FactoryGirl.create(:task, :user_id => current_user.id, :name => "Completed task", :bucket => "due_asap", :completed_at => 1.days.ago, :completed_by => current_user.id, :assigned_to => current_user.id)
      get :index
      assigns[:my_tasks].should == [task_1]
    end







  end

  describe "timeline" do

    before(:each) do
      require_user
    end

    it "should collapse all comments and emails on a specific contact" do
      comment = double(Comment)
      Comment.should_receive(:find).with("1").and_return(comment)
      comment.should_receive(:update_attribute).with(:state, 'Collapsed')
      xhr :get, :timeline, :type => "comment", :id => "1", :state => "Collapsed"
    end

    it "should expand all comments and emails on a specific contact" do
      comment = double(Comment)
      Comment.should_receive(:find).with("1").and_return(comment)
      comment.should_receive(:update_attribute).with(:state, 'Expanded')
      xhr :get, :timeline, :type => "comment", :id => "1", :state => "Expanded"
    end

    it "should not do anything when state neither Expanded nor Collapsed" do
      comment = double(Comment)
      Comment.should_not_receive(:find).with("1")
      xhr :get, :timeline, :type => "comment", :id => "1", :state => "Explode"
    end

    it "should collapse all comments and emails on Contact" do
      where_stub = double
      where_stub.should_receive(:update_all).with(:state => "Collapsed")
      Comment.should_receive(:where).and_return(where_stub)
      xhr :get, :timeline, :id => "1,2,3,4+", :state => "Collapsed"
    end

    it "should not allow an arbitary state (sanitizes input)" do
      where_stub = double
      where_stub.should_receive(:update_all).with(:state => "Expanded")
      Comment.should_receive(:where).and_return(where_stub)
      xhr :get, :timeline, :id => "1,2,3,4+", :state => "Expanded"
    end

    it "should not update an arbitary model (sanitizes input)" do
      where_stub = double
      where_stub.should_receive(:update_all).with(:state => "Expanded")
      Comment.should_receive(:where).and_return(where_stub)
      xhr :get, :timeline, :id => "1,2,3,4+", :state => "Expanded"
    end