loading up the forgejo repo on tangled to test page performance
at forgejo 6.0 kB view raw
1// Copyright 2019 The Gitea Authors. All rights reserved. 2// SPDX-License-Identifier: MIT 3 4package setting 5 6import ( 7 "regexp" 8 "strings" 9 10 "forgejo.org/modules/log" 11) 12 13// ExternalMarkupRenderers represents the external markup renderers 14var ( 15 ExternalMarkupRenderers []*MarkupRenderer 16 ExternalSanitizerRules []MarkupSanitizerRule 17 MermaidMaxSourceCharacters int 18 FilePreviewMaxLines int 19) 20 21const ( 22 RenderContentModeSanitized = "sanitized" 23 RenderContentModeNoSanitizer = "no-sanitizer" 24 RenderContentModeIframe = "iframe" 25) 26 27// Markdown settings 28var Markdown = struct { 29 EnableHardLineBreakInComments bool 30 EnableHardLineBreakInDocuments bool 31 CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"` 32 FileExtensions []string 33 EnableMath bool 34}{ 35 EnableHardLineBreakInComments: true, 36 EnableHardLineBreakInDocuments: false, 37 FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd,.livemd", ","), 38 EnableMath: true, 39} 40 41// MarkupRenderer defines the external parser configured in ini 42type MarkupRenderer struct { 43 Enabled bool 44 MarkupName string 45 Command string 46 FileExtensions []string 47 IsInputFile bool 48 NeedPostProcess bool 49 MarkupSanitizerRules []MarkupSanitizerRule 50 RenderContentMode string 51} 52 53// MarkupSanitizerRule defines the policy for whitelisting attributes on 54// certain elements. 55type MarkupSanitizerRule struct { 56 Element string 57 AllowAttr string 58 Regexp *regexp.Regexp 59 AllowDataURIImages bool 60} 61 62func loadMarkupFrom(rootCfg ConfigProvider) { 63 mustMapSetting(rootCfg, "markdown", &Markdown) 64 65 MermaidMaxSourceCharacters = rootCfg.Section("markup").Key("MERMAID_MAX_SOURCE_CHARACTERS").MustInt(5000) 66 FilePreviewMaxLines = rootCfg.Section("markup").Key("FILEPREVIEW_MAX_LINES").MustInt(50) 67 ExternalMarkupRenderers = make([]*MarkupRenderer, 0, 10) 68 ExternalSanitizerRules = make([]MarkupSanitizerRule, 0, 10) 69 70 for _, sec := range rootCfg.Section("markup").ChildSections() { 71 name := strings.TrimPrefix(sec.Name(), "markup.") 72 if name == "" { 73 log.Warn("name is empty, markup " + sec.Name() + "ignored") 74 continue 75 } 76 77 if name == "sanitizer" || strings.HasPrefix(name, "sanitizer.") { 78 newMarkupSanitizer(name, sec) 79 } else { 80 newMarkupRenderer(name, sec) 81 } 82 } 83} 84 85func newMarkupSanitizer(name string, sec ConfigSection) { 86 rule, ok := createMarkupSanitizerRule(name, sec) 87 if ok { 88 if strings.HasPrefix(name, "sanitizer.") { 89 names := strings.SplitN(strings.TrimPrefix(name, "sanitizer."), ".", 2) 90 name = names[0] 91 } 92 for _, renderer := range ExternalMarkupRenderers { 93 if name == renderer.MarkupName { 94 renderer.MarkupSanitizerRules = append(renderer.MarkupSanitizerRules, rule) 95 return 96 } 97 } 98 ExternalSanitizerRules = append(ExternalSanitizerRules, rule) 99 } 100} 101 102func createMarkupSanitizerRule(name string, sec ConfigSection) (MarkupSanitizerRule, bool) { 103 var rule MarkupSanitizerRule 104 105 ok := false 106 if sec.HasKey("ALLOW_DATA_URI_IMAGES") { 107 rule.AllowDataURIImages = sec.Key("ALLOW_DATA_URI_IMAGES").MustBool(false) 108 ok = true 109 } 110 111 if sec.HasKey("ELEMENT") || sec.HasKey("ALLOW_ATTR") { 112 rule.Element = sec.Key("ELEMENT").Value() 113 rule.AllowAttr = sec.Key("ALLOW_ATTR").Value() 114 115 if rule.Element == "" || rule.AllowAttr == "" { 116 log.Error("Missing required values from markup.%s. Must have ELEMENT and ALLOW_ATTR defined!", name) 117 return rule, false 118 } 119 120 regexpStr := sec.Key("REGEXP").Value() 121 if regexpStr != "" { 122 // Validate when parsing the config that this is a valid regular 123 // expression. Then we can use regexp.MustCompile(...) later. 124 compiled, err := regexp.Compile(regexpStr) 125 if err != nil { 126 log.Error("In markup.%s: REGEXP (%s) failed to compile: %v", name, regexpStr, err) 127 return rule, false 128 } 129 130 rule.Regexp = compiled 131 } 132 133 ok = true 134 } 135 136 if !ok { 137 log.Error("Missing required keys from markup.%s. Must have ELEMENT and ALLOW_ATTR or ALLOW_DATA_URI_IMAGES defined!", name) 138 return rule, false 139 } 140 141 return rule, true 142} 143 144func newMarkupRenderer(name string, sec ConfigSection) { 145 extensionReg := regexp.MustCompile(`\.\w`) 146 147 extensions := sec.Key("FILE_EXTENSIONS").Strings(",") 148 exts := make([]string, 0, len(extensions)) 149 for _, extension := range extensions { 150 if !extensionReg.MatchString(extension) { 151 log.Warn(sec.Name() + " file extension " + extension + " is invalid. Extension ignored") 152 } else { 153 exts = append(exts, extension) 154 } 155 } 156 157 if len(exts) == 0 { 158 log.Warn(sec.Name() + " file extension is empty, markup " + name + " ignored") 159 return 160 } 161 162 command := sec.Key("RENDER_COMMAND").MustString("") 163 if command == "" { 164 log.Warn(" RENDER_COMMAND is empty, markup " + name + " ignored") 165 return 166 } 167 168 if sec.HasKey("DISABLE_SANITIZER") { 169 log.Error("Deprecated setting `[markup.*]` `DISABLE_SANITIZER` present. This fallback will be removed in v1.18.0") 170 } 171 172 renderContentMode := sec.Key("RENDER_CONTENT_MODE").MustString(RenderContentModeSanitized) 173 if !sec.HasKey("RENDER_CONTENT_MODE") && sec.Key("DISABLE_SANITIZER").MustBool(false) { 174 renderContentMode = RenderContentModeNoSanitizer // if only the legacy DISABLE_SANITIZER exists, use it 175 } 176 if renderContentMode != RenderContentModeSanitized && 177 renderContentMode != RenderContentModeNoSanitizer && 178 renderContentMode != RenderContentModeIframe { 179 log.Error("invalid RENDER_CONTENT_MODE: %q, default to %q", renderContentMode, RenderContentModeSanitized) 180 renderContentMode = RenderContentModeSanitized 181 } 182 183 ExternalMarkupRenderers = append(ExternalMarkupRenderers, &MarkupRenderer{ 184 Enabled: sec.Key("ENABLED").MustBool(false), 185 MarkupName: name, 186 FileExtensions: exts, 187 Command: command, 188 IsInputFile: sec.Key("IS_INPUT_FILE").MustBool(false), 189 NeedPostProcess: sec.Key("NEED_POSTPROCESS").MustBool(true), 190 RenderContentMode: renderContentMode, 191 }) 192}