# Other helpers

## Time Helpers[​](#time-helpers "Direct link to Time Helpers")

### `TimeNow() string`[​](#timenow-string "Direct link to timenow-string")

Return RFC3339 formatted time

> `TimeNow()`

### `ParseUnix(unix string) string`[​](#parseunixunix-string-string "Direct link to parseunixunix-string-string")

TEXTCOPY

```
ParseUnix("1672239773.3590894") -> "2022-12-28T15:02:53Z"
ParseUnix("1672239773") -> "2022-12-28T15:02:53Z"
ParseUnix("notatimestamp") -> ""
```

Parses unix timestamp string and returns RFC3339 formatted time

### `AverageInterval(timestamps []time.Time) time.Duration`[​](#averageintervaltimestamps-timetime-timeduration "Direct link to averageintervaltimestamps-timetime-timeduration")

Calculates the average interval (time duration) between consecutive timestamps in a slice.

**Use case:** Detecting consistent timing patterns over time, such as slow brute-force attacks or rate anomalies.

**Example:**

YAMLCOPY

```
type: conditional
name: me/slow-http
debug: true
description: "Detect slow HTTP requests returning 404"
filter: "evt.Meta.log_type in ['http_access-log', 'http_error-log'] && evt.Parsed.static_ressource == 'false' && evt.Parsed.verb in ['GET', 'HEAD']"
groupby: "evt.Meta.source_ip + '/' + evt.Parsed.target_fqdn"
capacity: -1
condition: |
    len(queue.Queue) >= 3 &&
    AverageInterval(map(queue.Queue[-3:], { #.Time })) > duration("5m") &&
    all(queue.Queue, #.Meta.http_status == '404')
leakspeed: 1h
labels:
  remediation: true
```

In this example, we check if the queue has at least 3 items, compute the average interval between the last 3 requests, and trigger if the average time between requests exceeds 5 minutes and all responses are 404s (indicating a slow scan).

**Notes:**

* Timestamps are automatically sorted internally for correctness
* Requires at least two timestamps
* Useful for detecting consistent behavior patterns over time

### `MedianInterval(timestamps []time.Time) time.Duration`[​](#medianintervaltimestamps-timetime-timeduration "Direct link to medianintervaltimestamps-timetime-timeduration")

Calculates the median interval (time duration) between consecutive timestamps in a slice.

**Use case:** Detecting typical timing patterns when intervals vary widely. The median is more robust against outliers than the average, making it ideal for identifying timing anomalies in irregular patterns.

**Example:**

YAMLCOPY

```
type: conditional
name: me/slow-http-median
debug: true
description: "Detect slow HTTP requests returning 404"
filter: "evt.Meta.log_type in ['http_access-log', 'http_error-log'] && evt.Parsed.static_ressource == 'false' && evt.Parsed.verb in ['GET', 'HEAD']"
groupby: "evt.Meta.source_ip + '/' + evt.Parsed.target_fqdn"
capacity: -1
condition: |
    len(queue.Queue) >= 5 &&
    MedianInterval(map(queue.Queue[-5:], { #.Time })) > duration("10m") &&
    all(queue.Queue, #.Meta.http_status == '404')
leakspeed: 1h
labels:
  remediation: true
```

In this example, we check if there are at least 5 events in the queue, calculate the median interval between the last 5 requests, and trigger if the median interval exceeds 10 minutes and all responses are 404s.

**Notes:**

* Timestamps are automatically sorted internally for correctness
* Handles both even and odd numbers of intervals correctly
* Requires at least two timestamps
* More robust against outliers compared to `AverageInterval`
* Useful for capturing typical timing patterns in skewed data

## Stash Helpers[​](#stash-helpers "Direct link to Stash Helpers")

### `GetFromStash(cache string, key string)`[​](#getfromstashcache-string-key-string "Direct link to getfromstashcache-string-key-string")

