Refactor markup render system (#32533)

Remove unmaintainable sanitizer rules. No need to add special "class"
regexp rules anymore, use RenderInternal.SafeAttr instead, more details
(and examples) are in the tests
This commit is contained in:
wxiaoguang 2024-11-18 13:25:42 +08:00 committed by GitHub
parent 4f879a00df
commit 8a20fba8eb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
42 changed files with 568 additions and 508 deletions

View file

@ -9,14 +9,15 @@ import (
"io"
"net/url"
"strings"
"sync"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/markup/internal"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"github.com/yuin/goldmark/ast"
"golang.org/x/sync/errgroup"
)
type RenderMetaMode string
@ -65,6 +66,8 @@ type RenderContext struct {
SidebarTocNode ast.Node
RenderMetaAs RenderMetaMode
InStandalonePage bool // used by external render. the router "/org/repo/render/..." will output the rendered content in a standalone page
RenderInternal internal.RenderInternal
}
// Cancel runs any cleanup functions that have been registered for this Ctx
@ -156,59 +159,53 @@ sandbox="allow-scripts"
return err
}
func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Writer) error {
var wg sync.WaitGroup
var err error
func pipes() (io.ReadCloser, io.WriteCloser, func()) {
pr, pw := io.Pipe()
defer func() {
return pr, pw, func() {
_ = pr.Close()
_ = pw.Close()
}()
}
}
var pr2 io.ReadCloser
var pw2 io.WriteCloser
func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Writer) error {
finalProcessor := ctx.RenderInternal.Init(output)
defer finalProcessor.Close()
var sanitizerDisabled bool
if r, ok := renderer.(ExternalRenderer); ok {
sanitizerDisabled = r.SanitizerDisabled()
// input -> (pw1=pr1) -> renderer -> (pw2=pr2) -> SanitizeReader -> finalProcessor -> output
// no sanitizer: input -> (pw1=pr1) -> renderer -> pw2(finalProcessor) -> output
pr1, pw1, close1 := pipes()
defer close1()
eg, _ := errgroup.WithContext(ctx.Ctx)
var pw2 io.WriteCloser = util.NopCloser{Writer: finalProcessor}
if r, ok := renderer.(ExternalRenderer); !ok || !r.SanitizerDisabled() {
var pr2 io.ReadCloser
var close2 func()
pr2, pw2, close2 = pipes()
defer close2()
eg.Go(func() error {
defer pr2.Close()
return SanitizeReader(pr2, renderer.Name(), finalProcessor)
})
}
if !sanitizerDisabled {
pr2, pw2 = io.Pipe()
defer func() {
_ = pr2.Close()
_ = pw2.Close()
}()
wg.Add(1)
go func() {
err = SanitizeReader(pr2, renderer.Name(), output)
_ = pr2.Close()
wg.Done()
}()
} else {
pw2 = util.NopCloser{Writer: output}
}
wg.Add(1)
go func() {
eg.Go(func() (err error) {
if r, ok := renderer.(PostProcessRenderer); ok && r.NeedPostProcess() {
err = PostProcess(ctx, pr, pw2)
err = PostProcess(ctx, pr1, pw2)
} else {
_, err = io.Copy(pw2, pr)
_, err = io.Copy(pw2, pr1)
}
_ = pr.Close()
_ = pw2.Close()
wg.Done()
}()
_, _ = pr1.Close(), pw2.Close()
return err
})
if err1 := renderer.Render(ctx, input, pw); err1 != nil {
return err1
if err := renderer.Render(ctx, input, pw1); err != nil {
return err
}
_ = pw.Close()
_ = pw1.Close()
wg.Wait()
return err
return eg.Wait()
}
// Init initializes the render global variables