thoughtbot/paperclip

View on GitHub
lib/paperclip/media_type_spoof_detector.rb

Summary

Maintainability
A
0 mins
Test Coverage
module Paperclip
  class MediaTypeSpoofDetector
    def self.using(file, name, content_type)
      new(file, name, content_type)
    end

    def initialize(file, name, content_type)
      @file = file
      @name = name
      @content_type = content_type || ""
    end

    def spoofed?
      if has_name? && media_type_mismatch? && mapping_override_mismatch?
        Paperclip.log("Content Type Spoof: Filename #{File.basename(@name)} (#{supplied_content_type} from Headers, #{content_types_from_name.map(&:to_s)} from Extension), content type discovered from file command: #{calculated_content_type}. See documentation to allow this combination.")
        true
      else
        false
      end
    end

    private

    def has_name?
      @name.present?
    end

    def has_extension?
      File.extname(@name).present?
    end

    def media_type_mismatch?
      extension_type_mismatch? || calculated_type_mismatch?
    end

    def extension_type_mismatch?
      supplied_media_type.present? &&
        has_extension? &&
        !media_types_from_name.include?(supplied_media_type)
    end

    def calculated_type_mismatch?
      supplied_media_type.present? &&
        !calculated_content_type.include?(supplied_media_type)
    end

    def mapping_override_mismatch?
      !Array(mapped_content_type).include?(calculated_content_type)
    end


    def supplied_content_type
      @content_type
    end

    def supplied_media_type
      @content_type.split("/").first
    end

    def content_types_from_name
      @content_types_from_name ||= MIME::Types.type_for(@name)
    end

    def media_types_from_name
      @media_types_from_name ||= content_types_from_name.collect(&:media_type)
    end

    def calculated_content_type
      @calculated_content_type ||= type_from_file_command.chomp
    end

    def calculated_media_type
      @calculated_media_type ||= calculated_content_type.split("/").first
    end

    def type_from_file_command
      begin
        Paperclip.run("file", "-b --mime :file", file: @file.path).
          split(/[:;\s]+/).first
      rescue Terrapin::CommandLineError
        ""
      end
    end

    def mapped_content_type
      Paperclip.options[:content_type_mappings][filename_extension]
    end

    def filename_extension
      File.extname(@name.to_s.downcase).sub(/^\./, '').to_sym
    end
  end
end