diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb index 8689a956e..7474b5653 100644 --- a/app/models/media_attachment.rb +++ b/app/models/media_attachment.rb @@ -57,7 +57,7 @@ class MediaAttachment < ApplicationRecord ).freeze IMAGE_MIME_TYPES = %w(image/jpeg image/png image/gif image/heic image/heif image/webp image/avif).freeze - IMAGE_CONVERTIBLE_MIME_TYPES = %w(image/heic image/heif).freeze + IMAGE_CONVERTIBLE_MIME_TYPES = %w(image/heic image/heif image/avif).freeze VIDEO_MIME_TYPES = %w(video/webm video/mp4 video/quicktime video/ogg).freeze VIDEO_CONVERTIBLE_MIME_TYPES = %w(video/webm video/quicktime).freeze AUDIO_MIME_TYPES = %w(audio/wave audio/wav audio/x-wav audio/x-pn-wave audio/vnd.wave audio/ogg audio/vorbis audio/mpeg audio/mp3 audio/webm audio/flac audio/aac audio/m4a audio/x-m4a audio/mp4 audio/3gpp video/x-ms-asf).freeze diff --git a/config/imagemagick/policy.xml b/config/imagemagick/policy.xml index 1052476b3..e2aa202f2 100644 --- a/config/imagemagick/policy.xml +++ b/config/imagemagick/policy.xml @@ -22,6 +22,6 @@ - + diff --git a/lib/paperclip/media_type_spoof_detector_extensions.rb b/lib/paperclip/media_type_spoof_detector_extensions.rb index a406ef312..c8868d5e9 100644 --- a/lib/paperclip/media_type_spoof_detector_extensions.rb +++ b/lib/paperclip/media_type_spoof_detector_extensions.rb @@ -2,13 +2,15 @@ module Paperclip module MediaTypeSpoofDetectorExtensions + MARCEL_MIME_TYPES = %w(audio/mpeg image/avif).freeze + 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 = type_from_marcel if @calculated_content_type == 'application/octet-stream' && type_from_marcel.in?(MARCEL_MIME_TYPES) @calculated_content_type end diff --git a/spec/fixtures/files/600x400.avif b/spec/fixtures/files/600x400.avif new file mode 100644 index 000000000..f306942db Binary files /dev/null and b/spec/fixtures/files/600x400.avif differ diff --git a/spec/fixtures/files/600x400.heic b/spec/fixtures/files/600x400.heic new file mode 100644 index 000000000..1add5832a Binary files /dev/null and b/spec/fixtures/files/600x400.heic differ diff --git a/spec/fixtures/files/600x400.jpeg b/spec/fixtures/files/600x400.jpeg new file mode 100644 index 000000000..1c20bea51 Binary files /dev/null and b/spec/fixtures/files/600x400.jpeg differ diff --git a/spec/fixtures/files/600x400.png b/spec/fixtures/files/600x400.png new file mode 100644 index 000000000..3b8a2dee3 Binary files /dev/null and b/spec/fixtures/files/600x400.png differ diff --git a/spec/fixtures/files/600x400.webp b/spec/fixtures/files/600x400.webp new file mode 100644 index 000000000..56fa5151c Binary files /dev/null and b/spec/fixtures/files/600x400.webp differ diff --git a/spec/models/media_attachment_spec.rb b/spec/models/media_attachment_spec.rb index 90e4f2f47..d142875aa 100644 --- a/spec/models/media_attachment_spec.rb +++ b/spec/models/media_attachment_spec.rb @@ -84,7 +84,87 @@ RSpec.describe MediaAttachment, paperclip_processing: true do end end - describe 'animated gif conversion' do + shared_examples 'static 600x400 image' do |content_type, extension| + after do + media.destroy + end + + it 'saves media attachment' do + expect(media.persisted?).to be true + expect(media.file).to_not be_nil + end + + it 'completes processing' do + expect(media.processing_complete?).to be true + end + + it 'sets type' do + expect(media.type).to eq 'image' + end + + it 'sets content type' do + expect(media.file_content_type).to eq content_type + end + + it 'sets file extension' do + expect(media.file_file_name).to end_with extension + end + + it 'strips original file name' do + expect(media.file_file_name).to_not start_with '600x400' + end + + it 'sets meta for original' do + expect(media.file.meta['original']['width']).to eq 600 + expect(media.file.meta['original']['height']).to eq 400 + expect(media.file.meta['original']['aspect']).to eq 1.5 + end + + it 'sets meta for thumbnail' do + expect(media.file.meta['small']['width']).to eq 588 + expect(media.file.meta['small']['height']).to eq 392 + expect(media.file.meta['small']['aspect']).to eq 1.5 + end + end + + describe 'jpeg' do + let(:media) { described_class.create(account: Fabricate(:account), file: attachment_fixture('600x400.jpeg')) } + + it_behaves_like 'static 600x400 image', 'image/jpeg', '.jpeg' + end + + describe 'png' do + let(:media) { described_class.create(account: Fabricate(:account), file: attachment_fixture('600x400.png')) } + + it_behaves_like 'static 600x400 image', 'image/png', '.png' + end + + describe 'webp' do + let(:media) { described_class.create(account: Fabricate(:account), file: attachment_fixture('600x400.webp')) } + + it_behaves_like 'static 600x400 image', 'image/webp', '.webp' + end + + describe 'avif' do + let(:media) { described_class.create(account: Fabricate(:account), file: attachment_fixture('600x400.avif')) } + + it_behaves_like 'static 600x400 image', 'image/jpeg', '.jpeg' + end + + describe 'heic' do + let(:media) { described_class.create(account: Fabricate(:account), file: attachment_fixture('600x400.heic')) } + + it_behaves_like 'static 600x400 image', 'image/jpeg', '.jpeg' + end + + describe 'base64-encoded image' do + let(:base64_attachment) { "data:image/jpeg;base64,#{Base64.encode64(attachment_fixture('600x400.jpeg').read)}" } + let(:media) { described_class.create(account: Fabricate(:account), file: base64_attachment) } + + it_behaves_like 'static 600x400 image', 'image/jpeg', '.jpeg' + end + + describe 'animated gif' do let(:media) { described_class.create(account: Fabricate(:account), file: attachment_fixture('avatar.gif')) } it 'sets type to gifv' do @@ -101,7 +181,7 @@ RSpec.describe MediaAttachment, paperclip_processing: true do end end - describe 'non-animated gif non-conversion' do + describe 'static gif' do fixtures = [ { filename: 'attachment.gif', width: 600, height: 400, aspect: 1.5 }, { filename: 'mini-static.gif', width: 32, height: 32, aspect: 1.0 }, @@ -172,37 +252,6 @@ RSpec.describe MediaAttachment, paperclip_processing: true do end end - describe 'jpeg' do - let(:media) { described_class.create(account: Fabricate(:account), file: attachment_fixture('attachment.jpg')) } - - it 'sets meta for different style' do - expect(media.file.meta['original']['width']).to eq 600 - expect(media.file.meta['original']['height']).to eq 400 - expect(media.file.meta['original']['aspect']).to eq 1.5 - expect(media.file.meta['small']['width']).to eq 588 - expect(media.file.meta['small']['height']).to eq 392 - expect(media.file.meta['small']['aspect']).to eq 1.5 - end - - it 'gives the file a random name' do - expect(media.file_file_name).to_not eq 'attachment.jpg' - end - end - - describe 'base64-encoded jpeg' do - let(:base64_attachment) { "data:image/jpeg;base64,#{Base64.encode64(attachment_fixture('attachment.jpg').read)}" } - let(:media) { described_class.create(account: Fabricate(:account), file: base64_attachment) } - - it 'saves media attachment' do - expect(media.persisted?).to be true - expect(media.file).to_not be_nil - end - - it 'gives the file a file name' do - expect(media.file_file_name).to_not be_blank - end - end - it 'is invalid without file' do media = described_class.new(account: Fabricate(:account)) expect(media.valid?).to be false