diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 5814c0d69d9ff..f9eea4fc7a578 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -2181,8 +2181,11 @@ PATH = ;RENDER_COMMAND = "asciidoc --out-file=- -" ;; Don't pass the file on STDIN, pass the filename as argument instead. ;IS_INPUT_FILE = false -; Don't filter html tags and attributes if true +;; Don't filter html tags and attributes if true ;DISABLE_SANITIZER = false +;; Display the HTML with an embed iframe instead of rendering inside the page. When a renderer uses iframe +;; it suppresses DISABLE_SANITIZER option and there will be no sanitizer. +;USE_IFRAME=false ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index c4c81d58ace80..cee27dc57172c 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -1027,7 +1027,7 @@ IS_INPUT_FILE = false - RENDER\_COMMAND: External command to render all matching extensions. - IS\_INPUT\_FILE: **false** Input is not a standard input but a file param followed `RENDER_COMMAND`. - DISABLE_SANITIZER: **false** Don't filter html tags and attributes if true. This is insecure. Don't change this to true except you know what that means. -- USE_IFRAME: **false** Display the HTML with an embed iframe but not directly renderer. +- USE_IFRAME: **false** Display the HTML with an embed iframe instead of rendering inside the page. When a renderer uses iframe, it suppresses DISABLE_SANITIZER option and there will be no sanitizer. Two special environment variables are passed to the render command: - `GITEA_PREFIX_SRC`, which contains the current URL prefix in the `src` path tree. To be used as prefix for links. diff --git a/modules/markup/console/console.go b/modules/markup/console/console.go index 06298c72c4160..597593eee153c 100644 --- a/modules/markup/console/console.go +++ b/modules/markup/console/console.go @@ -26,9 +26,7 @@ func init() { } // Renderer implements markup.Renderer -type Renderer struct { - markup.BaseRenderer -} +type Renderer struct{} // Name implements markup.Renderer func (Renderer) Name() string { diff --git a/modules/markup/csv/csv.go b/modules/markup/csv/csv.go index 269816278d51d..5095b854650d1 100644 --- a/modules/markup/csv/csv.go +++ b/modules/markup/csv/csv.go @@ -22,9 +22,7 @@ func init() { } // Renderer implements markup.Renderer for csv files -type Renderer struct { - markup.BaseRenderer -} +type Renderer struct{} // Name implements markup.Renderer func (Renderer) Name() string { diff --git a/modules/markup/external/external.go b/modules/markup/external/external.go index 8354c676ecc46..7cd9ab4401515 100644 --- a/modules/markup/external/external.go +++ b/modules/markup/external/external.go @@ -34,6 +34,9 @@ type Renderer struct { *setting.MarkupRenderer } +var _ markup.PostProcessRenderer = (*Renderer)(nil) +var _ markup.ExternalRenderer = (*Renderer)(nil) + // Name returns the external tool name func (p *Renderer) Name() string { return p.MarkupName diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go index f52c5bc97bd0d..37e11e606fce5 100644 --- a/modules/markup/markdown/markdown.go +++ b/modules/markup/markdown/markdown.go @@ -203,16 +203,16 @@ func init() { } // Renderer implements markup.Renderer -type Renderer struct { - markup.BaseRenderer -} +type Renderer struct{} + +var _ markup.PostProcessRenderer = (*Renderer)(nil) // Name implements markup.Renderer func (Renderer) Name() string { return MarkupName } -// NeedPostProcess implements markup.Renderer +// NeedPostProcess implements markup.PostProcessRenderer func (Renderer) NeedPostProcess() bool { return true } // Extensions implements markup.Renderer diff --git a/modules/markup/orgmode/orgmode.go b/modules/markup/orgmode/orgmode.go index 04b4b43196d04..8c9f3b3da736b 100644 --- a/modules/markup/orgmode/orgmode.go +++ b/modules/markup/orgmode/orgmode.go @@ -27,16 +27,16 @@ func init() { } // Renderer implements markup.Renderer for orgmode -type Renderer struct { - markup.BaseRenderer -} +type Renderer struct{} + +var _ markup.PostProcessRenderer = (*Renderer)(nil) // Name implements markup.Renderer func (Renderer) Name() string { return "orgmode" } -// NeedPostProcess implements markup.Renderer +// NeedPostProcess implements markup.PostProcessRenderer func (Renderer) NeedPostProcess() bool { return true } // Extensions implements markup.Renderer diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go index 7f5e9dcaef595..21f974b71b714 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -44,18 +44,18 @@ type Header struct { // RenderContext represents a render context type RenderContext struct { - Ctx context.Context - RelativePath string // relative path from tree root of the branch - Type string - IsWiki bool - URLPrefix string - Metas map[string]string - DefaultLink string - GitRepo *git.Repository - ShaExistCache map[string]bool - cancelFn func() - TableOfContents []Header - AllowIFrame bool + Ctx context.Context + RelativePath string // relative path from tree root of the branch + Type string + IsWiki bool + URLPrefix string + Metas map[string]string + DefaultLink string + GitRepo *git.Repository + ShaExistCache map[string]bool + cancelFn func() + TableOfContents []Header + InStandalonePage bool // used by external render. the router "/org/repo/render/..." will output the rendered content in a standalone page } // Cancel runs any cleanup functions that have been registered for this Ctx @@ -90,23 +90,23 @@ func (ctx *RenderContext) AddCancel(fn func()) { type Renderer interface { Name() string // markup format name Extensions() []string - NeedPostProcess() bool SanitizerRules() []setting.MarkupSanitizerRule - SanitizerDisabled() bool - DisplayInIFrame() bool Render(ctx *RenderContext, input io.Reader, output io.Writer) error } -type BaseRenderer struct{} - -// NeedPostProcess implements markup.Renderer -func (BaseRenderer) NeedPostProcess() bool { return false } +// PostProcessRenderer defines an interface for renderers who need post process +type PostProcessRenderer interface { + NeedPostProcess() bool +} -// SanitizerDisabled disabled sanitize if return true -func (BaseRenderer) SanitizerDisabled() bool { return false } +// PostProcessRenderer defines an interface for external renderers +type ExternalRenderer interface { + // SanitizerDisabled disabled sanitize if return true + SanitizerDisabled() bool -// DisplayInIFrame represents whether render the content with an iframe -func (BaseRenderer) DisplayInIFrame() bool { return false } + // DisplayInIFrame represents whether render the content with an iframe + DisplayInIFrame() bool +} // RendererContentDetector detects if the content can be rendered // by specified renderer @@ -177,10 +177,14 @@ type nopCloser struct { func (nopCloser) Close() error { return nil } -func renderIFrame(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Writer) error { - _, err := io.WriteString(output, fmt.Sprintf(``, +func renderIFrame(ctx *RenderContext, output io.Writer) error { + _, err := io.WriteString(output, fmt.Sprintf(` +`, setting.AppSubURL, url.PathEscape(ctx.Metas["user"]), url.PathEscape(ctx.Metas["repo"]), @@ -202,7 +206,12 @@ func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Wr var pr2 io.ReadCloser var pw2 io.WriteCloser - if !renderer.SanitizerDisabled() { + var sanitizerDisabled bool + if r, ok := renderer.(ExternalRenderer); ok { + sanitizerDisabled = r.SanitizerDisabled() + } + + if !sanitizerDisabled { pr2, pw2 = io.Pipe() defer func() { _ = pr2.Close() @@ -221,7 +230,7 @@ func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Wr wg.Add(1) go func() { - if renderer.NeedPostProcess() { + if r, ok := renderer.(PostProcessRenderer); ok && r.NeedPostProcess() { err = PostProcess(ctx, pr, pw2) } else { _, err = io.Copy(pw2, pr) @@ -268,8 +277,12 @@ func (err ErrUnsupportedRenderExtension) Error() string { func renderFile(ctx *RenderContext, input io.Reader, output io.Writer) error { extension := strings.ToLower(filepath.Ext(ctx.RelativePath)) if renderer, ok := extRenderers[extension]; ok { - if renderer.DisplayInIFrame() && ctx.AllowIFrame { - return renderIFrame(ctx, renderer, input, output) + if r, ok := renderer.(ExternalRenderer); ok && r.DisplayInIFrame() { + if !ctx.InStandalonePage { + // for an external render, it could only output its content in a standalone page + // otherwise, a