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.

141 lines
3.8 KiB

  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package setting
  5. import (
  6. "regexp"
  7. "strings"
  8. "code.gitea.io/gitea/modules/log"
  9. "gopkg.in/ini.v1"
  10. )
  11. // ExternalMarkupParsers represents the external markup parsers
  12. var (
  13. ExternalMarkupParsers []MarkupParser
  14. ExternalSanitizerRules []MarkupSanitizerRule
  15. )
  16. // MarkupParser defines the external parser configured in ini
  17. type MarkupParser struct {
  18. Enabled bool
  19. MarkupName string
  20. Command string
  21. FileExtensions []string
  22. IsInputFile bool
  23. }
  24. // MarkupSanitizerRule defines the policy for whitelisting attributes on
  25. // certain elements.
  26. type MarkupSanitizerRule struct {
  27. Element string
  28. AllowAttr string
  29. Regexp *regexp.Regexp
  30. }
  31. func newMarkup() {
  32. for _, sec := range Cfg.Section("markup").ChildSections() {
  33. name := strings.TrimPrefix(sec.Name(), "markup.")
  34. if name == "" {
  35. log.Warn("name is empty, markup " + sec.Name() + "ignored")
  36. continue
  37. }
  38. if name == "sanitizer" {
  39. newMarkupSanitizer(name, sec)
  40. } else {
  41. newMarkupRenderer(name, sec)
  42. }
  43. }
  44. }
  45. func newMarkupSanitizer(name string, sec *ini.Section) {
  46. haveElement := sec.HasKey("ELEMENT")
  47. haveAttr := sec.HasKey("ALLOW_ATTR")
  48. haveRegexp := sec.HasKey("REGEXP")
  49. if !haveElement && !haveAttr && !haveRegexp {
  50. log.Warn("Skipping empty section: markup.%s.", name)
  51. return
  52. }
  53. if !haveElement || !haveAttr || !haveRegexp {
  54. log.Error("Missing required keys from markup.%s. Must have all three of ELEMENT, ALLOW_ATTR, and REGEXP defined!", name)
  55. return
  56. }
  57. elements := sec.Key("ELEMENT").ValueWithShadows()
  58. allowAttrs := sec.Key("ALLOW_ATTR").ValueWithShadows()
  59. regexps := sec.Key("REGEXP").ValueWithShadows()
  60. if len(elements) != len(allowAttrs) ||
  61. len(elements) != len(regexps) {
  62. log.Error("All three keys in markup.%s (ELEMENT, ALLOW_ATTR, REGEXP) must be defined the same number of times! Got %d, %d, and %d respectively.", name, len(elements), len(allowAttrs), len(regexps))
  63. return
  64. }
  65. ExternalSanitizerRules = make([]MarkupSanitizerRule, 0, len(elements))
  66. for index, pattern := range regexps {
  67. if pattern == "" {
  68. rule := MarkupSanitizerRule{
  69. Element: elements[index],
  70. AllowAttr: allowAttrs[index],
  71. Regexp: nil,
  72. }
  73. ExternalSanitizerRules = append(ExternalSanitizerRules, rule)
  74. continue
  75. }
  76. // Validate when parsing the config that this is a valid regular
  77. // expression. Then we can use regexp.MustCompile(...) later.
  78. compiled, err := regexp.Compile(pattern)
  79. if err != nil {
  80. log.Error("In module.%s: REGEXP at definition %d failed to compile: %v", name, index+1, err)
  81. continue
  82. }
  83. rule := MarkupSanitizerRule{
  84. Element: elements[index],
  85. AllowAttr: allowAttrs[index],
  86. Regexp: compiled,
  87. }
  88. ExternalSanitizerRules = append(ExternalSanitizerRules, rule)
  89. }
  90. }
  91. func newMarkupRenderer(name string, sec *ini.Section) {
  92. extensionReg := regexp.MustCompile(`\.\w`)
  93. extensions := sec.Key("FILE_EXTENSIONS").Strings(",")
  94. var exts = make([]string, 0, len(extensions))
  95. for _, extension := range extensions {
  96. if !extensionReg.MatchString(extension) {
  97. log.Warn(sec.Name() + " file extension " + extension + " is invalid. Extension ignored")
  98. } else {
  99. exts = append(exts, extension)
  100. }
  101. }
  102. if len(exts) == 0 {
  103. log.Warn(sec.Name() + " file extension is empty, markup " + name + " ignored")
  104. return
  105. }
  106. command := sec.Key("RENDER_COMMAND").MustString("")
  107. if command == "" {
  108. log.Warn(" RENDER_COMMAND is empty, markup " + name + " ignored")
  109. return
  110. }
  111. ExternalMarkupParsers = append(ExternalMarkupParsers, MarkupParser{
  112. Enabled: sec.Key("ENABLED").MustBool(false),
  113. MarkupName: name,
  114. FileExtensions: exts,
  115. Command: command,
  116. IsInputFile: sec.Key("IS_INPUT_FILE").MustBool(false),
  117. })
  118. }