Skip to content

Commit 42a2f9c

Browse files
committed
logging: fix multiple regexp filters on same field (fixes #7049)
1 parent 0f209f6 commit 42a2f9c

File tree

4 files changed

+523
-0
lines changed

4 files changed

+523
-0
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
:80
2+
3+
log {
4+
output stdout
5+
format filter {
6+
wrap console
7+
8+
# Multiple regexp filters for the same field - this should work now!
9+
request>headers>Authorization regexp "Bearer\s+([A-Za-z0-9_-]+)" "Bearer [REDACTED]"
10+
request>headers>Authorization regexp "Basic\s+([A-Za-z0-9+/=]+)" "Basic [REDACTED]"
11+
request>headers>Authorization regexp "token=([^&\s]+)" "token=[REDACTED]"
12+
13+
# Single regexp filter - this should continue to work as before
14+
request>headers>Cookie regexp "sessionid=[^;]+" "sessionid=[REDACTED]"
15+
16+
# Mixed filters (non-regexp) - these should work normally
17+
request>headers>Server delete
18+
request>remote_ip ip_mask {
19+
ipv4 24
20+
ipv6 32
21+
}
22+
}
23+
}
24+
----------
25+
{
26+
"logging": {
27+
"logs": {
28+
"default": {
29+
"exclude": [
30+
"http.log.access.log0"
31+
]
32+
},
33+
"log0": {
34+
"writer": {
35+
"output": "stdout"
36+
},
37+
"encoder": {
38+
"fields": {
39+
"request\u003eheaders\u003eAuthorization": {
40+
"filter": "multi_regexp",
41+
"operations": [
42+
{
43+
"regexp": "Bearer\\s+([A-Za-z0-9_-]+)",
44+
"value": "Bearer [REDACTED]"
45+
},
46+
{
47+
"regexp": "Basic\\s+([A-Za-z0-9+/=]+)",
48+
"value": "Basic [REDACTED]"
49+
},
50+
{
51+
"regexp": "token=([^&\\s]+)",
52+
"value": "token=[REDACTED]"
53+
}
54+
]
55+
},
56+
"request\u003eheaders\u003eCookie": {
57+
"filter": "regexp",
58+
"regexp": "sessionid=[^;]+",
59+
"value": "sessionid=[REDACTED]"
60+
},
61+
"request\u003eheaders\u003eServer": {
62+
"filter": "delete"
63+
},
64+
"request\u003eremote_ip": {
65+
"filter": "ip_mask",
66+
"ipv4_cidr": 24,
67+
"ipv6_cidr": 32
68+
}
69+
},
70+
"format": "filter",
71+
"wrap": {
72+
"format": "console"
73+
}
74+
},
75+
"include": [
76+
"http.log.access.log0"
77+
]
78+
}
79+
}
80+
},
81+
"apps": {
82+
"http": {
83+
"servers": {
84+
"srv0": {
85+
"listen": [
86+
":80"
87+
],
88+
"logs": {
89+
"default_logger_name": "log0"
90+
}
91+
}
92+
}
93+
}
94+
}
95+
}

modules/logging/filterencoder.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ func (fe *FilterEncoder) ConfigureDefaultFormat(wo caddy.WriterOpener) error {
152152
func (fe *FilterEncoder) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
153153
d.Next() // consume encoder name
154154

155+
// Track regexp filters for automatic merging
156+
regexpFilters := make(map[string][]*RegexpFilter)
157+
155158
// parse a field
156159
parseField := func() error {
157160
if fe.FieldsRaw == nil {
@@ -171,6 +174,23 @@ func (fe *FilterEncoder) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
171174
if !ok {
172175
return d.Errf("module %s (%T) is not a logging.LogFieldFilter", moduleID, unm)
173176
}
177+
178+
// Special handling for regexp filters to support multiple instances
179+
if regexpFilter, isRegexp := filter.(*RegexpFilter); isRegexp {
180+
regexpFilters[field] = append(regexpFilters[field], regexpFilter)
181+
return nil // Don't set FieldsRaw yet, we'll merge them later
182+
}
183+
184+
// Check if we're trying to add a non-regexp filter to a field that already has regexp filters
185+
if _, hasRegexpFilters := regexpFilters[field]; hasRegexpFilters {
186+
return d.Errf("cannot mix regexp filters with other filter types for field %s", field)
187+
}
188+
189+
// Check if field already has a filter and it's not regexp-related
190+
if _, exists := fe.FieldsRaw[field]; exists {
191+
return d.Errf("field %s already has a filter; multiple non-regexp filters per field are not supported", field)
192+
}
193+
174194
fe.FieldsRaw[field] = caddyconfig.JSONModuleObject(filter, "filter", filterName, nil)
175195
return nil
176196
}
@@ -210,6 +230,25 @@ func (fe *FilterEncoder) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
210230
}
211231
}
212232
}
233+
234+
// After parsing all fields, merge multiple regexp filters into MultiRegexpFilter
235+
for field, filters := range regexpFilters {
236+
if len(filters) == 1 {
237+
// Single regexp filter, use the original RegexpFilter
238+
fe.FieldsRaw[field] = caddyconfig.JSONModuleObject(filters[0], "filter", "regexp", nil)
239+
} else {
240+
// Multiple regexp filters, merge into MultiRegexpFilter
241+
multiFilter := &MultiRegexpFilter{}
242+
for _, regexpFilter := range filters {
243+
err := multiFilter.AddOperation(regexpFilter.RawRegexp, regexpFilter.Value)
244+
if err != nil {
245+
return fmt.Errorf("adding regexp operation for field %s: %v", field, err)
246+
}
247+
}
248+
fe.FieldsRaw[field] = caddyconfig.JSONModuleObject(multiFilter, "filter", "multi_regexp", nil)
249+
}
250+
}
251+
213252
return nil
214253
}
215254

0 commit comments

Comments
 (0)