-
Notifications
You must be signed in to change notification settings - Fork 0
/
config.go
127 lines (106 loc) · 3.07 KB
/
config.go
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
package main
import (
"io/ioutil"
"regexp"
// hashicorp/hcl has a bug that was a show-stopper for parsing the config
// the way I wanted: https://github.com/hashicorp/hcl/issues/164
//
// This is fixed by the following fork (a PR has been submitted against it:
// https://github.com/hashicorp/hcl/pull/228)
//
// TODO: replace with hashicorp/hcl when 228 is merged.
"github.com/carlsverre/hcl"
)
var (
ArraySplat = regexp.MustCompile(`\[\*\]`)
EscapeReserved = regexp.MustCompile(`([.$\[\]])`)
)
type ConfigRule struct {
Whitelist string
}
type RuleOptions struct {
Body []ConfigRule
Querystring []ConfigRule
}
type HTTPMatch struct {
Path string
Method string
RuleOptions `hcl:"rule"`
}
type MatchOptions struct {
HTTP []HTTPMatch
}
type Config struct {
Match MatchOptions
Port string
ProxyPass string `hcl:"proxy_pass"`
}
func loadConfig(file string, config *Config) error {
data, err := ioutil.ReadFile(file)
if err != nil {
return err
}
return hcl.Unmarshal(data, config)
}
// Parses a location string into a regex capable of matching against. A
// location string supports specifying an arbitrarily nested key in a tree-type
// data structure (like a JSON or XML blob).
//
// For instance, given the following JSON:
//
// { "a": [{ "c": 4 }, { "c" }] }
//
// We can whitelist all c values with:
//
// $.a[*].c
//
// * `$` specifies the root of the document
// * `.` specifies dereferencing a key on an Object
// * `[n]` specifies dereferencing an index of an Array where n can be..
// * any positive integer to specify a specific index to whitelist
// * `*` to specify all indexes in an Array
func locationToRegex(location string) *regexp.Regexp {
result := ArraySplat.ReplaceAllLiteralString(location, "[\\d+]")
result = EscapeReserved.ReplaceAllString(result, "\\$1")
pattern := "^" + result + "$"
return regexp.MustCompile(pattern)
}
// FindHTTPMatch finds the first http match clause in the server's config that
// matches the method and pathname of the current request. Used to lookup the
// whitelist rules defined for the match.
func (config Config) FindHTTPMatch(method string, pathname string) HTTPMatch {
for _, m := range config.Match.HTTP {
isMatch := true
if m.Method != "" {
isMatch = isMatch && isSameCaseInsensitive(m.Method, method)
}
if m.Path != "" {
isMatch = isMatch && isSamePath(m.Path, pathname)
}
if isMatch {
return m
}
}
return HTTPMatch{}
}
// HasBodyWhitelistMatch returns whether or not the whitelist request body rules
// match the location of data currently being scanned.
func (r RuleOptions) HasBodyWhitelistMatch(location string) bool {
for _, rule := range r.Body {
re := locationToRegex(rule.Whitelist) // todo: cache, somewhere
if re.MatchString(location) {
return true
}
}
return false
}
// HasQuerystringWhitelistMatch returns whether or not a key in a querystring
// has been whitelisted
func (r RuleOptions) HasQuerystringWhitelistMatch(key string) bool {
for _, rule := range r.Querystring {
if rule.Whitelist == key {
return true
}
}
return false
}