1
1
package appsecacquisition
2
2
3
3
import (
4
- "encoding/json"
5
4
"fmt"
5
+ "net"
6
+ "slices"
7
+ "strconv"
6
8
"time"
7
9
8
10
"github.com/crowdsecurity/coraza/v3/collection"
9
11
"github.com/crowdsecurity/coraza/v3/types/variables"
12
+ "github.com/crowdsecurity/crowdsec/pkg/alertcontext"
10
13
"github.com/crowdsecurity/crowdsec/pkg/appsec"
14
+ "github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
11
15
"github.com/crowdsecurity/crowdsec/pkg/models"
12
16
"github.com/crowdsecurity/crowdsec/pkg/types"
13
17
"github.com/crowdsecurity/go-cs-lib/ptr"
18
+ "github.com/oschwald/geoip2-golang"
14
19
"github.com/prometheus/client_golang/prometheus"
15
20
log "github.com/sirupsen/logrus"
16
21
)
17
22
23
+ var appsecMetaKeys = []string {
24
+ "id" ,
25
+ "name" ,
26
+ "method" ,
27
+ "uri" ,
28
+ "matched_zones" ,
29
+ "msg" ,
30
+ }
31
+
32
+ func appendMeta (meta models.Meta , key string , value string ) models.Meta {
33
+ if value == "" {
34
+ return meta
35
+ }
36
+
37
+ meta = append (meta , & models.MetaItems0 {
38
+ Key : key ,
39
+ Value : value ,
40
+ })
41
+ return meta
42
+ }
43
+
18
44
func AppsecEventGeneration (inEvt types.Event ) (* types.Event , error ) {
19
45
//if the request didnd't trigger inband rules, we don't want to generate an event to LAPI/CAPI
20
46
if ! inEvt .Appsec .HasInBandMatches {
@@ -23,48 +49,127 @@ func AppsecEventGeneration(inEvt types.Event) (*types.Event, error) {
23
49
evt := types.Event {}
24
50
evt .Type = types .APPSEC
25
51
evt .Process = true
52
+ sourceIP := inEvt .Parsed ["source_ip" ]
26
53
source := models.Source {
27
- Value : ptr . Of ( inEvt . Parsed [ "source_ip" ]) ,
28
- IP : inEvt . Parsed [ "source_ip" ] ,
54
+ Value : & sourceIP ,
55
+ IP : sourceIP ,
29
56
Scope : ptr .Of (types .Ip ),
30
57
}
31
58
59
+ asndata , err := exprhelpers .GeoIPASNEnrich (sourceIP )
60
+
61
+ if err != nil {
62
+ log .Errorf ("Unable to enrich ip '%s' for ASN: %s" , sourceIP , err )
63
+ } else if asndata != nil {
64
+ record := asndata .(* geoip2.ASN )
65
+ source .AsName = record .AutonomousSystemOrganization
66
+ source .AsNumber = fmt .Sprintf ("%d" , record .AutonomousSystemNumber )
67
+ }
68
+
69
+ cityData , err := exprhelpers .GeoIPEnrich (sourceIP )
70
+ if err != nil {
71
+ log .Errorf ("Unable to enrich ip '%s' for geo data: %s" , sourceIP , err )
72
+ } else if cityData != nil {
73
+ record := cityData .(* geoip2.City )
74
+ source .Cn = record .Country .IsoCode
75
+ source .Latitude = float32 (record .Location .Latitude )
76
+ source .Longitude = float32 (record .Location .Longitude )
77
+ }
78
+
79
+ rangeData , err := exprhelpers .GeoIPRangeEnrich (sourceIP )
80
+ if err != nil {
81
+ log .Errorf ("Unable to enrich ip '%s' for range: %s" , sourceIP , err )
82
+ } else if rangeData != nil {
83
+ record := rangeData .(* net.IPNet )
84
+ source .Range = record .String ()
85
+ }
86
+
32
87
evt .Overflow .Sources = make (map [string ]models.Source )
33
- evt .Overflow .Sources ["ip" ] = source
88
+ evt .Overflow .Sources [sourceIP ] = source
34
89
35
90
alert := models.Alert {}
36
91
alert .Capacity = ptr .Of (int32 (1 ))
37
- alert .Events = make ([]* models.Event , 0 )
38
- alert .Meta = make (models.Meta , 0 )
39
- for _ , key := range []string {"target_uri" , "method" } {
92
+ alert .Events = make ([]* models.Event , len (evt .Appsec .GetRuleIDs ()))
40
93
41
- valueByte , err := json .Marshal ([]string {inEvt .Parsed [key ]})
42
- if err != nil {
43
- log .Debugf ("unable to serialize key %s" , key )
94
+ now := ptr .Of (time .Now ().UTC ().Format (time .RFC3339 ))
95
+
96
+ tmpAppsecContext := make (map [string ][]string )
97
+
98
+ for _ , matched_rule := range inEvt .Appsec .MatchedRules {
99
+ evtRule := models.Event {}
100
+
101
+ evtRule .Timestamp = now
102
+
103
+ evtRule .Meta = make (models.Meta , 0 )
104
+
105
+ for _ , key := range appsecMetaKeys {
106
+
107
+ if tmpAppsecContext [key ] == nil {
108
+ tmpAppsecContext [key ] = make ([]string , 0 )
109
+ }
110
+
111
+ switch value := matched_rule [key ].(type ) {
112
+ case string :
113
+ evtRule .Meta = appendMeta (evtRule .Meta , key , value )
114
+ if value != "" && ! slices .Contains (tmpAppsecContext [key ], value ) {
115
+ tmpAppsecContext [key ] = append (tmpAppsecContext [key ], value )
116
+ }
117
+ case int :
118
+ val := strconv .Itoa (value )
119
+ evtRule .Meta = appendMeta (evtRule .Meta , key , val )
120
+ if val != "" && ! slices .Contains (tmpAppsecContext [key ], val ) {
121
+ tmpAppsecContext [key ] = append (tmpAppsecContext [key ], val )
122
+ }
123
+ case []string :
124
+ for _ , v := range value {
125
+ evtRule .Meta = appendMeta (evtRule .Meta , key , v )
126
+ if v != "" && ! slices .Contains (tmpAppsecContext [key ], v ) {
127
+ tmpAppsecContext [key ] = append (tmpAppsecContext [key ], v )
128
+ }
129
+ }
130
+ case []int :
131
+ for _ , v := range value {
132
+ val := strconv .Itoa (v )
133
+ evtRule .Meta = appendMeta (evtRule .Meta , key , val )
134
+ if val != "" && ! slices .Contains (tmpAppsecContext [key ], val ) {
135
+ tmpAppsecContext [key ] = append (tmpAppsecContext [key ], val )
136
+ }
137
+
138
+ }
139
+ default :
140
+ val := fmt .Sprintf ("%v" , value )
141
+ evtRule .Meta = appendMeta (evtRule .Meta , key , val )
142
+ if val != "" && ! slices .Contains (tmpAppsecContext [key ], val ) {
143
+ tmpAppsecContext [key ] = append (tmpAppsecContext [key ], val )
144
+ }
145
+
146
+ }
147
+ }
148
+ alert .Events = append (alert .Events , & evtRule )
149
+ }
150
+
151
+ metas := make ([]* models.MetaItems0 , 0 )
152
+
153
+ for key , values := range tmpAppsecContext {
154
+ if len (values ) == 0 {
44
155
continue
45
156
}
46
157
158
+ valueStr , err := alertcontext .TruncateContext (values , alertcontext .MaxContextValueLen )
159
+ if err != nil {
160
+ log .Warningf (err .Error ())
161
+ }
162
+
47
163
meta := models.MetaItems0 {
48
164
Key : key ,
49
- Value : string (valueByte ),
50
- }
51
- alert .Meta = append (alert .Meta , & meta )
52
- }
53
- matchedZones := inEvt .Appsec .GetMatchedZones ()
54
- if matchedZones != nil {
55
- valueByte , err := json .Marshal (matchedZones )
56
- if err != nil {
57
- log .Debugf ("unable to serialize key matched_zones" )
58
- } else {
59
- meta := models.MetaItems0 {
60
- Key : "matched_zones" ,
61
- Value : string (valueByte ),
62
- }
63
- alert .Meta = append (alert .Meta , & meta )
165
+ Value : valueStr ,
64
166
}
167
+ metas = append (metas , & meta )
65
168
}
66
169
67
- alert .EventsCount = ptr .Of (int32 (1 ))
170
+ alert .Meta = metas
171
+
172
+ alert .EventsCount = ptr .Of (int32 (len (alert .Events )))
68
173
alert .Leakspeed = ptr .Of ("" )
69
174
alert .Scenario = ptr .Of (inEvt .Appsec .MatchedRules .GetName ())
70
175
alert .ScenarioHash = ptr .Of (inEvt .Appsec .MatchedRules .GetHash ())
@@ -200,7 +305,7 @@ func (r *AppsecRunner) AccumulateTxToEvent(evt *types.Event, req *appsec.ParsedR
200
305
})
201
306
202
307
for _ , rule := range req .Tx .MatchedRules () {
203
- if rule .Message () == "" || rule . DisruptiveAction () == "pass" || rule . DisruptiveAction () == "allow" {
308
+ if rule .Message () == "" {
204
309
r .logger .Tracef ("discarding rule %d (action: %s)" , rule .Rule ().ID (), rule .DisruptiveAction ())
205
310
continue
206
311
}
@@ -242,7 +347,7 @@ func (r *AppsecRunner) AccumulateTxToEvent(evt *types.Event, req *appsec.ParsedR
242
347
243
348
corazaRule := map [string ]interface {}{
244
349
"id" : rule .Rule ().ID (),
245
- "uri" : evt .Parsed ["uri " ],
350
+ "uri" : evt .Parsed ["target_uri " ],
246
351
"rule_type" : kind ,
247
352
"method" : evt .Parsed ["method" ],
248
353
"disruptive" : rule .Disruptive (),
0 commit comments