Skip to content

Commit

Permalink
feat(logging): add a filter for query parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
dunglas committed Nov 17, 2021
1 parent e7457b4 commit 91f2c7f
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 0 deletions.
18 changes: 18 additions & 0 deletions caddytest/integration/caddyfile_adapt/log_filters.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ log {
format filter {
wrap console
fields {
uri query {
replace foo REDACTED
delete bar
}
request>headers>Authorization replace REDACTED
request>headers>Server delete
request>remote_addr ip_mask {
Expand Down Expand Up @@ -40,6 +44,20 @@ log {
"filter": "ip_mask",
"ipv4_cidr": 24,
"ipv6_cidr": 32
},
"uri": {
"actions": [
{
"parameter": "foo",
"type": "replace",
"value": "REDACTED"
},
{
"parameter": "bar",
"type": "delete"
}
],
"filter": "query"
}
},
"format": "filter",
Expand Down
94 changes: 94 additions & 0 deletions modules/logging/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package logging

import (
"net"
"net/url"
"strconv"

"github.com/caddyserver/caddy/v2"
Expand All @@ -27,6 +28,7 @@ func init() {
caddy.RegisterModule(DeleteFilter{})
caddy.RegisterModule(ReplaceFilter{})
caddy.RegisterModule(IPMaskFilter{})
caddy.RegisterModule(QueryFilter{})
}

// LogFieldFilter can filter (or manipulate)
Expand Down Expand Up @@ -185,15 +187,107 @@ func (m IPMaskFilter) Filter(in zapcore.Field) zapcore.Field {
return in
}

type ActionType string

const (
ReplaceType ActionType = "replace"
DeleteType ActionType = "delete"
)

type queryFilterAction struct {
Type ActionType `json:"type"`
Parameter string `json:"parameter"`
Value string `json:"value,omitempty"`
}

// QueryFilter is a Caddy log field filter that
// filters query parameters.
type QueryFilter struct {
Actions []queryFilterAction `json:"actions"`
}

// CaddyModule returns the Caddy module information.
func (QueryFilter) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "caddy.logging.encoders.filter.query",
New: func() caddy.Module { return new(QueryFilter) },
}
}

// UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (m *QueryFilter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() {
for d.NextBlock(0) {
qfa := queryFilterAction{}
switch d.Val() {
case "replace":
if !d.NextArg() {
return d.ArgErr()
}

qfa.Type = ReplaceType
qfa.Parameter = d.Val()

if !d.NextArg() {
return d.ArgErr()
}
qfa.Value = d.Val()

case "delete":
if !d.NextArg() {
return d.ArgErr()
}

qfa.Type = DeleteType
qfa.Parameter = d.Val()

default:
return d.Errf("unrecognized subdirective %s", d.Val())
}

m.Actions = append(m.Actions, qfa)
}
}
return nil
}

// Filter filters the input field.
func (m QueryFilter) Filter(in zapcore.Field) zapcore.Field {
u, err := url.Parse(in.String)
if err != nil {
return in
}

q := u.Query()
for _, a := range m.Actions {
switch a.Type {
case ReplaceType:
if q.Has(a.Parameter) {
q.Set(a.Parameter, a.Value)
}

case DeleteType:
q.Del(a.Parameter)
}
}

u.RawQuery = q.Encode()
in.String = u.String()

return in
}

// Interface guards
var (
_ LogFieldFilter = (*DeleteFilter)(nil)
_ LogFieldFilter = (*ReplaceFilter)(nil)
_ LogFieldFilter = (*IPMaskFilter)(nil)
_ LogFieldFilter = (*QueryFilter)(nil)

_ caddyfile.Unmarshaler = (*DeleteFilter)(nil)
_ caddyfile.Unmarshaler = (*ReplaceFilter)(nil)
_ caddyfile.Unmarshaler = (*IPMaskFilter)(nil)
_ caddyfile.Unmarshaler = (*QueryFilter)(nil)

_ caddy.Provisioner = (*IPMaskFilter)(nil)
)
19 changes: 19 additions & 0 deletions modules/logging/filters_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package logging

import (
"testing"

"go.uber.org/zap/zapcore"
)

func TestQueryFilter(t *testing.T) {
f := QueryFilter{[]queryFilterAction{
{ReplaceType, "foo", "REDACTED"},
{DeleteType, "bar", ""},
}}

out := f.Filter(zapcore.Field{String: "/path?foo=a&foo=b&bar=c&bar=d&baz=e"})
if out.String != "/path?baz=e&foo=REDACTED" {
t.Fatalf("query parameters have not been filtered: %s", out.String)
}
}

0 comments on commit 91f2c7f

Please sign in to comment.