gitea源码

renderinternal.go 2.1KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. // Copyright 2024 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package internal
  4. import (
  5. "crypto/rand"
  6. "encoding/base64"
  7. "html/template"
  8. "io"
  9. "regexp"
  10. "strings"
  11. "sync"
  12. "code.gitea.io/gitea/modules/htmlutil"
  13. "golang.org/x/net/html"
  14. )
  15. var reAttrClass = sync.OnceValue(func() *regexp.Regexp {
  16. // TODO: it isn't a problem at the moment because our HTML contents are always well constructed
  17. return regexp.MustCompile(`(<[^>]+)\s+class="([^"]+)"([^>]*>)`)
  18. })
  19. // RenderInternal also works without initialization
  20. // If no initialization (no secureID), it will not protect any attributes and return the original name&value
  21. type RenderInternal struct {
  22. secureID string
  23. secureIDPrefix string
  24. }
  25. func (r *RenderInternal) Init(output io.Writer) io.WriteCloser {
  26. buf := make([]byte, 12)
  27. _, err := rand.Read(buf)
  28. if err != nil {
  29. panic("unable to generate secure id")
  30. }
  31. return r.init(base64.URLEncoding.EncodeToString(buf), output)
  32. }
  33. func (r *RenderInternal) init(secID string, output io.Writer) io.WriteCloser {
  34. r.secureID = secID
  35. r.secureIDPrefix = r.secureID + ":"
  36. return &finalProcessor{renderInternal: r, output: output}
  37. }
  38. func (r *RenderInternal) RecoverProtectedValue(v string) (string, bool) {
  39. if !strings.HasPrefix(v, r.secureIDPrefix) {
  40. return "", false
  41. }
  42. return v[len(r.secureIDPrefix):], true
  43. }
  44. func (r *RenderInternal) SafeAttr(name string) string {
  45. if r.secureID == "" {
  46. return name
  47. }
  48. return "data-attr-" + name
  49. }
  50. func (r *RenderInternal) SafeValue(val string) string {
  51. if r.secureID == "" {
  52. return val
  53. }
  54. return r.secureID + ":" + val
  55. }
  56. func (r *RenderInternal) NodeSafeAttr(attr, val string) html.Attribute {
  57. return html.Attribute{Key: r.SafeAttr(attr), Val: r.SafeValue(val)}
  58. }
  59. func (r *RenderInternal) ProtectSafeAttrs(content template.HTML) template.HTML {
  60. if r.secureID == "" {
  61. return content
  62. }
  63. return template.HTML(reAttrClass().ReplaceAllString(string(content), `$1 data-attr-class="`+r.secureIDPrefix+`$2"$3`))
  64. }
  65. func (r *RenderInternal) FormatWithSafeAttrs(w io.Writer, fmt template.HTML, a ...any) error {
  66. _, err := w.Write([]byte(r.ProtectSafeAttrs(htmlutil.HTMLFormat(fmt, a...))))
  67. return err
  68. }