Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alert context appsec #3288

Merged
merged 14 commits into from
Nov 4, 2024
4 changes: 2 additions & 2 deletions pkg/acquisition/modules/appsec/appsec_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@

// Should the in band match trigger an overflow ?
if r.AppsecRuntime.Response.SendAlert {
appsecOvlfw, err := AppsecEventGeneration(evt)
appsecOvlfw, err := AppsecEventGeneration(evt, request.HTTPRequest)
if err != nil {
r.logger.Errorf("unable to generate appsec event : %s", err)
return
Expand Down Expand Up @@ -293,7 +293,7 @@

// Should the match trigger an overflow ?
if r.AppsecRuntime.Response.SendAlert {
appsecOvlfw, err := AppsecEventGeneration(evt)
appsecOvlfw, err := AppsecEventGeneration(evt, request.HTTPRequest)

Check warning on line 296 in pkg/acquisition/modules/appsec/appsec_runner.go

View check run for this annotation

Codecov / codecov/patch

pkg/acquisition/modules/appsec/appsec_runner.go#L296

Added line #L296 was not covered by tests
if err != nil {
r.logger.Errorf("unable to generate appsec event : %s", err)
return
Expand Down
163 changes: 42 additions & 121 deletions pkg/acquisition/modules/appsec/utils.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package appsecacquisition

import (
"errors"
"fmt"
"net"
"slices"
"strconv"
"net/http"
"time"

"github.com/oschwald/geoip2-golang"
Expand All @@ -22,29 +22,44 @@
"github.com/crowdsecurity/crowdsec/pkg/types"
)

var appsecMetaKeys = []string{
"id",
"name",
"method",
"uri",
"matched_zones",
"msg",
}
func AppsecEventGenerationGeoIPEnrich(src *models.Source) error {

func appendMeta(meta models.Meta, key string, value string) models.Meta {
if value == "" {
return meta
if src == nil || src.Scope == nil || *src.Scope != types.Ip {
return errors.New("source is nil or not an IP")

Check warning on line 28 in pkg/acquisition/modules/appsec/utils.go

View check run for this annotation

Codecov / codecov/patch

pkg/acquisition/modules/appsec/utils.go#L28

Added line #L28 was not covered by tests
}

meta = append(meta, &models.MetaItems0{
Key: key,
Value: value,
})
//GeoIP enrich
asndata, err := exprhelpers.GeoIPASNEnrich(src.IP)

if err != nil {
return err

Check warning on line 35 in pkg/acquisition/modules/appsec/utils.go

View check run for this annotation

Codecov / codecov/patch

pkg/acquisition/modules/appsec/utils.go#L35

Added line #L35 was not covered by tests
} else if asndata != nil {
record := asndata.(*geoip2.ASN)
src.AsName = record.AutonomousSystemOrganization
src.AsNumber = fmt.Sprintf("%d", record.AutonomousSystemNumber)
}

Check warning on line 40 in pkg/acquisition/modules/appsec/utils.go

View check run for this annotation

Codecov / codecov/patch

pkg/acquisition/modules/appsec/utils.go#L37-L40

Added lines #L37 - L40 were not covered by tests

return meta
cityData, err := exprhelpers.GeoIPEnrich(src.IP)
if err != nil {
return err

Check warning on line 44 in pkg/acquisition/modules/appsec/utils.go

View check run for this annotation

Codecov / codecov/patch

pkg/acquisition/modules/appsec/utils.go#L44

Added line #L44 was not covered by tests
} else if cityData != nil {
record := cityData.(*geoip2.City)
src.Cn = record.Country.IsoCode
src.Latitude = float32(record.Location.Latitude)
src.Longitude = float32(record.Location.Longitude)
}

Check warning on line 50 in pkg/acquisition/modules/appsec/utils.go

View check run for this annotation

Codecov / codecov/patch

pkg/acquisition/modules/appsec/utils.go#L46-L50

Added lines #L46 - L50 were not covered by tests

rangeData, err := exprhelpers.GeoIPRangeEnrich(src.IP)
if err != nil {
return err

Check warning on line 54 in pkg/acquisition/modules/appsec/utils.go

View check run for this annotation

Codecov / codecov/patch

pkg/acquisition/modules/appsec/utils.go#L54

Added line #L54 was not covered by tests
} else if rangeData != nil {
record := rangeData.(*net.IPNet)
src.Range = record.String()
}

Check warning on line 58 in pkg/acquisition/modules/appsec/utils.go

View check run for this annotation

Codecov / codecov/patch

pkg/acquisition/modules/appsec/utils.go#L56-L58

Added lines #L56 - L58 were not covered by tests
return nil
}

func AppsecEventGeneration(inEvt types.Event) (*types.Event, error) {
func AppsecEventGeneration(inEvt types.Event, request *http.Request) (*types.Event, error) {
// if the request didnd't trigger inband rules, we don't want to generate an event to LAPI/CAPI
if !inEvt.Appsec.HasInBandMatches {
return nil, nil
Expand All @@ -60,118 +75,24 @@
Scope: ptr.Of(types.Ip),
}

asndata, err := exprhelpers.GeoIPASNEnrich(sourceIP)

if err != nil {
log.Errorf("Unable to enrich ip '%s' for ASN: %s", sourceIP, err)
} else if asndata != nil {
record := asndata.(*geoip2.ASN)
source.AsName = record.AutonomousSystemOrganization
source.AsNumber = fmt.Sprintf("%d", record.AutonomousSystemNumber)
}

cityData, err := exprhelpers.GeoIPEnrich(sourceIP)
if err != nil {
log.Errorf("Unable to enrich ip '%s' for geo data: %s", sourceIP, err)
} else if cityData != nil {
record := cityData.(*geoip2.City)
source.Cn = record.Country.IsoCode
source.Latitude = float32(record.Location.Latitude)
source.Longitude = float32(record.Location.Longitude)
}

rangeData, err := exprhelpers.GeoIPRangeEnrich(sourceIP)
if err != nil {
log.Errorf("Unable to enrich ip '%s' for range: %s", sourceIP, err)
} else if rangeData != nil {
record := rangeData.(*net.IPNet)
source.Range = record.String()
// Enrich source with GeoIP data
if err := AppsecEventGenerationGeoIPEnrich(&source); err != nil {
log.Errorf("unable to enrich source with GeoIP data : %s", err)

Check warning on line 80 in pkg/acquisition/modules/appsec/utils.go

View check run for this annotation

Codecov / codecov/patch

pkg/acquisition/modules/appsec/utils.go#L80

Added line #L80 was not covered by tests
}

// Build overflow
evt.Overflow.Sources = make(map[string]models.Source)
evt.Overflow.Sources[sourceIP] = source

alert := models.Alert{}
alert.Capacity = ptr.Of(int32(1))
alert.Events = make([]*models.Event, len(evt.Appsec.GetRuleIDs()))

now := ptr.Of(time.Now().UTC().Format(time.RFC3339))

tmpAppsecContext := make(map[string][]string)

for _, matched_rule := range inEvt.Appsec.MatchedRules {
evtRule := models.Event{}

evtRule.Timestamp = now

evtRule.Meta = make(models.Meta, 0)

for _, key := range appsecMetaKeys {
if tmpAppsecContext[key] == nil {
tmpAppsecContext[key] = make([]string, 0)
}

switch value := matched_rule[key].(type) {
case string:
evtRule.Meta = appendMeta(evtRule.Meta, key, value)

if value != "" && !slices.Contains(tmpAppsecContext[key], value) {
tmpAppsecContext[key] = append(tmpAppsecContext[key], value)
}
case int:
val := strconv.Itoa(value)
evtRule.Meta = appendMeta(evtRule.Meta, key, val)

if val != "" && !slices.Contains(tmpAppsecContext[key], val) {
tmpAppsecContext[key] = append(tmpAppsecContext[key], val)
}
case []string:
for _, v := range value {
evtRule.Meta = appendMeta(evtRule.Meta, key, v)

if v != "" && !slices.Contains(tmpAppsecContext[key], v) {
tmpAppsecContext[key] = append(tmpAppsecContext[key], v)
}
}
case []int:
for _, v := range value {
val := strconv.Itoa(v)
evtRule.Meta = appendMeta(evtRule.Meta, key, val)

if val != "" && !slices.Contains(tmpAppsecContext[key], val) {
tmpAppsecContext[key] = append(tmpAppsecContext[key], val)
}
}
default:
val := fmt.Sprintf("%v", value)
evtRule.Meta = appendMeta(evtRule.Meta, key, val)

if val != "" && !slices.Contains(tmpAppsecContext[key], val) {
tmpAppsecContext[key] = append(tmpAppsecContext[key], val)
}
}
}

alert.Events = append(alert.Events, &evtRule)
}

metas := make([]*models.MetaItems0, 0)

for key, values := range tmpAppsecContext {
if len(values) == 0 {
continue
}

valueStr, err := alertcontext.TruncateContext(values, alertcontext.MaxContextValueLen)
if err != nil {
log.Warning(err.Error())
}

meta := models.MetaItems0{
Key: key,
Value: valueStr,
metas, errors := alertcontext.AppsecEventToContext(inEvt.Appsec, request)
if len(errors) > 0 {
for _, err := range errors {
log.Errorf("failed to generate appsec context: %s", err)

Check warning on line 94 in pkg/acquisition/modules/appsec/utils.go

View check run for this annotation

Codecov / codecov/patch

pkg/acquisition/modules/appsec/utils.go#L93-L94

Added lines #L93 - L94 were not covered by tests
}
metas = append(metas, &meta)
}

alert.Meta = metas
Expand Down
Loading