2019-12-04 20:38:33 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2019-04-03 18:18:56 +05:30
|
|
|
require 'spec_helper'
|
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
RSpec.describe Gitlab::Sanitizers::Exif do
|
2019-04-03 18:18:56 +05:30
|
|
|
let(:sanitizer) { described_class.new }
|
2021-04-15 22:33:27 +05:30
|
|
|
let(:mime_type) { 'image/jpeg' }
|
|
|
|
|
|
|
|
before do
|
|
|
|
allow(Gitlab::Utils::MimeType).to receive(:from_string).and_return(mime_type)
|
|
|
|
end
|
2019-04-03 18:18:56 +05:30
|
|
|
|
|
|
|
describe '#batch_clean' do
|
|
|
|
context 'with image uploads' do
|
2020-04-08 14:13:33 +05:30
|
|
|
let_it_be(:upload1) { create(:upload, :with_file, :issuable_upload) }
|
|
|
|
let_it_be(:upload2) { create(:upload, :with_file, :personal_snippet_upload) }
|
|
|
|
let_it_be(:upload3) { create(:upload, :with_file, created_at: 3.days.ago) }
|
2019-04-03 18:18:56 +05:30
|
|
|
|
|
|
|
it 'processes all uploads if range ID is not set' do
|
|
|
|
expect(sanitizer).to receive(:clean).exactly(3).times
|
|
|
|
|
|
|
|
sanitizer.batch_clean
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'processes only uploads in the selected range' do
|
|
|
|
expect(sanitizer).to receive(:clean).once
|
|
|
|
|
2019-09-04 21:01:54 +05:30
|
|
|
sanitizer.batch_clean(start_id: upload1.id, stop_id: upload1.id)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'processes only uploads for the selected uploader' do
|
|
|
|
expect(sanitizer).to receive(:clean).once
|
|
|
|
|
|
|
|
sanitizer.batch_clean(uploader: 'PersonalFileUploader')
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'processes only uploads created since specified date' do
|
2020-03-13 15:44:24 +05:30
|
|
|
expect(sanitizer).to receive(:clean).twice
|
2019-09-04 21:01:54 +05:30
|
|
|
|
|
|
|
sanitizer.batch_clean(since: 2.days.ago)
|
2019-04-03 18:18:56 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it 'pauses if sleep_time is set' do
|
|
|
|
expect(sanitizer).to receive(:sleep).exactly(3).times.with(1.second)
|
|
|
|
expect(sanitizer).to receive(:clean).exactly(3).times
|
|
|
|
|
|
|
|
sanitizer.batch_clean(sleep_time: 1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-04-15 22:33:27 +05:30
|
|
|
it 'filters only jpg/tiff images by filename' do
|
2019-04-03 18:18:56 +05:30
|
|
|
create(:upload, path: 'filename.jpg')
|
|
|
|
create(:upload, path: 'filename.jpeg')
|
|
|
|
create(:upload, path: 'filename.JPG')
|
|
|
|
create(:upload, path: 'filename.tiff')
|
|
|
|
create(:upload, path: 'filename.TIFF')
|
|
|
|
create(:upload, path: 'filename.png')
|
|
|
|
create(:upload, path: 'filename.txt')
|
|
|
|
|
|
|
|
expect(sanitizer).to receive(:clean).exactly(5).times
|
2021-04-15 22:33:27 +05:30
|
|
|
|
2019-04-03 18:18:56 +05:30
|
|
|
sanitizer.batch_clean
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#clean' do
|
2019-12-21 20:55:43 +05:30
|
|
|
let(:uploader) { create(:upload, :with_file, :issuable_upload).retrieve_uploader }
|
2021-04-15 22:33:27 +05:30
|
|
|
let(:dry_run) { false }
|
|
|
|
|
|
|
|
subject { sanitizer.clean(uploader, dry_run: dry_run) }
|
2019-04-03 18:18:56 +05:30
|
|
|
|
|
|
|
context "no dry run" do
|
|
|
|
it "removes exif from the image" do
|
|
|
|
uploader.store!(fixture_file_upload('spec/fixtures/rails_sample.jpg'))
|
|
|
|
|
|
|
|
original_upload = uploader.upload
|
|
|
|
expected_args = ["exiftool", "-all=", "-tagsFromFile", "@", *Gitlab::Sanitizers::Exif::EXCLUDE_PARAMS, "--IPTC:all", "--XMP-iptcExt:all", kind_of(String)]
|
|
|
|
|
|
|
|
expect(sanitizer).to receive(:extra_tags).and_return(["", 0])
|
|
|
|
expect(sanitizer).to receive(:exec_remove_exif!).once.and_call_original
|
|
|
|
expect(uploader).to receive(:store!).and_call_original
|
|
|
|
expect(Gitlab::Popen).to receive(:popen).with(expected_args) do |args|
|
|
|
|
File.write("#{args.last}_original", "foo") if args.last.start_with?(Dir.tmpdir)
|
|
|
|
|
|
|
|
[expected_args, 0]
|
|
|
|
end
|
|
|
|
|
2021-04-15 22:33:27 +05:30
|
|
|
subject
|
2019-04-03 18:18:56 +05:30
|
|
|
|
|
|
|
expect(uploader.upload.id).not_to eq(original_upload.id)
|
|
|
|
expect(uploader.upload.path).to eq(original_upload.path)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "ignores image without exif" do
|
|
|
|
expected_args = ["exiftool", "-all", "-j", "-sort", "--IPTC:all", "--XMP-iptcExt:all", kind_of(String)]
|
|
|
|
|
|
|
|
expect(Gitlab::Popen).to receive(:popen).with(expected_args).and_return(["[{}]", 0])
|
|
|
|
expect(sanitizer).not_to receive(:exec_remove_exif!)
|
|
|
|
expect(uploader).not_to receive(:store!)
|
|
|
|
|
2021-04-15 22:33:27 +05:30
|
|
|
subject
|
2019-04-03 18:18:56 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it "raises an error if the exiftool fails with an error" do
|
|
|
|
expect(Gitlab::Popen).to receive(:popen).and_return(["error", 1])
|
|
|
|
|
2021-04-15 22:33:27 +05:30
|
|
|
expect { subject }.to raise_exception(RuntimeError, "failed to get exif tags: error")
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'for files that do not have the correct MIME type' do
|
|
|
|
let(:mime_type) { 'text/plain' }
|
|
|
|
|
|
|
|
it 'cleans only jpg/tiff images with the correct mime types' do
|
|
|
|
expect(sanitizer).not_to receive(:extra_tags)
|
|
|
|
|
2021-04-29 21:17:54 +05:30
|
|
|
expect { subject }.to raise_error(RuntimeError, %r{File type text/plain not supported})
|
2021-04-15 22:33:27 +05:30
|
|
|
end
|
2019-04-03 18:18:56 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "dry run" do
|
2021-04-15 22:33:27 +05:30
|
|
|
let(:dry_run) { true }
|
|
|
|
|
2019-04-03 18:18:56 +05:30
|
|
|
it "doesn't change the image" do
|
|
|
|
expect(sanitizer).to receive(:extra_tags).and_return({ 'foo' => 'bar' })
|
|
|
|
expect(sanitizer).not_to receive(:exec_remove_exif!)
|
|
|
|
expect(uploader).not_to receive(:store!)
|
|
|
|
|
2021-04-15 22:33:27 +05:30
|
|
|
subject
|
2019-04-03 18:18:56 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-05-07 20:08:51 +05:30
|
|
|
describe '#clean_existing_path' do
|
|
|
|
let(:dry_run) { false }
|
|
|
|
|
|
|
|
let(:tmp_file) { Tempfile.new("rails_sample.jpg") }
|
|
|
|
|
|
|
|
subject { sanitizer.clean_existing_path(tmp_file.path, dry_run: dry_run) }
|
|
|
|
|
|
|
|
context "no dry run" do
|
|
|
|
let(:file_content) { fixture_file_upload('spec/fixtures/rails_sample.jpg') }
|
|
|
|
|
|
|
|
before do
|
|
|
|
File.open(tmp_file.path, "w+b") { |f| f.write file_content }
|
|
|
|
end
|
|
|
|
|
|
|
|
it "removes exif from the image" do
|
|
|
|
expected_args = ["exiftool", "-all=", "-tagsFromFile", "@", *Gitlab::Sanitizers::Exif::EXCLUDE_PARAMS, "--IPTC:all", "--XMP-iptcExt:all", kind_of(String)]
|
|
|
|
|
|
|
|
expect(sanitizer).to receive(:extra_tags).and_return(["", 0])
|
|
|
|
expect(sanitizer).to receive(:exec_remove_exif!).once.and_call_original
|
|
|
|
expect(Gitlab::Popen).to receive(:popen).with(expected_args) do |args|
|
|
|
|
File.write("#{args.last}_original", "foo") if args.last.start_with?(Dir.tmpdir)
|
|
|
|
|
|
|
|
[expected_args, 0]
|
|
|
|
end
|
|
|
|
|
|
|
|
subject
|
|
|
|
end
|
|
|
|
|
|
|
|
it "ignores image without exif" do
|
|
|
|
expected_args = ["exiftool", "-all", "-j", "-sort", "--IPTC:all", "--XMP-iptcExt:all", kind_of(String)]
|
|
|
|
|
|
|
|
expect(Gitlab::Popen).to receive(:popen).with(expected_args).and_return(["[{}]", 0])
|
|
|
|
expect(sanitizer).not_to receive(:exec_remove_exif!)
|
|
|
|
|
|
|
|
subject
|
|
|
|
end
|
|
|
|
|
|
|
|
it "raises an error if the exiftool fails with an error" do
|
|
|
|
expect(Gitlab::Popen).to receive(:popen).and_return(["error", 1])
|
|
|
|
|
|
|
|
expect { subject }.to raise_exception(RuntimeError, "failed to get exif tags: error")
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'for files that do not have the correct MIME type from file' do
|
|
|
|
let(:mime_type) { 'text/plain' }
|
|
|
|
|
|
|
|
it 'cleans only jpg/tiff images with the correct mime types' do
|
|
|
|
expect(sanitizer).not_to receive(:extra_tags)
|
|
|
|
|
|
|
|
expect { subject }.to raise_error(RuntimeError, %r{File type text/plain not supported})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'skip_unallowed_types is false' do
|
|
|
|
context 'for files that do not have the correct MIME type from input content' do
|
|
|
|
let(:mime_type) { 'text/plain' }
|
|
|
|
|
|
|
|
it 'raises an error if not jpg/tiff images with the correct mime types' do
|
|
|
|
expect(sanitizer).not_to receive(:extra_tags)
|
|
|
|
|
|
|
|
expect do
|
|
|
|
sanitizer.clean_existing_path(tmp_file.path, content: file_content)
|
|
|
|
end.to raise_error(RuntimeError, %r{File type text/plain not supported})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'for files that do not have the correct MIME type from input content' do
|
|
|
|
let(:mime_type) { 'text/plain' }
|
|
|
|
|
|
|
|
it 'raises an error if not jpg/tiff images with the correct mime types' do
|
|
|
|
expect(sanitizer).not_to receive(:extra_tags)
|
|
|
|
|
|
|
|
expect do
|
|
|
|
sanitizer.clean_existing_path(tmp_file.path, content: file_content)
|
|
|
|
end.to raise_error(RuntimeError, %r{File type text/plain not supported})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'skip_unallowed_types is true' do
|
|
|
|
context 'for files that do not have the correct MIME type from input content' do
|
|
|
|
let(:mime_type) { 'text/plain' }
|
|
|
|
|
|
|
|
it 'cleans only jpg/tiff images with the correct mime types' do
|
|
|
|
expect(sanitizer).not_to receive(:extra_tags)
|
|
|
|
|
|
|
|
expect do
|
|
|
|
sanitizer.clean_existing_path(tmp_file.path, content: file_content, skip_unallowed_types: true)
|
|
|
|
end.not_to raise_error
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'for files that do not have the correct MIME type from input content' do
|
|
|
|
let(:mime_type) { 'text/plain' }
|
|
|
|
|
|
|
|
it 'cleans only jpg/tiff images with the correct mime types' do
|
|
|
|
expect(sanitizer).not_to receive(:extra_tags)
|
|
|
|
|
|
|
|
expect do
|
|
|
|
sanitizer.clean_existing_path(tmp_file.path, content: file_content, skip_unallowed_types: true)
|
|
|
|
end.not_to raise_error
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "dry run" do
|
|
|
|
let(:dry_run) { true }
|
|
|
|
|
|
|
|
it "doesn't change the image" do
|
|
|
|
expect(sanitizer).to receive(:extra_tags).and_return({ 'foo' => 'bar' })
|
|
|
|
expect(sanitizer).not_to receive(:exec_remove_exif!)
|
|
|
|
|
|
|
|
subject
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-04-03 18:18:56 +05:30
|
|
|
describe "#extra_tags" do
|
|
|
|
it "returns a list of keys for exif file" do
|
|
|
|
tags = '[{
|
|
|
|
"DigitalSourceType": "some source",
|
|
|
|
"ImageHeight": 654
|
|
|
|
}]'
|
|
|
|
|
|
|
|
expect(Gitlab::Popen).to receive(:popen).and_return([tags, 0])
|
|
|
|
|
2021-04-15 22:33:27 +05:30
|
|
|
expect(sanitizer.send(:extra_tags, 'filename')).not_to be_empty
|
2019-04-03 18:18:56 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it "returns an empty list for file with only whitelisted and ignored tags" do
|
|
|
|
tags = '[{
|
|
|
|
"ImageHeight": 654,
|
|
|
|
"Megapixels": 0.641
|
|
|
|
}]'
|
|
|
|
|
|
|
|
expect(Gitlab::Popen).to receive(:popen).and_return([tags, 0])
|
|
|
|
|
2021-04-15 22:33:27 +05:30
|
|
|
expect(sanitizer.send(:extra_tags, 'some file')).to be_empty
|
2019-04-03 18:18:56 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|