summaryrefslogtreecommitdiffstats
path: root/modules/setting/markup.go
blob: 75e6d651bdde2717a35f09128f720544e2fb90cb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package setting

import (
	"regexp"
	"strings"

	"code.gitea.io/gitea/modules/log"

	"gopkg.in/ini.v1"
)

// ExternalMarkupParsers represents the external markup parsers
var (
	ExternalMarkupParsers  []MarkupParser
	ExternalSanitizerRules []MarkupSanitizerRule
)

// MarkupParser defines the external parser configured in ini
type MarkupParser struct {
	Enabled        bool
	MarkupName     string
	Command        string
	FileExtensions []string
	IsInputFile    bool
}

// MarkupSanitizerRule defines the policy for whitelisting attributes on
// certain elements.
type MarkupSanitizerRule struct {
	Element   string
	AllowAttr string
	Regexp    *regexp.Regexp
}

func newMarkup() {
	for _, sec := range Cfg.Section("markup").ChildSections() {
		name := strings.TrimPrefix(sec.Name(), "markup.")
		if name == "" {
			log.Warn("name is empty, markup " + sec.Name() + "ignored")
			continue
		}

		if name == "sanitizer" {
			newMarkupSanitizer(name, sec)
		} else {
			newMarkupRenderer(name, sec)
		}
	}
}

func newMarkupSanitizer(name string, sec *ini.Section) {
	haveElement := sec.HasKey("ELEMENT")
	haveAttr := sec.HasKey("ALLOW_ATTR")
	haveRegexp := sec.HasKey("REGEXP")

	if !haveElement && !haveAttr && !haveRegexp {
		log.Warn("Skipping empty section: markup.%s.", name)
		return
	}

	if !haveElement || !haveAttr || !haveRegexp {
		log.Error("Missing required keys from markup.%s. Must have all three of ELEMENT, ALLOW_ATTR, and REGEXP defined!", name)
		return
	}

	elements := sec.Key("ELEMENT").ValueWithShadows()
	allowAttrs := sec.Key("ALLOW_ATTR").ValueWithShadows()
	regexps := sec.Key("REGEXP").ValueWithShadows()

	if len(elements) != len(allowAttrs) ||
		len(elements) != len(regexps) {
		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))
		return
	}

	ExternalSanitizerRules = make([]MarkupSanitizerRule, 0, len(elements))

	for index, pattern := range regexps {
		if pattern == "" {
			rule := MarkupSanitizerRule{
				Element:   elements[index],
				AllowAttr: allowAttrs[index],
				Regexp:    nil,
			}
			ExternalSanitizerRules = append(ExternalSanitizerRules, rule)
			continue
		}

		// Validate when parsing the config that this is a valid regular
		// expression. Then we can use regexp.MustCompile(...) later.
		compiled, err := regexp.Compile(pattern)
		if err != nil {
			log.Error("In module.%s: REGEXP at definition %d failed to compile: %v", name, index+1, err)
			continue
		}

		rule := MarkupSanitizerRule{
			Element:   elements[index],
			AllowAttr: allowAttrs[index],
			Regexp:    compiled,
		}
		ExternalSanitizerRules = append(ExternalSanitizerRules, rule)
	}
}

func newMarkupRenderer(name string, sec *ini.Section) {
	extensionReg := regexp.MustCompile(`\.\w`)

	extensions := sec.Key("FILE_EXTENSIONS").Strings(",")
	var exts = make([]string, 0, len(extensions))
	for _, extension := range extensions {
		if !extensionReg.MatchString(extension) {
			log.Warn(sec.Name() + " file extension " + extension + " is invalid. Extension ignored")
		} else {
			exts = append(exts, extension)
		}
	}

	if len(exts) == 0 {
		log.Warn(sec.Name() + " file extension is empty, markup " + name + " ignored")
		return
	}

	command := sec.Key("RENDER_COMMAND").MustString("")
	if command == "" {
		log.Warn(" RENDER_COMMAND is empty, markup " + name + " ignored")
		return
	}

	ExternalMarkupParsers = append(ExternalMarkupParsers, MarkupParser{
		Enabled:        sec.Key("ENABLED").MustBool(false),
		MarkupName:     name,
		FileExtensions: exts,
		Command:        command,
		IsInputFile:    sec.Key("IS_INPUT_FILE").MustBool(false),
	})
}