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:
parent
4f879a00df
commit
8a20fba8eb
42 changed files with 568 additions and 508 deletions
82
modules/markup/internal/renderinternal.go
Normal file
82
modules/markup/internal/renderinternal.go
Normal file
|
@ -0,0 +1,82 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"html/template"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"code.gitea.io/gitea/modules/htmlutil"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
var reAttrClass = sync.OnceValue[*regexp.Regexp](func() *regexp.Regexp {
|
||||
// TODO: it isn't a problem at the moment because our HTML contents are always well constructed
|
||||
return regexp.MustCompile(`(<[^>]+)\s+class="([^"]+)"([^>]*>)`)
|
||||
})
|
||||
|
||||
// RenderInternal also works without initialization
|
||||
// If no initialization (no secureID), it will not protect any attributes and return the original name&value
|
||||
type RenderInternal struct {
|
||||
secureID string
|
||||
secureIDPrefix string
|
||||
}
|
||||
|
||||
func (r *RenderInternal) Init(output io.Writer) io.WriteCloser {
|
||||
buf := make([]byte, 12)
|
||||
_, err := rand.Read(buf)
|
||||
if err != nil {
|
||||
panic("unable to generate secure id")
|
||||
}
|
||||
return r.init(base64.URLEncoding.EncodeToString(buf), output)
|
||||
}
|
||||
|
||||
func (r *RenderInternal) init(secID string, output io.Writer) io.WriteCloser {
|
||||
r.secureID = secID
|
||||
r.secureIDPrefix = r.secureID + ":"
|
||||
return &finalProcessor{renderInternal: r, output: output}
|
||||
}
|
||||
|
||||
func (r *RenderInternal) RecoverProtectedValue(v string) (string, bool) {
|
||||
if !strings.HasPrefix(v, r.secureIDPrefix) {
|
||||
return "", false
|
||||
}
|
||||
return v[len(r.secureIDPrefix):], true
|
||||
}
|
||||
|
||||
func (r *RenderInternal) SafeAttr(name string) string {
|
||||
if r.secureID == "" {
|
||||
return name
|
||||
}
|
||||
return "data-attr-" + name
|
||||
}
|
||||
|
||||
func (r *RenderInternal) SafeValue(val string) string {
|
||||
if r.secureID == "" {
|
||||
return val
|
||||
}
|
||||
return r.secureID + ":" + val
|
||||
}
|
||||
|
||||
func (r *RenderInternal) NodeSafeAttr(attr, val string) html.Attribute {
|
||||
return html.Attribute{Key: r.SafeAttr(attr), Val: r.SafeValue(val)}
|
||||
}
|
||||
|
||||
func (r *RenderInternal) ProtectSafeAttrs(content template.HTML) template.HTML {
|
||||
if r.secureID == "" {
|
||||
return content
|
||||
}
|
||||
return template.HTML(reAttrClass().ReplaceAllString(string(content), `$1 data-attr-class="`+r.secureIDPrefix+`$2"$3`))
|
||||
}
|
||||
|
||||
func (r *RenderInternal) FormatWithSafeAttrs(w io.Writer, fmt string, a ...any) error {
|
||||
_, err := w.Write([]byte(r.ProtectSafeAttrs(htmlutil.HTMLFormat(fmt, a...))))
|
||||
return err
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue