Skip to content

Commit f4a6958

Browse files
authored
feat: decode all fields from WAF diagnostics (#43)
1 parent 643bed9 commit f4a6958

File tree

4 files changed

+168
-139
lines changed

4 files changed

+168
-139
lines changed

decoder.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,20 @@ func decodeDiagnostics(obj *wafObject) (*Diagnostics, error) {
4949
objElem := castWithOffset[wafObject](obj.value, i)
5050
key := gostringSized(cast[byte](objElem.parameterName), objElem.parameterNameLength)
5151
switch key {
52+
case "custom_rules":
53+
diags.CustomRules, err = decodeDiagnosticsEntry(objElem)
54+
case "exclusions":
55+
diags.Exclusions, err = decodeDiagnosticsEntry(objElem)
5256
case "rules":
5357
diags.Rules, err = decodeDiagnosticsEntry(objElem)
58+
case "rules_data":
59+
diags.RulesData, err = decodeDiagnosticsEntry(objElem)
60+
case "rules_override":
61+
diags.RulesOverrides, err = decodeDiagnosticsEntry(objElem)
62+
case "processors":
63+
diags.Processors, err = decodeDiagnosticsEntry(objElem)
64+
case "scanners":
65+
diags.Scanners, err = decodeDiagnosticsEntry(objElem)
5466
case "ruleset_version":
5567
diags.Version = gostringSized(cast[byte](objElem.value), objElem.nbEntries)
5668
default:
@@ -80,6 +92,8 @@ func decodeDiagnosticsEntry(obj *wafObject) (*DiagnosticEntry, error) {
8092
switch key {
8193
case "addresses":
8294
entry.Addresses, err = decodeDiagnosticAddresses(objElem)
95+
case "error":
96+
entry.Error = gostringSized(cast[byte](objElem.value), objElem.nbEntries)
8397
case "errors":
8498
entry.Errors, err = decodeErrors(objElem)
8599
case "failed":

decoder_test.go

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed
2+
// under the Apache License Version 2.0.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
// Copyright 2016-present Datadog, Inc.
5+
6+
package waf
7+
8+
import (
9+
"reflect"
10+
"testing"
11+
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
// This test needs a working encoder to function properly, as it first encodes the objects before decoding them
16+
func TestDecoder(t *testing.T) {
17+
t.Run("Errors", func(t *testing.T) {
18+
e := newMaxEncoder()
19+
objBuilder := func(value any) *wafObject {
20+
encoded, err := e.Encode(value)
21+
require.NoError(t, err, "Encoding object failed")
22+
return encoded
23+
}
24+
25+
for _, tc := range []struct {
26+
Name string
27+
ExpectedValue map[string][]string
28+
}{
29+
{
30+
Name: "empty",
31+
ExpectedValue: map[string][]string{},
32+
},
33+
{
34+
Name: "one-empty-entry",
35+
ExpectedValue: map[string][]string{
36+
"afasdfafs": nil,
37+
},
38+
},
39+
{
40+
Name: "one-filled-entry",
41+
ExpectedValue: map[string][]string{
42+
"afasdfafs": {"rule1", "rule2"},
43+
},
44+
},
45+
{
46+
Name: "multiple-entries",
47+
ExpectedValue: map[string][]string{
48+
"afasdfafs": {"rule1", "rule2"},
49+
"sfdsafdsa": {"rule3", "rule4"},
50+
},
51+
},
52+
} {
53+
t.Run(tc.Name, func(t *testing.T) {
54+
val, err := decodeErrors(objBuilder(tc.ExpectedValue))
55+
require.NoErrorf(t, err, "Error decoding the object: %v", err)
56+
require.Equal(t, tc.ExpectedValue, val)
57+
})
58+
}
59+
60+
keepAlive(e.cgoRefs.arrayRefs)
61+
keepAlive(e.cgoRefs.stringRefs)
62+
})
63+
t.Run("Values", func(t *testing.T) {
64+
65+
for _, tc := range []struct {
66+
name string
67+
data any
68+
}{
69+
{
70+
name: "int64",
71+
data: int64(-4),
72+
},
73+
{
74+
name: "uint64",
75+
data: uint64(4),
76+
},
77+
{
78+
name: "float64",
79+
data: 4.4444,
80+
},
81+
{
82+
name: "bool",
83+
data: true,
84+
},
85+
{
86+
name: "bool",
87+
data: false,
88+
},
89+
{
90+
name: "string",
91+
data: "",
92+
},
93+
{
94+
name: "string",
95+
data: "EliottAndFrancoisLoveDecoding",
96+
},
97+
{
98+
name: "string",
99+
data: "123",
100+
},
101+
{
102+
name: "slice",
103+
data: []any{int64(1), int64(2), int64(3), int64(4)},
104+
},
105+
{
106+
name: "slice",
107+
data: []any{"1", "2", "3", "4"},
108+
},
109+
{
110+
name: "slice",
111+
data: []any{true, false, false, true, true, true},
112+
},
113+
{
114+
name: "slice-nested",
115+
data: []any{[]any{true, false}, []any{false, false}, []any{true, true, true}},
116+
},
117+
{
118+
name: "map",
119+
data: map[string]any{"1": int64(1), "2": int64(2), "3": int64(3)},
120+
},
121+
{
122+
name: "map",
123+
data: map[string]any{"1": uint64(1), "2": uint64(2), "3": uint64(3)},
124+
},
125+
{
126+
name: "map",
127+
data: map[string]any{"1": 1.111, "2": 2.222, "3": 3.333},
128+
},
129+
{
130+
name: "map",
131+
data: map[string]any{"1": "1", "2": "2", "3": "3"},
132+
},
133+
{
134+
name: "map-nested",
135+
data: map[string]any{"1": map[string]any{"1": int64(1), "2": int64(2)}, "2": map[string]any{"3": int64(3), "4": int64(4)}},
136+
},
137+
} {
138+
t.Run(tc.name, func(t *testing.T) {
139+
e := newMaxEncoder()
140+
obj, err := e.Encode(tc.data)
141+
require.NoError(t, err)
142+
143+
val, err := decodeObject(obj)
144+
require.NoError(t, err)
145+
require.True(t, reflect.DeepEqual(tc.data, val))
146+
})
147+
}
148+
149+
})
150+
}

waf.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ type Diagnostics struct {
3131
Exclusions *DiagnosticEntry
3232
RulesOverrides *DiagnosticEntry
3333
RulesData *DiagnosticEntry
34+
Processors *DiagnosticEntry
35+
Scanners *DiagnosticEntry
3436
}
3537

3638
// DiagnosticEntry stores the information - provided by the WAF - about loaded and failed rules
@@ -39,7 +41,8 @@ type DiagnosticEntry struct {
3941
Addresses DiagnosticAddresses
4042
Loaded []string
4143
Failed []string
42-
Errors map[string][]string
44+
Error string // If the entire entry was in error (e.g: invalid format)
45+
Errors map[string][]string // Item-level errors
4346
}
4447

4548
// DiagnosticAddresses stores the information - provided by the WAF - about the known addresses and

waf_test.go

Lines changed: 0 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
"encoding/json"
1414
"fmt"
1515
"math/rand"
16-
"reflect"
1716
"sort"
1817
"sync"
1918
"testing"
@@ -907,143 +906,6 @@ func TestMetrics(t *testing.T) {
907906
})
908907
}
909908

910-
// This test needs a working encoder to function properly, as it first encodes the objects before decoding them
911-
func TestDecoder(t *testing.T) {
912-
t.Run("Errors", func(t *testing.T) {
913-
e := newMaxEncoder()
914-
objBuilder := func(value any) *wafObject {
915-
encoded, err := e.Encode(value)
916-
require.NoError(t, err, "Encoding object failed")
917-
return encoded
918-
}
919-
920-
for _, tc := range []struct {
921-
Name string
922-
ExpectedValue map[string][]string
923-
}{
924-
{
925-
Name: "empty",
926-
ExpectedValue: map[string][]string{},
927-
},
928-
{
929-
Name: "one-empty-entry",
930-
ExpectedValue: map[string][]string{
931-
"afasdfafs": nil,
932-
},
933-
},
934-
{
935-
Name: "one-filled-entry",
936-
ExpectedValue: map[string][]string{
937-
"afasdfafs": {"rule1", "rule2"},
938-
},
939-
},
940-
{
941-
Name: "multiple-entries",
942-
ExpectedValue: map[string][]string{
943-
"afasdfafs": {"rule1", "rule2"},
944-
"sfdsafdsa": {"rule3", "rule4"},
945-
},
946-
},
947-
} {
948-
t.Run(tc.Name, func(t *testing.T) {
949-
val, err := decodeErrors(objBuilder(tc.ExpectedValue))
950-
require.NoErrorf(t, err, "Error decoding the object: %v", err)
951-
require.Equal(t, tc.ExpectedValue, val)
952-
})
953-
}
954-
955-
keepAlive(e.cgoRefs.arrayRefs)
956-
keepAlive(e.cgoRefs.stringRefs)
957-
})
958-
t.Run("Values", func(t *testing.T) {
959-
960-
for _, tc := range []struct {
961-
name string
962-
data any
963-
}{
964-
{
965-
name: "int64",
966-
data: int64(-4),
967-
},
968-
{
969-
name: "uint64",
970-
data: uint64(4),
971-
},
972-
{
973-
name: "float64",
974-
data: 4.4444,
975-
},
976-
{
977-
name: "bool",
978-
data: true,
979-
},
980-
{
981-
name: "bool",
982-
data: false,
983-
},
984-
{
985-
name: "string",
986-
data: "",
987-
},
988-
{
989-
name: "string",
990-
data: "EliottAndFrancoisLoveDecoding",
991-
},
992-
{
993-
name: "string",
994-
data: "123",
995-
},
996-
{
997-
name: "slice",
998-
data: []any{int64(1), int64(2), int64(3), int64(4)},
999-
},
1000-
{
1001-
name: "slice",
1002-
data: []any{"1", "2", "3", "4"},
1003-
},
1004-
{
1005-
name: "slice",
1006-
data: []any{true, false, false, true, true, true},
1007-
},
1008-
{
1009-
name: "slice-nested",
1010-
data: []any{[]any{true, false}, []any{false, false}, []any{true, true, true}},
1011-
},
1012-
{
1013-
name: "map",
1014-
data: map[string]any{"1": int64(1), "2": int64(2), "3": int64(3)},
1015-
},
1016-
{
1017-
name: "map",
1018-
data: map[string]any{"1": uint64(1), "2": uint64(2), "3": uint64(3)},
1019-
},
1020-
{
1021-
name: "map",
1022-
data: map[string]any{"1": 1.111, "2": 2.222, "3": 3.333},
1023-
},
1024-
{
1025-
name: "map",
1026-
data: map[string]any{"1": "1", "2": "2", "3": "3"},
1027-
},
1028-
{
1029-
name: "map-nested",
1030-
data: map[string]any{"1": map[string]any{"1": int64(1), "2": int64(2)}, "2": map[string]any{"3": int64(3), "4": int64(4)}},
1031-
},
1032-
} {
1033-
t.Run(tc.name, func(t *testing.T) {
1034-
e := newMaxEncoder()
1035-
obj, err := e.Encode(tc.data)
1036-
require.NoError(t, err)
1037-
1038-
val, err := decodeObject(obj)
1039-
require.NoError(t, err)
1040-
require.True(t, reflect.DeepEqual(tc.data, val))
1041-
})
1042-
}
1043-
1044-
})
1045-
}
1046-
1047909
func TestObfuscatorConfig(t *testing.T) {
1048910
rule := newArachniTestRule([]ruleInput{{Address: "my.addr", KeyPath: []string{"key"}}}, nil)
1049911
t.Run("key", func(t *testing.T) {

0 commit comments

Comments
 (0)