diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2022-06-16 11:33:23 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-16 11:33:23 +0800 |
commit | b01dce2a6e98c25915a8e98afb741a1c34d05aba (patch) | |
tree | b391ae8dff3ed5270b9721900bdc667b05fd555c /routers/web | |
parent | 7d1770cd714416bd80f114681d19e3076a0b0966 (diff) | |
download | gitea-b01dce2a6e98c25915a8e98afb741a1c34d05aba.tar.gz gitea-b01dce2a6e98c25915a8e98afb741a1c34d05aba.zip |
Allow render HTML with css/js external links (#19017)
* Allow render HTML with css/js external links
* Fix bug because of filename escape chars
* Fix lint
* Update docs about new configuration item
* Fix bug of render HTML in sub directory
* Add CSP head for displaying iframe in rendering file
* Fix test
* Apply suggestions from code review
Co-authored-by: delvh <dev.lh@web.de>
* Some improvements
* some improvement
* revert change in SanitizerDisabled of external renderer
* Add sandbox for iframe and support allow-scripts and allow-same-origin
* refactor
* fix
* fix lint
* fine tune
* use single option RENDER_CONTENT_MODE, use sandbox=allow-scripts
* fine tune CSP
* Apply suggestions from code review
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Diffstat (limited to 'routers/web')
-rw-r--r-- | routers/web/repo/compare.go | 4 | ||||
-rw-r--r-- | routers/web/repo/render.go | 79 | ||||
-rw-r--r-- | routers/web/repo/view.go | 36 | ||||
-rw-r--r-- | routers/web/web.go | 7 |
4 files changed, 108 insertions, 18 deletions
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 605594d5a9..5c46882f3d 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -139,7 +139,7 @@ func setCsvCompareContext(ctx *context.Context) { return csvReader, reader, err } - baseReader, baseBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, Filename: diffFile.OldName}, baseCommit) + baseReader, baseBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, RelativePath: diffFile.OldName}, baseCommit) if baseBlobCloser != nil { defer baseBlobCloser.Close() } @@ -151,7 +151,7 @@ func setCsvCompareContext(ctx *context.Context) { return CsvDiffResult{nil, "unable to load file from base commit"} } - headReader, headBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, Filename: diffFile.Name}, headCommit) + headReader, headBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, RelativePath: diffFile.Name}, headCommit) if headBlobCloser != nil { defer headBlobCloser.Close() } diff --git a/routers/web/repo/render.go b/routers/web/repo/render.go new file mode 100644 index 0000000000..28a6d2f429 --- /dev/null +++ b/routers/web/repo/render.go @@ -0,0 +1,79 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package repo + +import ( + "bytes" + "io" + "net/http" + "path" + + "code.gitea.io/gitea/modules/charset" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/typesniffer" + "code.gitea.io/gitea/modules/util" +) + +// RenderFile renders a file by repos path +func RenderFile(ctx *context.Context) { + blob, err := ctx.Repo.Commit.GetBlobByPath(ctx.Repo.TreePath) + if err != nil { + if git.IsErrNotExist(err) { + ctx.NotFound("GetBlobByPath", err) + } else { + ctx.ServerError("GetBlobByPath", err) + } + return + } + + dataRc, err := blob.DataAsync() + if err != nil { + ctx.ServerError("DataAsync", err) + return + } + defer dataRc.Close() + + buf := make([]byte, 1024) + n, _ := util.ReadAtMost(dataRc, buf) + buf = buf[:n] + + st := typesniffer.DetectContentType(buf) + isTextFile := st.IsText() + + rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc)) + + if markupType := markup.Type(blob.Name()); markupType == "" { + if isTextFile { + _, err = io.Copy(ctx.Resp, rd) + if err != nil { + ctx.ServerError("Copy", err) + } + return + } + ctx.Error(http.StatusInternalServerError, "Unsupported file type render") + return + } + + treeLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() + if ctx.Repo.TreePath != "" { + treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath) + } + + ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'; sandbox allow-scripts") + err = markup.Render(&markup.RenderContext{ + Ctx: ctx, + RelativePath: ctx.Repo.TreePath, + URLPrefix: path.Dir(treeLink), + Metas: ctx.Repo.Repository.ComposeDocumentMetas(), + GitRepo: ctx.Repo.GitRepo, + InStandalonePage: true, + }, rd, ctx.Resp) + if err != nil { + ctx.ServerError("Render", err) + return + } +} diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 01bd2d8923..fe60cf44c7 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -356,11 +356,11 @@ func renderReadmeFile(ctx *context.Context, readmeFile *namedBlob, readmeTreelin ctx.Data["MarkupType"] = string(markupType) var result strings.Builder err := markup.Render(&markup.RenderContext{ - Ctx: ctx, - Filename: readmeFile.name, - URLPrefix: readmeTreelink, - Metas: ctx.Repo.Repository.ComposeDocumentMetas(), - GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, + RelativePath: ctx.Repo.TreePath, + URLPrefix: readmeTreelink, + Metas: ctx.Repo.Repository.ComposeDocumentMetas(), + GitRepo: ctx.Repo.GitRepo, }, rd, &result) if err != nil { log.Error("Render failed: %v then fallback", err) @@ -528,18 +528,22 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st if !detected { markupType = "" } + metas := ctx.Repo.Repository.ComposeDocumentMetas() + metas["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL() err := markup.Render(&markup.RenderContext{ - Ctx: ctx, - Type: markupType, - Filename: blob.Name(), - URLPrefix: path.Dir(treeLink), - Metas: ctx.Repo.Repository.ComposeDocumentMetas(), - GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, + Type: markupType, + RelativePath: ctx.Repo.TreePath, + URLPrefix: path.Dir(treeLink), + Metas: metas, + GitRepo: ctx.Repo.GitRepo, }, rd, &result) if err != nil { ctx.ServerError("Render", err) return } + // to prevent iframe load third-party url + ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'") ctx.Data["EscapeStatus"], ctx.Data["FileContent"] = charset.EscapeControlString(result.String()) } else if readmeExist && !shouldRenderSource { buf := &bytes.Buffer{} @@ -627,11 +631,11 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st ctx.Data["MarkupType"] = markupType var result strings.Builder err := markup.Render(&markup.RenderContext{ - Ctx: ctx, - Filename: blob.Name(), - URLPrefix: path.Dir(treeLink), - Metas: ctx.Repo.Repository.ComposeDocumentMetas(), - GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, + RelativePath: ctx.Repo.TreePath, + URLPrefix: path.Dir(treeLink), + Metas: ctx.Repo.Repository.ComposeDocumentMetas(), + GitRepo: ctx.Repo.GitRepo, }, rd, &result) if err != nil { ctx.ServerError("Render", err) diff --git a/routers/web/web.go b/routers/web/web.go index ad005f74df..374bafbc8d 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1161,6 +1161,13 @@ func RegisterRoutes(m *web.Route) { m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.SingleDownload) }, repo.MustBeNotEmpty, reqRepoCodeReader) + m.Group("/render", func() { + m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RenderFile) + m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RenderFile) + m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RenderFile) + m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.RenderFile) + }, repo.MustBeNotEmpty, reqRepoCodeReader) + m.Group("/commits", func() { m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RefCommits) m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RefCommits) |