2016-02-12 23:52:43 +08:00
|
|
|
|
|
|
|
if ENV['PAPERCLIP_HASH_SECRET'].nil?
|
|
|
|
puts "WARNING! Environment variable PAPERCLIP_HASH_SECRET must be set."
|
|
|
|
exit 1
|
|
|
|
end
|
|
|
|
|
2017-10-10 19:32:14 +08:00
|
|
|
Paperclip::Attachment.default_options.merge!(
|
2016-02-12 23:52:43 +08:00
|
|
|
hash_data: ':class/:attachment/:id/:style',
|
|
|
|
hash_secret: ENV['PAPERCLIP_HASH_SECRET'],
|
2017-10-10 19:32:14 +08:00
|
|
|
preserve_files: true,
|
2016-02-12 23:52:43 +08:00
|
|
|
url: '/system/:class/:attachment/:id_partition/:hash/:style/:filename'
|
2017-10-10 19:32:14 +08:00
|
|
|
)
|
2016-02-12 23:52:43 +08:00
|
|
|
|
|
|
|
if ENV['PAPERCLIP_STORAGE'] == "s3"
|
|
|
|
|
|
|
|
if ENV['S3_BUCKET'].nil? or ENV['AWS_REGION'].nil? or
|
|
|
|
ENV['AWS_ACCESS_KEY_ID'].nil? or ENV['AWS_SECRET_ACCESS_KEY'].nil?
|
|
|
|
puts "WARNING! Environment variables S3_BUCKET, AWS_REGION, " +
|
|
|
|
"AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be set."
|
|
|
|
exit 1
|
|
|
|
end
|
|
|
|
Paperclip::Attachment.default_options.merge!({
|
|
|
|
url: ':s3_domain_url',
|
|
|
|
path: '/:class/:attachment/:id_partition/:hash/:style/:filename',
|
|
|
|
storage: :s3,
|
|
|
|
s3_host_name: "s3.#{ENV['AWS_REGION']}.amazonaws.com",
|
|
|
|
s3_protocol: 'https',
|
|
|
|
s3_credentials: {
|
|
|
|
bucket: ENV['S3_BUCKET'],
|
|
|
|
access_key_id: ENV['AWS_ACCESS_KEY_ID'],
|
|
|
|
secret_access_key: ENV['AWS_SECRET_ACCESS_KEY']
|
|
|
|
},
|
|
|
|
s3_permissions: {
|
2016-08-17 21:51:04 +08:00
|
|
|
original: :private,
|
|
|
|
medium: :private
|
2016-02-12 23:52:43 +08:00
|
|
|
},
|
|
|
|
s3_storage_class: {
|
|
|
|
medium: :reduced_redundancy,
|
|
|
|
thumb: :reduced_redundancy,
|
|
|
|
icon: :reduced_redundancy,
|
|
|
|
icon_small: :reduced_redundancy
|
|
|
|
}
|
|
|
|
})
|
2016-07-21 19:11:15 +08:00
|
|
|
elsif ENV['PAPERCLIP_STORAGE'] == "filesystem"
|
|
|
|
Paperclip::Attachment.default_options.merge!({
|
|
|
|
storage: :filesystem
|
|
|
|
})
|
|
|
|
|
2016-02-12 23:52:43 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
Paperclip::Attachment.class_eval do
|
|
|
|
def is_stored_on_s3?
|
|
|
|
options[:storage].to_sym == :s3
|
|
|
|
end
|
|
|
|
|
|
|
|
def fetch
|
|
|
|
Paperclip.io_adapters.for self
|
|
|
|
end
|
|
|
|
end
|
2016-08-25 13:49:51 +08:00
|
|
|
|
|
|
|
module Paperclip
|
|
|
|
# Checks file for spoofing
|
|
|
|
class MediaTypeSpoofDetector
|
|
|
|
def spoofed?
|
|
|
|
if has_name? && has_extension? && (media_type_mismatch? ||
|
|
|
|
mapping_override_mismatch?)
|
|
|
|
Paperclip.log("Content Type Spoof: Filename #{File.basename(@name)} "\
|
|
|
|
"(#{supplied_content_type} from Headers, #{content_types_from_name} "\
|
2016-08-26 17:13:40 +08:00
|
|
|
"from Extension), content type discovered: "\
|
2016-08-25 13:49:51 +08:00
|
|
|
"#{calculated_content_type}. See documentation to allow this "\
|
2016-08-26 17:13:40 +08:00
|
|
|
"combination.")
|
2016-08-25 13:49:51 +08:00
|
|
|
true
|
|
|
|
else
|
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
# Determine file content type from its name
|
|
|
|
def content_types_from_name
|
|
|
|
@content_types_from_name ||=
|
2017-07-14 22:19:03 +08:00
|
|
|
Paperclip.run('mimetype', '-b -- :file_name', file_name: @name).chomp
|
2016-08-25 13:49:51 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
# Determine file media type from its name
|
|
|
|
def media_types_from_name
|
|
|
|
@media_types_from_name ||= extract_media_type content_types_from_name
|
|
|
|
end
|
|
|
|
|
|
|
|
# Determine file content type from mimetype command
|
|
|
|
def type_from_mimetype_command
|
|
|
|
@type_from_mimetype_command ||=
|
2017-07-14 22:19:03 +08:00
|
|
|
Paperclip.run('mimetype', '-b -- :file', file: @file.path).chomp
|
2016-08-25 13:49:51 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
# Determine file media type from mimetype command
|
|
|
|
def media_type_from_mimetype_command
|
|
|
|
@media_type_from_mimetype_command ||=
|
|
|
|
extract_media_type type_from_mimetype_command
|
|
|
|
end
|
|
|
|
|
|
|
|
# Determine file content type from it's content (file and mimetype command)
|
|
|
|
def type_from_file_command
|
|
|
|
unless defined? @type_from_file_command
|
|
|
|
@type_from_file_command =
|
2017-07-14 22:19:03 +08:00
|
|
|
Paperclip.run('file', '-b --mime -- :file', file: @file.path)
|
2016-08-25 13:49:51 +08:00
|
|
|
.split(/[:;]\s+/).first
|
|
|
|
|
|
|
|
if allowed_spoof_exception?(@type_from_file_command,
|
|
|
|
media_type_from_file_command) ||
|
|
|
|
(@type_from_file_command.in?(%w(text/plain text/html)) &&
|
|
|
|
media_type_from_mimetype_command.in?(%w(text application)))
|
|
|
|
# File content type is generalized, so rely on file extension for
|
|
|
|
# correct/more specific content type
|
|
|
|
@type_from_file_command = type_from_mimetype_command
|
|
|
|
end
|
|
|
|
end
|
|
|
|
@type_from_file_command
|
|
|
|
rescue Cocaine::CommandLineError
|
|
|
|
''
|
|
|
|
end
|
|
|
|
|
|
|
|
# Determine file media type from it's content (file and mimetype command)
|
|
|
|
def media_type_from_file_command
|
|
|
|
@media_type_from_file_command ||=
|
|
|
|
extract_media_type type_from_file_command
|
|
|
|
end
|
|
|
|
|
|
|
|
def extract_media_type(content_type)
|
|
|
|
if content_type.empty?
|
|
|
|
''
|
|
|
|
else
|
|
|
|
content_type.split('/').first
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-09-07 01:57:24 +08:00
|
|
|
def media_type_mismatch?
|
|
|
|
calculated_type_mismatch?
|
|
|
|
end
|
|
|
|
|
2016-08-25 13:49:51 +08:00
|
|
|
# Checks file media type mismatch between file's name and header
|
2016-09-07 01:57:24 +08:00
|
|
|
# NOTE: Can't rely on headers, as different OS can have different file type
|
|
|
|
# MIME mappings
|
2016-08-25 13:49:51 +08:00
|
|
|
def supplied_type_mismatch?
|
|
|
|
!allowed_spoof_exception?(supplied_content_type, supplied_media_type) &&
|
|
|
|
media_types_from_name != supplied_media_type
|
|
|
|
end
|
|
|
|
|
|
|
|
# Checks file media type mismatch between file's name and content
|
|
|
|
def calculated_type_mismatch?
|
|
|
|
!allowed_spoof_exception?(calculated_content_type,
|
|
|
|
calculated_media_type) &&
|
|
|
|
media_types_from_name != calculated_media_type
|
|
|
|
end
|
|
|
|
|
|
|
|
# Checks file content type mismatch between file's name and content
|
|
|
|
def mapping_override_mismatch?
|
|
|
|
!allowed_spoof_exception?(calculated_content_type,
|
|
|
|
calculated_media_type) &&
|
|
|
|
content_types_from_name != calculated_content_type
|
|
|
|
end
|
|
|
|
|
|
|
|
# Check if we have a file spoof exception which is allowed/safe
|
|
|
|
def allowed_spoof_exception?(content_type, media_type)
|
|
|
|
content_type == 'application/octet-stream' ||
|
|
|
|
(content_type == 'inode/x-empty' && @file.size.zero?) ||
|
|
|
|
(content_type == 'text/x-c' &&
|
|
|
|
content_types_from_name == 'text/x-java') ||
|
|
|
|
(media_type.in?(%w(image audio video)) &&
|
|
|
|
media_type == media_types_from_name) ||
|
|
|
|
(content_types_from_name.in? %W(#{}
|
|
|
|
text/plain
|
2016-09-07 01:57:24 +08:00
|
|
|
application/octet-stream)) ||
|
|
|
|
# Types taken from: http://filext.com/faq/office_mime_types.php and
|
|
|
|
# https://www.openoffice.org/framework/documentation/mimetypes/mimetypes.html
|
|
|
|
#
|
2017-03-06 22:01:32 +08:00
|
|
|
# Generic application
|
|
|
|
(Set[content_type, content_types_from_name].subset? Set.new %w(
|
|
|
|
text/rtf
|
|
|
|
application/rtf
|
|
|
|
)) ||
|
2016-09-07 01:57:24 +08:00
|
|
|
# Word processor application
|
|
|
|
(Set[content_type, content_types_from_name].subset? Set.new %w(
|
2018-01-11 22:17:30 +08:00
|
|
|
application/zip
|
2016-09-07 19:04:12 +08:00
|
|
|
application/vnd.ms-office
|
2016-09-07 01:57:24 +08:00
|
|
|
application/msword
|
2017-03-06 22:01:32 +08:00
|
|
|
application/msword-template
|
2016-09-07 01:57:24 +08:00
|
|
|
application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
|
|
|
application/vnd.openxmlformats-officedocument.wordprocessingml.template
|
|
|
|
application/vnd.ms-word.document.macroEnabled.12
|
|
|
|
application/vnd.ms-word.template.macroEnabled.12
|
|
|
|
application/vnd.oasis.opendocument.text
|
|
|
|
application/vnd.oasis.opendocument.text-template
|
|
|
|
application/vnd.oasis.opendocument.text-web
|
|
|
|
application/vnd.oasis.opendocument.text-master
|
|
|
|
application/vnd.sun.xml.writer
|
|
|
|
application/vnd.sun.xml.writer.template
|
|
|
|
application/vnd.sun.xml.writer.global
|
|
|
|
application/vnd.stardivision.writer
|
|
|
|
application/vnd.stardivision.writer-global
|
|
|
|
application/x-starwriter
|
|
|
|
)) ||
|
|
|
|
# Spreadsheet application
|
|
|
|
(Set[content_type, content_types_from_name].subset? Set.new %w(
|
2017-04-04 16:51:54 +08:00
|
|
|
application/zip
|
2016-09-07 19:04:12 +08:00
|
|
|
application/vnd.ms-office
|
2016-09-07 01:57:24 +08:00
|
|
|
application/vnd.ms-excel
|
|
|
|
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
|
|
|
application/vnd.openxmlformats-officedocument.spreadsheetml.template
|
|
|
|
application/vnd.ms-excel.sheet.macroEnabled.12
|
|
|
|
application/vnd.ms-excel.template.macroEnabled.12
|
|
|
|
application/vnd.ms-excel.addin.macroEnabled.12
|
|
|
|
application/vnd.ms-excel.sheet.binary.macroEnabled.12
|
|
|
|
application/vnd.oasis.opendocument.spreadsheet
|
|
|
|
application/vnd.oasis.opendocument.spreadsheet-template
|
|
|
|
application/vnd.sun.xml.calc
|
|
|
|
application/vnd.sun.xml.calc.template
|
|
|
|
application/vnd.stardivision.calc
|
|
|
|
application/x-starcalc
|
2017-11-21 20:48:06 +08:00
|
|
|
application/CDFV2-encrypted
|
2016-09-07 01:57:24 +08:00
|
|
|
)) ||
|
|
|
|
# Presentation application
|
|
|
|
(Set[content_type, content_types_from_name].subset? Set.new %w(
|
2018-01-11 22:17:30 +08:00
|
|
|
application/zip
|
2016-09-07 19:04:12 +08:00
|
|
|
application/vnd.ms-office
|
2016-09-07 01:57:24 +08:00
|
|
|
application/vnd.ms-powerpoint
|
|
|
|
application/vnd.openxmlformats-officedocument.presentationml.presentation
|
|
|
|
application/vnd.openxmlformats-officedocument.presentationml.template
|
|
|
|
application/vnd.openxmlformats-officedocument.presentationml.slideshow
|
|
|
|
application/vnd.ms-powerpoint.addin.macroEnabled.12
|
|
|
|
application/vnd.ms-powerpoint.presentation.macroEnabled.12
|
|
|
|
application/vnd.ms-powerpoint.template.macroEnabled.12
|
|
|
|
application/vnd.ms-powerpoint.slideshow.macroEnabled.12
|
|
|
|
application/vnd.oasis.opendocument.presentation
|
|
|
|
application/vnd.oasis.opendocument.presentation-template
|
|
|
|
application/vnd.sun.xml.impress
|
|
|
|
application/vnd.sun.xml.impress.template
|
|
|
|
application/vnd.stardivision.impress
|
|
|
|
application/vnd.stardivision.impress-packed
|
|
|
|
application/x-starimpress
|
2017-03-06 22:01:32 +08:00
|
|
|
text/x-gettext-translation-template
|
2016-09-07 01:57:24 +08:00
|
|
|
)) ||
|
|
|
|
# Graphics application
|
|
|
|
(Set[content_type, content_types_from_name].subset? Set.new %w(
|
2016-09-07 19:04:12 +08:00
|
|
|
application/vnd.ms-office
|
2016-09-07 01:57:24 +08:00
|
|
|
application/vnd.oasis.opendocument.graphics
|
|
|
|
application/vnd.oasis.opendocument.graphics-template
|
|
|
|
application/vnd.sun.xml.draw
|
|
|
|
application/vnd.sun.xml.draw.template
|
|
|
|
application/vnd.stardivision.draw
|
|
|
|
application/x-stardraw
|
|
|
|
)) ||
|
|
|
|
# Formula application
|
|
|
|
(Set[content_type, content_types_from_name].subset? Set.new %w(
|
2016-09-07 19:04:12 +08:00
|
|
|
application/vnd.ms-office
|
2016-09-07 01:57:24 +08:00
|
|
|
application/vnd.oasis.opendocument.formula
|
|
|
|
application/vnd.sun.xml.math
|
|
|
|
application/vnd.stardivision.math
|
|
|
|
application/x-starmath
|
|
|
|
)) ||
|
|
|
|
# Chart application
|
|
|
|
(Set[content_type, content_types_from_name].subset? Set.new %w(
|
2016-09-07 19:04:12 +08:00
|
|
|
application/vnd.ms-office
|
2016-09-07 01:57:24 +08:00
|
|
|
application/vnd.oasis.opendocument.chart
|
|
|
|
application/vnd.stardivision.chart
|
|
|
|
application/x-starchart
|
|
|
|
))
|
2016-08-25 13:49:51 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|