Attachments: Add extension support, allow all types for releases (#12465)

* Attachments: Add extension support, allow all types for releases

- Add support for file extensions, matching the `accept` attribute of `<input type="file">`
- Add support for type wildcard mime types, e.g. `image/*`
- Create repository.release.ALLOWED_TYPES setting (default unrestricted)
- Change default for attachment.ALLOWED_TYPES to a list of extensions
- Split out POST /attachments into two endpoints for issue/pr and
  releases to prevent circumvention of allowed types check

Fixes: https://github.com/go-gitea/gitea/pull/10172
Fixes: https://github.com/go-gitea/gitea/issues/7266
Fixes: https://github.com/go-gitea/gitea/pull/12460
Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#Unique_file_type_specifiers

* rename function

* extract GET routes out of RepoMustNotBeArchived

Co-authored-by: Lauris BH <lauris@nix.lv>
This commit is contained in:
silverwind 2020-10-05 07:49:33 +02:00 committed by GitHub
parent 67a5573310
commit cda44750cb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 497 additions and 226 deletions

View file

@ -6,7 +6,6 @@ package repo
import (
"net/http"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
@ -182,7 +181,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) {
}
// Check if the filetype is allowed by the settings
err = upload.VerifyAllowedContentType(buf, strings.Split(setting.Attachment.AllowedTypes, ","))
err = upload.Verify(buf, header.Filename, setting.Repository.Release.AllowedTypes)
if err != nil {
ctx.Error(http.StatusBadRequest, "DetectContentType", err)
return

View file

@ -7,7 +7,6 @@ package repo
import (
"fmt"
"net/http"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
@ -17,16 +16,18 @@ import (
"code.gitea.io/gitea/modules/upload"
)
func renderAttachmentSettings(ctx *context.Context) {
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
ctx.Data["AttachmentStoreType"] = setting.Attachment.Storage.Type
ctx.Data["AttachmentAllowedTypes"] = setting.Attachment.AllowedTypes
ctx.Data["AttachmentMaxSize"] = setting.Attachment.MaxSize
ctx.Data["AttachmentMaxFiles"] = setting.Attachment.MaxFiles
// UploadIssueAttachment response for Issue/PR attachments
func UploadIssueAttachment(ctx *context.Context) {
uploadAttachment(ctx, setting.Attachment.AllowedTypes)
}
// UploadAttachment response for uploading issue's attachment
func UploadAttachment(ctx *context.Context) {
// UploadReleaseAttachment response for uploading release attachments
func UploadReleaseAttachment(ctx *context.Context) {
uploadAttachment(ctx, setting.Repository.Release.AllowedTypes)
}
// UploadAttachment response for uploading attachments
func uploadAttachment(ctx *context.Context, allowedTypes string) {
if !setting.Attachment.Enabled {
ctx.Error(404, "attachment is not enabled")
return
@ -45,7 +46,7 @@ func UploadAttachment(ctx *context.Context) {
buf = buf[:n]
}
err = upload.VerifyAllowedContentType(buf, strings.Split(setting.Attachment.AllowedTypes, ","))
err = upload.Verify(buf, header.Filename, allowedTypes)
if err != nil {
ctx.Error(400, err.Error())
return

View file

@ -17,6 +17,7 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/services/gitdiff"
)
@ -578,7 +579,8 @@ func CompareDiff(ctx *context.Context) {
ctx.Data["RequireSimpleMDE"] = true
ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes
setTemplateIfExists(ctx, pullRequestTemplateKey, nil, pullRequestTemplateCandidates)
renderAttachmentSettings(ctx)
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
upload.AddUploadContext(ctx, "comment")
ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWrite(models.UnitTypePullRequests)

View file

@ -494,18 +494,12 @@ func DeleteFilePost(ctx *context.Context, form auth.DeleteRepoFileForm) {
}
}
func renderUploadSettings(ctx *context.Context) {
ctx.Data["RequireTribute"] = true
ctx.Data["RequireSimpleMDE"] = true
ctx.Data["UploadAllowedTypes"] = strings.Join(setting.Repository.Upload.AllowedTypes, ",")
ctx.Data["UploadMaxSize"] = setting.Repository.Upload.FileMaxSize
ctx.Data["UploadMaxFiles"] = setting.Repository.Upload.MaxFiles
}
// UploadFile render upload file page
func UploadFile(ctx *context.Context) {
ctx.Data["PageIsUpload"] = true
renderUploadSettings(ctx)
ctx.Data["RequireTribute"] = true
ctx.Data["RequireSimpleMDE"] = true
upload.AddUploadContext(ctx, "repo")
canCommit := renderCommitRights(ctx)
treePath := cleanUploadFileName(ctx.Repo.TreePath)
if treePath != ctx.Repo.TreePath {
@ -538,7 +532,9 @@ func UploadFile(ctx *context.Context) {
// UploadFilePost response for uploading file
func UploadFilePost(ctx *context.Context, form auth.UploadRepoFileForm) {
ctx.Data["PageIsUpload"] = true
renderUploadSettings(ctx)
ctx.Data["RequireTribute"] = true
ctx.Data["RequireSimpleMDE"] = true
upload.AddUploadContext(ctx, "repo")
canCommit := renderCommitRights(ctx)
oldBranchName := ctx.Repo.BranchName
@ -704,12 +700,10 @@ func UploadFileToServer(ctx *context.Context) {
buf = buf[:n]
}
if len(setting.Repository.Upload.AllowedTypes) > 0 {
err = upload.VerifyAllowedContentType(buf, setting.Repository.Upload.AllowedTypes)
if err != nil {
ctx.Error(400, err.Error())
return
}
err = upload.Verify(buf, header.Filename, setting.Repository.Upload.AllowedTypes)
if err != nil {
ctx.Error(400, err.Error())
return
}
name := cleanUploadFileName(header.Filename)

View file

@ -26,6 +26,7 @@ import (
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/modules/util"
comment_service "code.gitea.io/gitea/services/comments"
issue_service "code.gitea.io/gitea/services/issue"
@ -573,6 +574,8 @@ func NewIssue(ctx *context.Context) {
body := ctx.Query("body")
ctx.Data["BodyQuery"] = body
ctx.Data["IsProjectsEnabled"] = ctx.Repo.CanRead(models.UnitTypeProjects)
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
upload.AddUploadContext(ctx, "comment")
milestoneID := ctx.QueryInt64("milestone")
if milestoneID > 0 {
@ -599,8 +602,6 @@ func NewIssue(ctx *context.Context) {
}
renderAttachmentSettings(ctx)
RetrieveRepoMetas(ctx, ctx.Repo.Repository, false)
setTemplateIfExists(ctx, issueTemplateKey, context.IssueTemplateDirCandidates, IssueTemplateCandidates)
if ctx.Written() {
@ -731,7 +732,8 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) {
ctx.Data["RequireSimpleMDE"] = true
ctx.Data["ReadOnly"] = false
ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes
renderAttachmentSettings(ctx)
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
upload.AddUploadContext(ctx, "comment")
var (
repo = ctx.Repo.Repository
@ -880,8 +882,8 @@ func ViewIssue(ctx *context.Context) {
ctx.Data["RequireTribute"] = true
ctx.Data["RequireSimpleMDE"] = true
ctx.Data["IsProjectsEnabled"] = ctx.Repo.CanRead(models.UnitTypeProjects)
renderAttachmentSettings(ctx)
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
upload.AddUploadContext(ctx, "comment")
if err = issue.LoadAttributes(); err != nil {
ctx.ServerError("LoadAttributes", err)

View file

@ -24,6 +24,7 @@ import (
"code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/utils"
"code.gitea.io/gitea/services/gitdiff"
@ -892,7 +893,8 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm)
ctx.Data["IsDiffCompare"] = true
ctx.Data["RequireHighlightJS"] = true
ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes
renderAttachmentSettings(ctx)
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
upload.AddUploadContext(ctx, "comment")
var (
repo = ctx.Repo.Repository

View file

@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/upload"
releaseservice "code.gitea.io/gitea/services/release"
)
@ -192,7 +193,8 @@ func NewRelease(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.release.new_release")
ctx.Data["PageIsReleaseList"] = true
ctx.Data["tag_target"] = ctx.Repo.Repository.DefaultBranch
renderAttachmentSettings(ctx)
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
upload.AddUploadContext(ctx, "release")
ctx.HTML(200, tplReleaseNew)
}
@ -278,7 +280,8 @@ func EditRelease(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.release.edit_release")
ctx.Data["PageIsReleaseList"] = true
ctx.Data["PageIsEditRelease"] = true
renderAttachmentSettings(ctx)
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
upload.AddUploadContext(ctx, "release")
tagName := ctx.Params("*")
rel, err := models.GetRelease(ctx.Repo.Repository.ID, tagName)

View file

@ -512,11 +512,6 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/attachments/:uuid", repo.GetAttachment)
}, ignSignIn)
m.Group("/attachments", func() {
m.Post("", repo.UploadAttachment)
m.Post("/delete", repo.DeleteAttachment)
}, reqSignIn)
m.Group("/:username", func() {
m.Post("/action/:action", user.Action)
}, reqSignIn)
@ -754,8 +749,11 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/reactions/:action", bindIgnErr(auth.ReactionForm{}), repo.ChangeIssueReaction)
m.Post("/lock", reqRepoIssueWriter, bindIgnErr(auth.IssueLockForm{}), repo.LockIssue)
m.Post("/unlock", reqRepoIssueWriter, repo.UnlockIssue)
m.Get("/attachments", repo.GetIssueAttachments)
}, context.RepoMustNotBeArchived())
m.Group("/:index", func() {
m.Get("/attachments", repo.GetIssueAttachments)
m.Get("/attachments/:uuid", repo.GetAttachment)
})
m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel)
m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone)
@ -764,13 +762,17 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/request_review", reqRepoIssuesOrPullsReader, repo.UpdatePullReviewRequest)
m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus)
m.Post("/resolve_conversation", reqRepoIssuesOrPullsReader, repo.UpdateResolveConversation)
m.Post("/attachments", repo.UploadIssueAttachment)
m.Post("/attachments/remove", repo.DeleteAttachment)
}, context.RepoMustNotBeArchived())
m.Group("/comments/:id", func() {
m.Post("", repo.UpdateCommentContent)
m.Post("/delete", repo.DeleteComment)
m.Post("/reactions/:action", bindIgnErr(auth.ReactionForm{}), repo.ChangeCommentReaction)
m.Get("/attachments", repo.GetCommentAttachments)
}, context.RepoMustNotBeArchived())
m.Group("/comments/:id", func() {
m.Get("/attachments", repo.GetCommentAttachments)
})
m.Group("/labels", func() {
m.Post("/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel)
m.Post("/edit", bindIgnErr(auth.CreateLabelForm{}), repo.UpdateLabel)
@ -826,11 +828,14 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/", repo.Releases)
m.Get("/tag/*", repo.SingleRelease)
m.Get("/latest", repo.LatestRelease)
m.Get("/attachments/:uuid", repo.GetAttachment)
}, repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefTag))
m.Group("/releases", func() {
m.Get("/new", repo.NewRelease)
m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost)
m.Post("/delete", repo.DeleteRelease)
m.Post("/attachments", repo.UploadReleaseAttachment)
m.Post("/attachments/remove", repo.DeleteAttachment)
}, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, context.RepoRef())
m.Group("/releases", func() {
m.Get("/edit/*", repo.EditRelease)