Skip to content

Commit

Permalink
Alert context appsec (#3288)
Browse files Browse the repository at this point in the history
  • Loading branch information
buixor authored Nov 4, 2024
1 parent 0940724 commit 1616991
Show file tree
Hide file tree
Showing 6 changed files with 337 additions and 163 deletions.
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 @@ func (r *AppsecRunner) handleInBandInterrupt(request *appsec.ParsedRequest) {

// 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 @@ func (r *AppsecRunner) handleOutBandInterrupt(request *appsec.ParsedRequest) {

// Should the 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
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 @@ import (
"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")
}

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

if err != nil {
return err
} else if asndata != nil {
record := asndata.(*geoip2.ASN)
src.AsName = record.AutonomousSystemOrganization
src.AsNumber = fmt.Sprintf("%d", record.AutonomousSystemNumber)
}

return meta
cityData, err := exprhelpers.GeoIPEnrich(src.IP)
if err != nil {
return err
} 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)
}

rangeData, err := exprhelpers.GeoIPRangeEnrich(src.IP)
if err != nil {
return err
} else if rangeData != nil {
record := rangeData.(*net.IPNet)
src.Range = record.String()
}
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 @@ func AppsecEventGeneration(inEvt types.Event) (*types.Event, error) {
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)
}

// 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)
}
metas = append(metas, &meta)
}

alert.Meta = metas
Expand Down
Loading

0 comments on commit 1616991

Please sign in to comment.