Commit f65f0914 authored by GitLab Release Tools Bot's avatar GitLab Release Tools Bot

Merge remote-tracking branch 'dev/master'

parents 9bceafeb ca8d3929
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
## 13.10.3 (2021-04-13)
- No changes.
## 13.10.2 (2021-04-01) ## 13.10.2 (2021-04-01)
- No changes. - No changes.
...@@ -178,6 +182,10 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -178,6 +182,10 @@ Please view this file on the master branch, on stable branches it's out of date.
- Delete redirect files. !56169 - Delete redirect files. !56169
## 13.9.6 (2021-04-13)
- No changes.
## 13.9.5 (2021-03-31) ## 13.9.5 (2021-03-31)
### Security (1 change) ### Security (1 change)
...@@ -355,6 +363,10 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -355,6 +363,10 @@ Please view this file on the master branch, on stable branches it's out of date.
- Review UI text - repo push rules settings. !52797 - Review UI text - repo push rules settings. !52797
## 13.8.8 (2021-04-13)
- No changes.
## 13.8.7 (2021-03-31) ## 13.8.7 (2021-03-31)
### Security (1 change) ### Security (1 change)
......
...@@ -2,6 +2,15 @@ ...@@ -2,6 +2,15 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 13.10.3 (2021-04-13)
### Security (3 changes)
- Check image content type before running exiftool in workhorse.
- Clean only legitimate JPG and TIFF files.
- Update ruby-saml and rexml gems.
## 13.10.2 (2021-04-01) ## 13.10.2 (2021-04-01)
### Fixed (1 change) ### Fixed (1 change)
...@@ -562,6 +571,14 @@ entry. ...@@ -562,6 +571,14 @@ entry.
- Convert mattermost alert to pajamas. !56556 - Convert mattermost alert to pajamas. !56556
## 13.9.6 (2021-04-13)
### Security (2 changes)
- Clean only legitimate JPG and TIFF files.
- Update ruby-saml and rexml gems.
## 13.9.5 (2021-03-31) ## 13.9.5 (2021-03-31)
### Security (6 changes) ### Security (6 changes)
...@@ -1199,6 +1216,14 @@ entry. ...@@ -1199,6 +1216,14 @@ entry.
- Apply new GitLab UI for buttons in pipeline schedules. - Apply new GitLab UI for buttons in pipeline schedules.
## 13.8.8 (2021-04-13)
### Security (2 changes)
- Clean only legitimate JPG and TIFF files.
- Update ruby-saml and rexml gems.
## 13.8.7 (2021-03-31) ## 13.8.7 (2021-03-31)
### Security (5 changes) ### Security (5 changes)
......
...@@ -28,6 +28,8 @@ gem 'devise', '~> 4.7.2' ...@@ -28,6 +28,8 @@ gem 'devise', '~> 4.7.2'
gem 'bcrypt', '~> 3.1', '>= 3.1.14' gem 'bcrypt', '~> 3.1', '>= 3.1.14'
gem 'doorkeeper', '~> 5.5.0.rc2' gem 'doorkeeper', '~> 5.5.0.rc2'
gem 'doorkeeper-openid_connect', '~> 1.7.5' gem 'doorkeeper-openid_connect', '~> 1.7.5'
gem 'rexml', '~> 3.2.5'
gem 'ruby-saml', '~> 1.12.1'
gem 'omniauth', '~> 1.8' gem 'omniauth', '~> 1.8'
gem 'omniauth-auth0', '~> 2.0.0' gem 'omniauth-auth0', '~> 2.0.0'
gem 'omniauth-azure-activedirectory-v2', '~> 0.1' gem 'omniauth-azure-activedirectory-v2', '~> 0.1'
......
...@@ -1050,7 +1050,7 @@ GEM ...@@ -1050,7 +1050,7 @@ GEM
retriable (3.1.2) retriable (3.1.2)
reverse_markdown (1.4.0) reverse_markdown (1.4.0)
nokogiri nokogiri
rexml (3.2.4) rexml (3.2.5)
rinku (2.0.0) rinku (2.0.0)
rotp (6.2.0) rotp (6.2.0)
rouge (3.26.0) rouge (3.26.0)
...@@ -1125,8 +1125,9 @@ GEM ...@@ -1125,8 +1125,9 @@ GEM
mini_portile2 (~> 2.5.0) mini_portile2 (~> 2.5.0)
ruby-prof (1.3.1) ruby-prof (1.3.1)
ruby-progressbar (1.11.0) ruby-progressbar (1.11.0)
ruby-saml (1.7.2) ruby-saml (1.12.1)
nokogiri (>= 1.5.10) nokogiri (>= 1.10.5)
rexml
ruby-statistics (2.1.2) ruby-statistics (2.1.2)
ruby2_keywords (0.0.2) ruby2_keywords (0.0.2)
ruby_parser (3.15.0) ruby_parser (3.15.0)
...@@ -1561,6 +1562,7 @@ DEPENDENCIES ...@@ -1561,6 +1562,7 @@ DEPENDENCIES
request_store (~> 1.5) request_store (~> 1.5)
responders (~> 3.0) responders (~> 3.0)
retriable (~> 3.1.2) retriable (~> 3.1.2)
rexml (~> 3.2.5)
rouge (~> 3.26.0) rouge (~> 3.26.0)
rqrcode-rails3 (~> 0.1.7) rqrcode-rails3 (~> 0.1.7)
rspec-parameterized rspec-parameterized
...@@ -1572,6 +1574,7 @@ DEPENDENCIES ...@@ -1572,6 +1574,7 @@ DEPENDENCIES
ruby-magic (~> 0.4) ruby-magic (~> 0.4)
ruby-prof (~> 1.3.0) ruby-prof (~> 1.3.0)
ruby-progressbar (~> 1.10) ruby-progressbar (~> 1.10)
ruby-saml (~> 1.12.1)
ruby_parser (~> 3.15) ruby_parser (~> 3.15)
rubyzip (~> 2.0.0) rubyzip (~> 2.0.0)
rugged (~> 1.1) rugged (~> 1.1)
......
---
title: Allow to disable exiftool depending on env variable.
merge_request:
author:
type: security
...@@ -45,6 +45,7 @@ module Gitlab ...@@ -45,6 +45,7 @@ module Gitlab
ALLOWED_TAGS = WHITELISTED_TAGS + IGNORED_TAGS ALLOWED_TAGS = WHITELISTED_TAGS + IGNORED_TAGS
EXCLUDE_PARAMS = WHITELISTED_TAGS.map { |tag| "-#{tag}" } EXCLUDE_PARAMS = WHITELISTED_TAGS.map { |tag| "-#{tag}" }
ALLOWED_MIME_TYPES = %w(image/jpeg image/tiff).freeze
attr_reader :logger attr_reader :logger
...@@ -96,12 +97,12 @@ module Gitlab ...@@ -96,12 +97,12 @@ module Gitlab
end end
end end
private
def extra_tags(path) def extra_tags(path)
exif_tags(path).keys - ALLOWED_TAGS exif_tags(path).keys - ALLOWED_TAGS
end end
private
def remove_and_store(tmpdir, src_path, uploader) def remove_and_store(tmpdir, src_path, uploader)
exec_remove_exif!(src_path) exec_remove_exif!(src_path)
logger.info "#{upload_ref(uploader.upload)}: exif removed, storing" logger.info "#{upload_ref(uploader.upload)}: exif removed, storing"
...@@ -133,15 +134,26 @@ module Gitlab ...@@ -133,15 +134,26 @@ module Gitlab
# upload is stored into the file with the original name - this filename # upload is stored into the file with the original name - this filename
# is used by carrierwave when storing the file back to the storage # is used by carrierwave when storing the file back to the storage
filename = File.join(dir, uploader.filename) filename = File.join(dir, uploader.filename)
contents = uploader.read
check_for_allowed_types(contents)
File.open(filename, 'w') do |file| File.open(filename, 'w') do |file|
file.binmode file.binmode
file.write uploader.read file.write contents
end end
filename filename
end end
def check_for_allowed_types(contents)
mime_type = Gitlab::Utils::MimeType.from_string(contents)
unless ALLOWED_MIME_TYPES.include?(mime_type)
raise "File type #{mime_type} not supported. Only supports #{ALLOWED_MIME_TYPES.join(", ")}."
end
end
def upload_ref(upload) def upload_ref(upload)
"#{upload.id}:#{upload.path}" "#{upload.id}:#{upload.path}"
end end
......
...@@ -4,6 +4,11 @@ require 'spec_helper' ...@@ -4,6 +4,11 @@ require 'spec_helper'
RSpec.describe Gitlab::Sanitizers::Exif do RSpec.describe Gitlab::Sanitizers::Exif do
let(:sanitizer) { described_class.new } let(:sanitizer) { described_class.new }
let(:mime_type) { 'image/jpeg' }
before do
allow(Gitlab::Utils::MimeType).to receive(:from_string).and_return(mime_type)
end
describe '#batch_clean' do describe '#batch_clean' do
context 'with image uploads' do context 'with image uploads' do
...@@ -43,7 +48,7 @@ RSpec.describe Gitlab::Sanitizers::Exif do ...@@ -43,7 +48,7 @@ RSpec.describe Gitlab::Sanitizers::Exif do
end end
end end
it 'filters only jpg/tiff images' do it 'filters only jpg/tiff images by filename' do
create(:upload, path: 'filename.jpg') create(:upload, path: 'filename.jpg')
create(:upload, path: 'filename.jpeg') create(:upload, path: 'filename.jpeg')
create(:upload, path: 'filename.JPG') create(:upload, path: 'filename.JPG')
...@@ -53,12 +58,16 @@ RSpec.describe Gitlab::Sanitizers::Exif do ...@@ -53,12 +58,16 @@ RSpec.describe Gitlab::Sanitizers::Exif do
create(:upload, path: 'filename.txt') create(:upload, path: 'filename.txt')
expect(sanitizer).to receive(:clean).exactly(5).times expect(sanitizer).to receive(:clean).exactly(5).times
sanitizer.batch_clean sanitizer.batch_clean
end end
end end
describe '#clean' do describe '#clean' do
let(:uploader) { create(:upload, :with_file, :issuable_upload).retrieve_uploader } let(:uploader) { create(:upload, :with_file, :issuable_upload).retrieve_uploader }
let(:dry_run) { false }
subject { sanitizer.clean(uploader, dry_run: dry_run) }
context "no dry run" do context "no dry run" do
it "removes exif from the image" do it "removes exif from the image" do
...@@ -76,7 +85,7 @@ RSpec.describe Gitlab::Sanitizers::Exif do ...@@ -76,7 +85,7 @@ RSpec.describe Gitlab::Sanitizers::Exif do
[expected_args, 0] [expected_args, 0]
end end
sanitizer.clean(uploader, dry_run: false) subject
expect(uploader.upload.id).not_to eq(original_upload.id) expect(uploader.upload.id).not_to eq(original_upload.id)
expect(uploader.upload.path).to eq(original_upload.path) expect(uploader.upload.path).to eq(original_upload.path)
...@@ -89,23 +98,35 @@ RSpec.describe Gitlab::Sanitizers::Exif do ...@@ -89,23 +98,35 @@ RSpec.describe Gitlab::Sanitizers::Exif do
expect(sanitizer).not_to receive(:exec_remove_exif!) expect(sanitizer).not_to receive(:exec_remove_exif!)
expect(uploader).not_to receive(:store!) expect(uploader).not_to receive(:store!)
sanitizer.clean(uploader, dry_run: false) subject
end end
it "raises an error if the exiftool fails with an error" do it "raises an error if the exiftool fails with an error" do
expect(Gitlab::Popen).to receive(:popen).and_return(["error", 1]) expect(Gitlab::Popen).to receive(:popen).and_return(["error", 1])
expect { sanitizer.clean(uploader, dry_run: false) }.to raise_exception(RuntimeError, "failed to get exif tags: error") 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)
expect { subject }.to raise_error(RuntimeError, /File type text\/plain not supported/)
end
end end
end end
context "dry run" do context "dry run" do
let(:dry_run) { true }
it "doesn't change the image" do it "doesn't change the image" do
expect(sanitizer).to receive(:extra_tags).and_return({ 'foo' => 'bar' }) expect(sanitizer).to receive(:extra_tags).and_return({ 'foo' => 'bar' })
expect(sanitizer).not_to receive(:exec_remove_exif!) expect(sanitizer).not_to receive(:exec_remove_exif!)
expect(uploader).not_to receive(:store!) expect(uploader).not_to receive(:store!)
sanitizer.clean(uploader, dry_run: true) subject
end end
end end
end end
...@@ -119,7 +140,7 @@ RSpec.describe Gitlab::Sanitizers::Exif do ...@@ -119,7 +140,7 @@ RSpec.describe Gitlab::Sanitizers::Exif do
expect(Gitlab::Popen).to receive(:popen).and_return([tags, 0]) expect(Gitlab::Popen).to receive(:popen).and_return([tags, 0])
expect(sanitizer.extra_tags('filename')).not_to be_empty expect(sanitizer.send(:extra_tags, 'filename')).not_to be_empty
end end
it "returns an empty list for file with only whitelisted and ignored tags" do it "returns an empty list for file with only whitelisted and ignored tags" do
...@@ -130,7 +151,7 @@ RSpec.describe Gitlab::Sanitizers::Exif do ...@@ -130,7 +151,7 @@ RSpec.describe Gitlab::Sanitizers::Exif do
expect(Gitlab::Popen).to receive(:popen).and_return([tags, 0]) expect(Gitlab::Popen).to receive(:popen).and_return([tags, 0])
expect(sanitizer.extra_tags('some file')).to be_empty expect(sanitizer.send(:extra_tags, 'some file')).to be_empty
end end
end end
end end
...@@ -31,6 +31,7 @@ require ( ...@@ -31,6 +31,7 @@ require (
gitlab.com/gitlab-org/gitaly v1.74.0 gitlab.com/gitlab-org/gitaly v1.74.0
gitlab.com/gitlab-org/labkit v1.0.0 gitlab.com/gitlab-org/labkit v1.0.0
gocloud.dev v0.21.1-0.20201223184910-5094f54ed8bb gocloud.dev v0.21.1-0.20201223184910-5094f54ed8bb
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8
golang.org/x/lint v0.0.0-20200302205851-738671d3881b golang.org/x/lint v0.0.0-20200302205851-738671d3881b
golang.org/x/net v0.0.0-20201224014010-6772e930b67b golang.org/x/net v0.0.0-20201224014010-6772e930b67b
golang.org/x/text v0.3.5 // indirect golang.org/x/text v0.3.5 // indirect
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"os"
"os/exec" "os/exec"
"regexp" "regexp"
...@@ -22,6 +23,14 @@ type cleaner struct { ...@@ -22,6 +23,14 @@ type cleaner struct {
eof bool eof bool
} }
type FileType int
const (
TypeUnknown FileType = iota
TypeJPEG
TypeTIFF
)
func NewCleaner(ctx context.Context, stdin io.Reader) (io.ReadCloser, error) { func NewCleaner(ctx context.Context, stdin io.Reader) (io.ReadCloser, error) {
c := &cleaner{ctx: ctx} c := &cleaner{ctx: ctx}
...@@ -100,8 +109,20 @@ func (c *cleaner) startProcessing(stdin io.Reader) error { ...@@ -100,8 +109,20 @@ func (c *cleaner) startProcessing(stdin io.Reader) error {
return nil return nil
} }
func IsExifFile(filename string) bool { func FileTypeFromSuffix(filename string) FileType {
filenameMatch := regexp.MustCompile(`(?i)\.(jpg|jpeg|tiff)$`) if os.Getenv("SKIP_EXIFTOOL") == "1" {
return TypeUnknown
}
jpegMatch := regexp.MustCompile(`(?i)^[^\n]*\.(jpg|jpeg)$`)
if jpegMatch.MatchString(filename) {
return TypeJPEG
}
tiffMatch := regexp.MustCompile(`(?i)^[^\n]*\.tiff$`)
if tiffMatch.MatchString(filename) {
return TypeTIFF
}
return filenameMatch.MatchString(filename) return TypeUnknown
} }
...@@ -11,39 +11,57 @@ import ( ...@@ -11,39 +11,57 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestIsExifFile(t *testing.T) { func TestFileTypeFromSuffix(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
expected bool expected FileType
}{ }{
{ {
name: "/full/path.jpg", name: "/full/path.jpg",
expected: true, expected: TypeJPEG,
}, },
{ {
name: "path.jpeg", name: "path.jpeg",
expected: true, expected: TypeJPEG,
}, },
{ {
name: "path.tiff", name: "path.tiff",
expected: true, expected: TypeTIFF,
}, },
{ {
name: "path.JPG", name: "path.JPG",
expected: true, expected: TypeJPEG,
}, },
{ {
name: "path.tar", name: "path.tar",
expected: false, expected: TypeUnknown,
}, },
{ {
name: "path", name: "path",
expected: false, expected: TypeUnknown,
},
{
name: "something.jpg.py",
expected: TypeUnknown,
},
{
name: "something.py.jpg",
expected: TypeJPEG,
},
{
name: `something.jpg
.py`,
expected: TypeUnknown,
},
{
name: `something.something
.jpg`,
expected: TypeUnknown,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
require.Equal(t, test.expected, IsExifFile(test.name)) require.Equal(t, test.expected, FileTypeFromSuffix(test.name))
}) })
} }
} }
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"io/ioutil" "io/ioutil"
"mime/multipart" "mime/multipart"
"net/http" "net/http"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
...@@ -15,6 +16,8 @@ import ( ...@@ -15,6 +16,8 @@ import (
"github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promauto"
"gitlab.com/gitlab-org/labkit/log" "gitlab.com/gitlab-org/labkit/log"
"golang.org/x/image/tiff"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/api" "gitlab.com/gitlab-org/gitlab-workhorse/internal/api"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/filestore" "gitlab.com/gitlab-org/gitlab-workhorse/internal/filestore"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/lsif_transformer/parser" "gitlab.com/gitlab-org/gitlab-workhorse/internal/lsif_transformer/parser"
...@@ -127,9 +130,11 @@ func (rew *rewriter) handleFilePart(ctx context.Context, name string, p *multipa ...@@ -127,9 +130,11 @@ func (rew *rewriter) handleFilePart(ctx context.Context, name string, p *multipa
var inputReader io.ReadCloser var inputReader io.ReadCloser
var err error var err error
imageType := exif.FileTypeFromSuffix(filename)
switch { switch {
case exif.IsExifFile(filename): case imageType != exif.TypeUnknown:
inputReader, err = handleExifUpload(ctx, p, filename) inputReader, err = handleExifUpload(ctx, p, filename, imageType)
if err != nil { if err != nil {
return err return err
} }
...@@ -169,12 +174,48 @@ func (rew *rewriter) handleFilePart(ctx context.Context, name string, p *multipa ...@@ -169,12 +174,48 @@ func (rew *rewriter) handleFilePart(ctx context.Context, name string, p *multipa
return rew.filter.ProcessFile(ctx, name, fh, rew.writer) return rew.filter.ProcessFile(ctx, name, fh, rew.writer)
} }
func handleExifUpload(ctx context.Context, r io.Reader, filename string) (io.ReadCloser, error) { func handleExifUpload(ctx context.Context, r io.Reader, filename string, imageType exif.FileType) (io.ReadCloser, error) {
tmpfile, err := ioutil.TempFile("", "exifremove")
if err != nil {
return nil, err
}
go func() {
<-ctx.Done()
tmpfile.Close()
}()
if err := os.Remove(tmpfile.Name()); err != nil {
return nil, err
}
_, err = io.Copy(tmpfile, r)
if err != nil {
return nil, err
}
tmpfile.Seek(0, io.SeekStart)
isValidType := false
switch imageType {
case exif.TypeJPEG:
isValidType = isJPEG(tmpfile)
case exif.TypeTIFF:
isValidType = isTIFF(tmpfile)
}
tmpfile.Seek(0, io.SeekStart)
if !isValidType {
log.WithContextFields(ctx, log.Fields{
"filename": filename,
"imageType": imageType,
}).Print("invalid content type, not running exiftool")
return tmpfile, nil
}
log.WithContextFields(ctx, log.Fields{ log.WithContextFields(ctx, log.Fields{
"filename": filename, "filename": filename,
}).Print("running exiftool to remove any metadata") }).Print("running exiftool to remove any metadata")
cleaner, err := exif.NewCleaner(ctx, r) cleaner, err := exif.NewCleaner(ctx, tmpfile)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -182,6 +223,29 @@ func handleExifUpload(ctx context.Context, r io.Reader, filename string) (io.Rea ...@@ -182,6 +223,29 @@ func handleExifUpload(ctx context.Context, r io.Reader, filename string) (io.Rea
return cleaner, nil return cleaner, nil
} }
func isTIFF(r io.Reader) bool {
_, err := tiff.Decode(r)
if err == nil {
return true
}
if _, unsupported := err.(tiff.UnsupportedError); unsupported {
return true
}
return false
}
func isJPEG(r io.Reader) bool {
// Only the first 512 bytes are used to sniff the content type.
buf, err := ioutil.ReadAll(io.LimitReader(r, 512))
if err != nil {
return false
}
return http.DetectContentType(buf) == "image/jpeg"
}
func handleLsifUpload(ctx context.Context, reader io.Reader, tempPath, filename string, preauth *api.Response) (io.ReadCloser, error) { func handleLsifUpload(ctx context.Context, reader io.Reader, tempPath, filename string, preauth *api.Response) (io.ReadCloser, error) {
parserConfig := parser.Config{ parserConfig := parser.Config{
TempPath: tempPath, TempPath: tempPath,
......
package upload
import (
"os"
"testing"
"github.com/stretchr/testify/require"
)
func TestImageTypeRecongition(t *testing.T) {
tests := []struct {
filename string
isJPEG bool
isTIFF bool
}{
{
filename: "exif/testdata/sample_exif.jpg",
isJPEG: true,
isTIFF: false,
}, {
filename: "exif/testdata/sample_exif.tiff",
isJPEG: false,
isTIFF: true,
}, {
filename: "exif/testdata/sample_exif_corrupted.jpg",
isJPEG: true,
isTIFF: false,
}, {
filename: "exif/testdata/sample_exif_invalid.jpg",
isJPEG: false,
isTIFF: false,
},
}
for _, test := range tests {
t.Run(test.filename, func(t *testing.T) {
input, err := os.Open(test.filename)
require.NoError(t, err)
require.Equal(t, test.isJPEG, isJPEG(input))
require.Equal(t, test.isTIFF, isTIFF(input))
})
}
}
...@@ -366,26 +366,28 @@ func TestInvalidFileNames(t *testing.T) { ...@@ -366,26 +366,28 @@ func TestInvalidFileNames(t *testing.T) {
} }
func TestUploadHandlerRemovingExif(t *testing.T) { func TestUploadHandlerRemovingExif(t *testing.T) {
tempPath, err := ioutil.TempDir("", "uploads")
require.NoError(t, err)
defer os.RemoveAll(tempPath)
var buffer bytes.Buffer
content, err := ioutil.ReadFile("exif/testdata/sample_exif.jpg") content, err := ioutil.ReadFile("exif/testdata/sample_exif.jpg")
require.NoError(t, err) require.NoError(t, err)
writer := multipart.NewWriter(&buffer) runUploadTest(t, content, "sample_exif.jpg", 200, func(w http.ResponseWriter, r *http.Request) {
file, err := writer.CreateFormFile("file", "test.jpg") err := r.ParseMultipartForm(100000)
require.NoError(t, err) require.NoError(t, err)
_, err = file.Write(content) size, err := strconv.Atoi(r.FormValue("file.size"))
require.NoError(t, err) require.NoError(t, err)
require.True(t, size < len(content), "Expected the file to be smaller after removal of exif")
require.True(t, size > 0, "Expected to receive not empty file")
err = writer.Close() w.WriteHeader(200)
fmt.Fprint(w, "RESPONSE")
})
}
func TestUploadHandlerRemovingExifTiff(t *testing.T) {
content, err := ioutil.ReadFile("exif/testdata/sample_exif.tiff")
require.NoError(t, err) require.NoError(t, err)
ts := testhelper.TestServerWithHandler(regexp.MustCompile(`/url/path\z`), func(w http.ResponseWriter, r *http.Request) { runUploadTest(t, content, "sample_exif.tiff", 200, func(w http.ResponseWriter, r *http.Request) {
err := r.ParseMultipartForm(100000) err := r.ParseMultipartForm(100000)
require.NoError(t, err) require.NoError(t, err)
...@@ -397,30 +399,36 @@ func TestUploadHandlerRemovingExif(t *testing.T) { ...@@ -397,30 +399,36 @@ func TestUploadHandlerRemovingExif(t *testing.T) {
w.WriteHeader(200) w.WriteHeader(200)
fmt.Fprint(w, "RESPONSE") fmt.Fprint(w, "RESPONSE")
}) })
defer ts.Close() }
httpRequest, err := http.NewRequest("POST", ts.URL+"/url/path", &buffer) func TestUploadHandlerRemovingExifInvalidContentType(t *testing.T) {
content, err := ioutil.ReadFile("exif/testdata/sample_exif_invalid.jpg")
require.NoError(t, err) require.NoError(t, err)
ctx, cancel := context.WithCancel(context.Background()) runUploadTest(t, content, "sample_exif_invalid.jpg", 200, func(w http.ResponseWriter, r *http.Request) {
defer cancel() err := r.ParseMultipartForm(100000)
require.NoError(t, err)
httpRequest = httpRequest.WithContext(ctx) output, err := ioutil.ReadFile(r.FormValue("file.path"))
httpRequest.ContentLength = int64(buffer.Len()) require.NoError(t, err)
httpRequest.Header.Set("Content-Type", writer.FormDataContentType()) require.Equal(t, content, output, "Expected the file to be same as before")
response := httptest.NewRecorder()
handler := newProxy(ts.URL) w.WriteHeader(200)
apiResponse := &api.Response{TempPath: tempPath} fmt.Fprint(w, "RESPONSE")
preparer := &DefaultPreparer{} })
opts, _, err := preparer.Prepare(apiResponse) }
func TestUploadHandlerRemovingExifCorruptedFile(t *testing.T) {
content, err := ioutil.ReadFile("exif/testdata/sample_exif_corrupted.jpg")
require.NoError(t, err) require.NoError(t, err)
HandleFileUploads(response, httpRequest, handler, apiResponse, &testFormProcessor{}, opts) runUploadTest(t, content, "sample_exif_corrupted.jpg", 422, func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, 200, response.Code) err := r.ParseMultipartForm(100000)
require.Error(t, err)
})
} }
func TestUploadHandlerRemovingInvalidExif(t *testing.T) { func runUploadTest(t *testing.T, image []byte, filename string, httpCode int, tsHandler func(http.ResponseWriter, *http.Request)) {
tempPath, err := ioutil.TempDir("", "uploads") tempPath, err := ioutil.TempDir("", "uploads")
require.NoError(t, err) require.NoError(t, err)
defer os.RemoveAll(tempPath) defer os.RemoveAll(tempPath)
...@@ -428,17 +436,16 @@ func TestUploadHandlerRemovingInvalidExif(t *testing.T) { ...@@ -428,17 +436,16 @@ func TestUploadHandlerRemovingInvalidExif(t *testing.T) {
var buffer bytes.Buffer var buffer bytes.Buffer
writer := multipart.NewWriter(&buffer) writer := multipart.NewWriter(&buffer)
file, err := writer.CreateFormFile("file", "test.jpg") file, err := writer.CreateFormFile("file", filename)
require.NoError(t, err)
_, err = file.Write(image)
require.NoError(t, err) require.NoError(t, err)
fmt.Fprint(file, "this is not valid image data")
err = writer.Close() err = writer.Close()
require.NoError(t, err) require.NoError(t, err)
ts := testhelper.TestServerWithHandler(regexp.MustCompile(`/url/path\z`), func(w http.ResponseWriter, r *http.Request) { ts := testhelper.TestServerWithHandler(regexp.MustCompile(`/url/path\z`), tsHandler)
err := r.ParseMultipartForm(100000)
require.Error(t, err)
})
defer ts.Close() defer ts.Close()
httpRequest, err := http.NewRequest("POST", ts.URL+"/url/path", &buffer) httpRequest, err := http.NewRequest("POST", ts.URL+"/url/path", &buffer)
...@@ -459,7 +466,7 @@ func TestUploadHandlerRemovingInvalidExif(t *testing.T) { ...@@ -459,7 +466,7 @@ func TestUploadHandlerRemovingInvalidExif(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
HandleFileUploads(response, httpRequest, handler, apiResponse, &testFormProcessor{}, opts) HandleFileUploads(response, httpRequest, handler, apiResponse, &testFormProcessor{}, opts)
require.Equal(t, 422, response.Code) require.Equal(t, httpCode, response.Code)
} }
func newProxy(url string) *proxy.Proxy { func newProxy(url string) *proxy.Proxy {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment