mirror of https://github.com/go-gitea/gitea.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
82 lines
2.2 KiB
82 lines
2.2 KiB
// 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(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, extraHeadHTML template.HTML) 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, extraHeadHTML) |
|
} |
|
|
|
func (r *RenderInternal) init(secID string, output io.Writer, extraHeadHTML template.HTML) io.WriteCloser { |
|
r.secureID = secID |
|
r.secureIDPrefix = r.secureID + ":" |
|
return &finalProcessor{renderInternal: r, output: output, extraHeadHTML: extraHeadHTML} |
|
} |
|
|
|
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 template.HTML, a ...any) error { |
|
_, err := w.Write([]byte(r.ProtectSafeAttrs(htmlutil.HTMLFormat(fmt, a...)))) |
|
return err |
|
}
|
|
|