`GetFromStash` retrieves the value for `key` in the named `cache`. The cache are usually populated by [parser's stash section](https://docs.crowdsec.net/docs/next/log_processor/parsers/format.md#stash). An empty string if the key doesn't exist (or has been evicted), and error is raised if the `cache` doesn't exist.

## Others[​](#others "Direct link to Others")

### `IsIPV4(ip string) bool`[​](#isipv4ip-string-bool "Direct link to isipv4ip-string-bool")

Returns true if it's a valid IPv4.

> `IsIPV4("192.168.1.1")`

> `IsIPV4(Alert.GetValue())`

### `IsIP(ip string) bool`[​](#isipip-string-bool "Direct link to isipip-string-bool")

Returns true if it's a valid IP (v4 or v6).

> `IsIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334")`

> `IsIP("192.168.1.1")`

> `IsIP(Alert.GetValue())`

### `GetDecisionsCount(value string) int`[​](#getdecisionscountvalue-string-int "Direct link to getdecisionscountvalue-string-int")

Returns the number of existing decisions in the database with the same value. This can return expired decisions if they have not been flushed yet.

> `GetDecisionsCount("192.168.1.1")`

> `GetDecisionsCount(Alert.GetValue())`

### `GetDecisionsSinceCount(value string, since string) int`[​](#getdecisionssincecountvalue-string-since-string-int "Direct link to getdecisionssincecountvalue-string-since-string-int")

Returns the number of existing decisions in the database with the same value since duration string (valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".). This can return expired decisions if they have not been flushed yet.

> `GetDecisionsSinceCount("192.168.1.1", "7h")`

> `GetDecisionsSinceCount(Alert.GetValue(), "30min")`

### `GetActiveDecisionsCount(value string) int`[​](#getactivedecisionscountvalue-string-int "Direct link to getactivedecisionscountvalue-string-int")

Returns the number of active decisions in the database with the same value.

> `GetActiveDecisionsCount(Alert.GetValue())`

### `GetActiveDecisionsTimeLeft(value string) time.Duration`[​](#getactivedecisionstimeleftvalue-string-timeduration "Direct link to getactivedecisionstimeleftvalue-string-timeduration")

Returns the time left for the longest decision associated with the value.

The returned value type is `time.Duration`, so you can use all the [time.Duration methods](https://pkg.go.dev/time#Duration).

> `GetActiveDecisionsTimeLeft(Alert.GetValue())`

> \`GetActiveDecisionsTimeLeft(Alert.GetValue()).Hours() > 1"

### `KeyExists(key string, map map[string]interface{}) bool`[​](#keyexistskey-string-map-mapstringinterface-bool "Direct link to keyexistskey-string-map-mapstringinterface-bool")

Return true if the `key` exists in the map.

### `Get(arr []string, index int) string`[​](#getarr-string-index-int-string "Direct link to getarr-string-index-int-string")

Returns the index'th entry of arr, or `""`.

### `Distance(lat1 string, long1 string, lat2 string, long2 string) float64`[​](#distancelat1-string-long1-string-lat2-string-long2-string-float64 "Direct link to distancelat1-string-long1-string-lat2-string-long2-string-float64")

Computes the distance in kilometers between the set of coordinates represented by lat1/long1 and lat2/long2. Designed to implement impossible travel and similar scenarios:

YAMLCOPY

```
type: conditional
name: demo/impossible-travel
description: "test"
filter: "evt.Meta.log_type == 'fake_ok'"
groupby: evt.Meta.user
capacity: -1
condition: |
  len(queue.Queue) >= 2 
  and Distance(queue.Queue[-1].Enriched.Latitude, queue.Queue[-1].Enriched.Longitude,
  queue.Queue[-2].Enriched.Latitude, queue.Queue[-2].Enriched.Longitude) > 100
leakspeed: 3h
labels:
  type: fraud
```

Notes:

* Will return `0` if either set of coordinates is nil (ie. IP couldn't be geoloc)
* Assumes that the earth is spherical and uses the haversine formula.

### `Hostname() string`[​](#hostname-string "Direct link to hostname-string")

Returns the hostname of the machine.

## Alert specific helpers[​](#alert-specific-helpers "Direct link to Alert specific helpers")

### `Alert.Remediation bool`[​](#alertremediation-bool "Direct link to alertremediation-bool")

Is `true` if the alert asks for a remediation. Will be true for alerts from scenarios with `remediation: true` flag. Will be false for alerts from manual `cscli decisions add` commands (as they come with their own decision).

### `Alert.GetScenario() string`[​](#alertgetscenario-string "Direct link to alertgetscenario-string")

Returns the name of the scenario that triggered the alert.

### `Alert.GetScope() string`[​](#alertgetscope-string "Direct link to alertgetscope-string")

Returns the scope of an alert. Most common value is `Ip`. `Country` and `As` are generally used for more distributed attacks detection/remediation.

### `Alert.GetValue() string`[​](#alertgetvalue-string "Direct link to alertgetvalue-string")

Returns the value of an alert. field value of a `Source`, most common value can be a IPv4, IPv6 or other if the Scope is different than `Ip`.

### `Alert.GetSources() []string`[​](#alertgetsources-string "Direct link to alertgetsources-string")

Return the list of IP addresses in the alert sources.

### `Alert.GetEventsCount() int32`[​](#alertgeteventscount-int32 "Direct link to alertgeteventscount-int32")

Return the number of events in the bucket.
