diff --git a/app/api/controller/upload/controller.go b/app/api/controller/upload/controller.go index 8c670e983..10532e901 100644 --- a/app/api/controller/upload/controller.go +++ b/app/api/controller/upload/controller.go @@ -17,8 +17,9 @@ package upload import ( "bufio" "context" + "errors" "fmt" - "net/http" + "io" "strings" apiauth "github.com/harness/gitness/app/api/auth" @@ -29,15 +30,21 @@ import ( "github.com/harness/gitness/blob" "github.com/harness/gitness/types" "github.com/harness/gitness/types/enum" + + "github.com/gabriel-vasile/mimetype" ) const ( - imageContentType = "image" - MaxFileSize = 5 << 20 // 5 MB file limit set in Handler + MaxFileSize = 10 << 20 // 10 MB file limit set in Handler fileBucketPathFmt = "uploads/%d/%s" peekBytes = 512 ) +var supportedFileTypes = map[string]struct{}{ + "image": {}, + "video": {}, +} + type Controller struct { authorizer authz.Authorizer repoStore store.RepoStore @@ -76,23 +83,24 @@ func (c *Controller) getRepoCheckAccess(ctx context.Context, return repo, nil } -func (c *Controller) ensureTypeImgAndGetExtn(file *bufio.Reader) (string, error) { +func (c *Controller) getFileExtension(file *bufio.Reader) (string, error) { buf, err := file.Peek(peekBytes) - if err != nil { + if err != nil && !errors.Is(err, io.EOF) { return "", fmt.Errorf("failed to read file: %w", err) } - detectedContentType := http.DetectContentType(buf) - if !strings.HasPrefix(detectedContentType, imageContentType) { - return "", usererror.BadRequestf("only image files are supported, uploaded file is of type %s", detectedContentType) + // Example: mType.String() = image/png + // Splitting on "/" and taking the first element of the slice + // will give us the file type. + mType := mimetype.Detect(buf) + if _, ok := supportedFileTypes[strings.Split(mType.String(), "/")[0]]; !ok { + return "", + usererror.BadRequestf( + "only image and video files are supported, uploaded file is of type %s", + mType.String()) } - contentTypeSlice := strings.Split(detectedContentType, "/") - if len(contentTypeSlice) < 2 { - return "", fmt.Errorf("failed to parse content type %s", detectedContentType) - } - extn := contentTypeSlice[1] - return extn, nil + return mType.Extension(), nil } func getFileBucketPath(repoID int64, fileName string) string { diff --git a/app/api/controller/upload/upload.go b/app/api/controller/upload/upload.go index d5397df87..0a29456eb 100644 --- a/app/api/controller/upload/upload.go +++ b/app/api/controller/upload/upload.go @@ -33,7 +33,7 @@ type Result struct { } const ( - fileNameFmt = "%s.%s" + fileNameFmt = "%s%s" ) func (c *Controller) Upload(ctx context.Context, @@ -51,8 +51,8 @@ func (c *Controller) Upload(ctx context.Context, return nil, usererror.BadRequest("no file provided") } bufReader := bufio.NewReader(file) - // Check if the file is an image - extn, err := c.ensureTypeImgAndGetExtn(bufReader) + // Check if the file is an image or video + extn, err := c.getFileExtension(bufReader) if err != nil { return nil, fmt.Errorf("failed to determine file type: %w", err) } diff --git a/go.mod b/go.mod index 3593fa8af..04c1749c6 100644 --- a/go.mod +++ b/go.mod @@ -52,11 +52,11 @@ require ( github.com/swaggest/swgui v1.4.2 github.com/unrolled/secure v1.0.8 go.uber.org/multierr v1.8.0 - golang.org/x/crypto v0.13.0 + golang.org/x/crypto v0.14.0 golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a golang.org/x/oauth2 v0.10.0 golang.org/x/sync v0.3.0 - golang.org/x/term v0.12.0 + golang.org/x/term v0.13.0 golang.org/x/text v0.13.0 google.golang.org/api v0.132.0 gopkg.in/alecthomas/kingpin.v2 v2.2.6 @@ -129,6 +129,7 @@ require ( github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fullstorydev/grpcurl v1.8.1 // indirect github.com/fxamacker/cbor/v2 v2.4.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-sql-driver/mysql v1.6.0 // indirect github.com/goccy/go-json v0.9.7 // indirect @@ -282,8 +283,8 @@ require ( github.com/yuin/goldmark v1.4.13 // indirect go.uber.org/atomic v1.10.0 // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.15.0 // indirect - golang.org/x/sys v0.12.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect golang.org/x/tools v0.13.0 // indirect google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 // indirect gopkg.in/ini.v1 v1.66.4 // indirect diff --git a/go.sum b/go.sum index 2e922a9b4..6b600d584 100644 --- a/go.sum +++ b/go.sum @@ -490,6 +490,8 @@ github.com/fullstorydev/grpcurl v1.8.1 h1:Pp648wlTTg3OKySeqxM5pzh8XF6vLqrm8wRq66 github.com/fullstorydev/grpcurl v1.8.1/go.mod h1:3BWhvHZwNO7iLXaQlojdg5NA6SxUDePli4ecpK1N7gw= github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= @@ -1558,6 +1560,7 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1674,6 +1677,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1827,6 +1832,7 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1836,6 +1842,7 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=