gitea源码

upload.go 4.3KB

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