mirror of
https://github.com/go-gitea/gitea
synced 2024-12-23 04:17:53 +01:00
8a20fba8eb
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
83 lines
2.2 KiB
Go
83 lines
2.2 KiB
Go
// 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
|
|
}
|