M app/models/concerns/attachmentable.rb => app/models/concerns/attachmentable.rb +2 -3
@@ 22,15 22,14 @@ module Attachmentable
included do
def self.has_attached_file(name, options = {}) # rubocop:disable Naming/PredicateName
- options = { validate_media_type: false }.merge(options)
super(name, options)
- send(:"before_#{name}_post_process") do
+
+ send(:"before_#{name}_validate") do
attachment = send(name)
check_image_dimension(attachment)
set_file_content_type(attachment)
obfuscate_file_name(attachment)
set_file_extension(attachment)
- Paperclip::Validators::MediaTypeSpoofDetectionValidator.new(attributes: [name]).validate(self)
end
end
end
M config/application.rb => config/application.rb +1 -0
@@ 28,6 28,7 @@ require_relative '../lib/paperclip/url_generator_extensions'
require_relative '../lib/paperclip/attachment_extensions'
require_relative '../lib/paperclip/lazy_thumbnail'
require_relative '../lib/paperclip/gif_transcoder'
+require_relative '../lib/paperclip/media_type_spoof_detector_extensions'
require_relative '../lib/paperclip/transcoder'
require_relative '../lib/paperclip/type_corrector'
require_relative '../lib/paperclip/response_with_limit_adapter'
A config/imagemagick/policy.xml => config/imagemagick/policy.xml +27 -0
@@ 0,0 1,27 @@
+<policymap>
+ <!-- Set some basic system resource limits -->
+ <policy domain="resource" name="time" value="60" />
+
+ <policy domain="module" rights="none" pattern="URL" />
+
+ <policy domain="filter" rights="none" pattern="*" />
+
+ <!--
+ Ideally, we would restrict ImageMagick to only accessing its own
+ disk-backed pixel cache as well as Mastodon-created Tempfiles.
+
+ However, those paths depend on the operating system and environment
+ variables, so they can only be known at runtime.
+
+ Furthermore, those paths are not necessarily shared across Mastodon
+ processes, so even creating a policy.xml at runtime is impractical.
+
+ For the time being, only disable indirect reads.
+ -->
+ <policy domain="path" rights="none" pattern="@*" />
+
+ <!-- Disallow any coder by default, and only enable ones required by Mastodon -->
+ <policy domain="coder" rights="none" pattern="*" />
+ <policy domain="coder" rights="read | write" pattern="{PNG,JPEG,GIF,HEIC,WEBP}" />
+ <policy domain="coder" rights="write" pattern="{HISTOGRAM,RGB,INFO}" />
+</policymap>
M config/initializers/paperclip.rb => config/initializers/paperclip.rb +7 -0
@@ 153,3 153,10 @@ unless defined?(Seahorse)
end
end
end
+
+# Set our ImageMagick security policy, but allow admins to override it
+ENV['MAGICK_CONFIGURE_PATH'] = begin
+ imagemagick_config_paths = ENV.fetch('MAGICK_CONFIGURE_PATH', '').split(File::PATH_SEPARATOR)
+ imagemagick_config_paths << Rails.root.join('config', 'imagemagick').expand_path.to_s
+ imagemagick_config_paths.join(File::PATH_SEPARATOR)
+end
A lib/paperclip/media_type_spoof_detector_extensions.rb => lib/paperclip/media_type_spoof_detector_extensions.rb +22 -0
@@ 0,0 1,22 @@
+# frozen_string_literal: true
+
+module Paperclip
+ module MediaTypeSpoofDetectorExtensions
+ def calculated_content_type
+ return @calculated_content_type if defined?(@calculated_content_type)
+
+ @calculated_content_type = type_from_file_command.chomp
+
+ # The `file` command fails to recognize some MP3 files as such
+ @calculated_content_type = type_from_marcel if @calculated_content_type == 'application/octet-stream' && type_from_marcel == 'audio/mpeg'
+ @calculated_content_type
+ end
+
+ def type_from_marcel
+ @type_from_marcel ||= Marcel::MimeType.for Pathname.new(@file.path),
+ name: @file.path
+ end
+ end
+end
+
+Paperclip::MediaTypeSpoofDetector.prepend(Paperclip::MediaTypeSpoofDetectorExtensions)
M lib/paperclip/transcoder.rb => lib/paperclip/transcoder.rb +1 -4
@@ 19,10 19,7 @@ module Paperclip
def make
metadata = VideoMetadataExtractor.new(@file.path)
- unless metadata.valid?
- Paperclip.log("Unsupported file #{@file.path}")
- return File.open(@file.path)
- end
+ raise Paperclip::Error, "Error while transcoding #{@file.path}: unsupported file" unless metadata.valid?
update_attachment_type(metadata)
update_options_from_metadata(metadata)
A spec/fixtures/files/boop.mp3 => spec/fixtures/files/boop.mp3 +0 -0
M spec/models/media_attachment_spec.rb => spec/models/media_attachment_spec.rb +20 -0
@@ 152,6 152,26 @@ RSpec.describe MediaAttachment, paperclip_processing: true do
end
end
+ describe 'mp3 with large cover art' do
+ let(:media) { described_class.create(account: Fabricate(:account), file: attachment_fixture('boop.mp3')) }
+
+ it 'detects it as an audio file' do
+ expect(media.type).to eq 'audio'
+ end
+
+ it 'sets meta for the duration' do
+ expect(media.file.meta['original']['duration']).to be_within(0.05).of(0.235102)
+ end
+
+ it 'extracts thumbnail' do
+ expect(media.thumbnail.present?).to be true
+ end
+
+ it 'gives the file a random name' do
+ expect(media.file_file_name).to_not eq 'boop.mp3'
+ end
+ end
+
describe 'jpeg' do
let(:media) { described_class.create(account: Fabricate(:account), file: attachment_fixture('attachment.jpg')) }