code.gitea.io/gitea@v1.19.3/modules/upload/upload.go (about)

     1  // Copyright 2019 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package upload
     5  
     6  import (
     7  	"mime"
     8  	"net/http"
     9  	"net/url"
    10  	"path"
    11  	"regexp"
    12  	"strings"
    13  
    14  	"code.gitea.io/gitea/modules/context"
    15  	"code.gitea.io/gitea/modules/log"
    16  	"code.gitea.io/gitea/modules/setting"
    17  )
    18  
    19  // ErrFileTypeForbidden not allowed file type error
    20  type ErrFileTypeForbidden struct {
    21  	Type string
    22  }
    23  
    24  // IsErrFileTypeForbidden checks if an error is a ErrFileTypeForbidden.
    25  func IsErrFileTypeForbidden(err error) bool {
    26  	_, ok := err.(ErrFileTypeForbidden)
    27  	return ok
    28  }
    29  
    30  func (err ErrFileTypeForbidden) Error() string {
    31  	return "This file extension or type is not allowed to be uploaded."
    32  }
    33  
    34  var wildcardTypeRe = regexp.MustCompile(`^[a-z]+/\*$`)
    35  
    36  // Verify validates whether a file is allowed to be uploaded.
    37  func Verify(buf []byte, fileName, allowedTypesStr string) error {
    38  	allowedTypesStr = strings.ReplaceAll(allowedTypesStr, "|", ",") // compat for old config format
    39  
    40  	allowedTypes := []string{}
    41  	for _, entry := range strings.Split(allowedTypesStr, ",") {
    42  		entry = strings.ToLower(strings.TrimSpace(entry))
    43  		if entry != "" {
    44  			allowedTypes = append(allowedTypes, entry)
    45  		}
    46  	}
    47  
    48  	if len(allowedTypes) == 0 {
    49  		return nil // everything is allowed
    50  	}
    51  
    52  	fullMimeType := http.DetectContentType(buf)
    53  	mimeType, _, err := mime.ParseMediaType(fullMimeType)
    54  	if err != nil {
    55  		log.Warn("Detected attachment type could not be parsed %s", fullMimeType)
    56  		return ErrFileTypeForbidden{Type: fullMimeType}
    57  	}
    58  	extension := strings.ToLower(path.Ext(fileName))
    59  
    60  	// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#Unique_file_type_specifiers
    61  	for _, allowEntry := range allowedTypes {
    62  		if allowEntry == "*/*" {
    63  			return nil // everything allowed
    64  		} else if strings.HasPrefix(allowEntry, ".") && allowEntry == extension {
    65  			return nil // extension is allowed
    66  		} else if mimeType == allowEntry {
    67  			return nil // mime type is allowed
    68  		} else if wildcardTypeRe.MatchString(allowEntry) && strings.HasPrefix(mimeType, allowEntry[:len(allowEntry)-1]) {
    69  			return nil // wildcard match, e.g. image/*
    70  		}
    71  	}
    72  
    73  	log.Info("Attachment with type %s blocked from upload", fullMimeType)
    74  	return ErrFileTypeForbidden{Type: fullMimeType}
    75  }
    76  
    77  // AddUploadContext renders template values for dropzone
    78  func AddUploadContext(ctx *context.Context, uploadType string) {
    79  	if uploadType == "release" {
    80  		ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/releases/attachments"
    81  		ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/releases/attachments/remove"
    82  		ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/releases/attachments"
    83  		ctx.Data["UploadAccepts"] = strings.ReplaceAll(setting.Repository.Release.AllowedTypes, "|", ",")
    84  		ctx.Data["UploadMaxFiles"] = setting.Attachment.MaxFiles
    85  		ctx.Data["UploadMaxSize"] = setting.Attachment.MaxSize
    86  	} else if uploadType == "comment" {
    87  		ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/issues/attachments"
    88  		ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/issues/attachments/remove"
    89  		if len(ctx.Params(":index")) > 0 {
    90  			ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/issues/" + url.PathEscape(ctx.Params(":index")) + "/attachments"
    91  		} else {
    92  			ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/issues/attachments"
    93  		}
    94  		ctx.Data["UploadAccepts"] = strings.ReplaceAll(setting.Attachment.AllowedTypes, "|", ",")
    95  		ctx.Data["UploadMaxFiles"] = setting.Attachment.MaxFiles
    96  		ctx.Data["UploadMaxSize"] = setting.Attachment.MaxSize
    97  	} else if uploadType == "repo" {
    98  		ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/upload-file"
    99  		ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/upload-remove"
   100  		ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/upload-file"
   101  		ctx.Data["UploadAccepts"] = strings.ReplaceAll(setting.Repository.Upload.AllowedTypes, "|", ",")
   102  		ctx.Data["UploadMaxFiles"] = setting.Repository.Upload.MaxFiles
   103  		ctx.Data["UploadMaxSize"] = setting.Repository.Upload.FileMaxSize
   104  	}
   105  }