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
|
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package headerfilter
import (
"errors"
"fmt"
"net/http"
"net/textproto"
"regexp"
)
// Maximum header value size before we return
// an instant negative match. They shouldn't
// go beyond this size in most cases anywho.
const MaxHeaderValue = 1024
// ErrLargeHeaderValue is returned on attempting to match on a value > MaxHeaderValue.
var ErrLargeHeaderValue = errors.New("header value too large")
// Filters represents a set of http.Header regular
// expression filters built-in statistic tracking.
type Filters []headerfilter
type headerfilter struct {
// key is the header key to match against
// in canonical textproto mime header format.
key string
// exprs contains regular expressions to
// match values against for this header key.
exprs []*regexp.Regexp
}
// Append will add new header filter expression under given header key.
func (fs *Filters) Append(key string, expr string) error {
var filter *headerfilter
// Ensure in canonical mime header format.
key = textproto.CanonicalMIMEHeaderKey(key)
// Look for existing filter
// with key in filter slice.
for i := range *fs {
if (*fs)[i].key == key {
filter = &((*fs)[i])
break
}
}
if filter == nil {
// No existing filter found, create new.
// Append new header filter to slice.
(*fs) = append((*fs), headerfilter{})
// Then take ptr to this new filter
// at the last index in the slice.
filter = &((*fs)[len((*fs))-1])
// Setup new key.
filter.key = key
}
// Compile regular expression.
reg, err := regexp.Compile(expr)
if err != nil {
return fmt.Errorf("error compiling regexp %q: %w", expr, err)
}
// Append regular expression to filter.
filter.exprs = append(filter.exprs, reg)
return nil
}
// RegularMatch returns whether any values in http header
// matches any of the receiving filter regular expressions.
// This returns the matched header key, and matching regexp.
func (fs Filters) RegularMatch(h http.Header) (string, string, error) {
for _, filter := range fs {
for _, value := range h[filter.key] {
// Don't perform match on large values
// to mitigate denial of service attacks.
if len(value) > MaxHeaderValue {
return "", "", ErrLargeHeaderValue
}
// Compare against regular exprs.
for _, expr := range filter.exprs {
if expr.MatchString(value) {
return filter.key, expr.String(), nil
}
}
}
}
return "", "", nil
}
// InverseMatch returns whether any values in http header do
// NOT match any of the receiving filter regular expressions.
// This returns the matched header key, and matching regexp.
func (fs Filters) InverseMatch(h http.Header) (string, string, error) {
for _, filter := range fs {
for _, value := range h[filter.key] {
// Don't perform match on large values
// to mitigate denial of service attacks.
if len(value) > MaxHeaderValue {
return "", "", ErrLargeHeaderValue
}
// Compare against regular exprs.
for _, expr := range filter.exprs {
if !expr.MatchString(value) {
return filter.key, expr.String(), nil
}
}
}
}
return "", "", nil
}
|