# Hooks

The Application Security Component lets you hook into different stages to change behavior at runtime.

Hooks run in four phases:

* `on_load`: Called just after the rules have been loaded into the engine.
* `pre_eval`: Called after a request has been received but before the rules are evaluated.
* `post_eval`: Called after the rules have been evaluated.
* `on_match`: Called after a successful match of a rule. If multiple rules, this hook will be called only once.

## Using hooks[​](#using-hooks "Direct link to Using hooks")

Hooks are configured in your AppSec config file.

The `on_load` hook only supports `apply`, while other hooks support `filter` and `apply`.

Both `filter` and `apply` of the same phase have access to the same helpers.

Except for `on_load`, hooks can be called twice per request: once for in-band processing and once for out-of-band processing. Use `IsInBand` and `IsOutBand` to filter the hook.

Hooks have the following format:

YAMLCOPY

```
on_match:
  - filter: IsInBand && 1 == 1
    apply:
      - valid expression
      - valid expression
```

If the filter returns `true`, each of the expressions in the `apply` section are executed.

### `on_load`[​](#on_load "Direct link to on_load")

This hook is intended to be used to disable rules at loading (eg, to temporarily disable a rule that is causing false positives).

#### Available helpers[​](#available-helpers "Direct link to Available helpers")

| Helper Name                    | Type                                    | Description                                                                                                                                                                             |
| ------------------------------ | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `RemoveInBandRuleByName`       | `func(tag str)`                         | Disable the named in-band rule                                                                                                                                                          |
| `RemoveInBandRuleByTag`        | `func(tag str)`                         | Disable the in-band rule identified by the tag (multiple rules can have the same tag)                                                                                                   |
| `RemoveInBandRuleByID`         | `func(id int)`                          | Disable the in-band rule identified by the ID                                                                                                                                           |
| `RemoveOutBandRuleByName`      | `func(tag str)`                         | Disable the named out-of-band rule                                                                                                                                                      |
| `RemoveOutBandRuleByTag`       | `func(tag str)`                         | Disable the out-of-band rule identified by the tag (multiple rules can have the same tag)                                                                                               |
| `RemoveOutBandRuleByID`        | `func(id int)`                          | Disable the out-of-band rule identified by the ID                                                                                                                                       |
| `SetRemediationByTag`          | `func(tag str, remediation string)`     | Change the remediation of the in-band rule identified by the tag (multiple rules can have the same tag)                                                                                 |
| `SetRemediationByID`           | `func(id int, remediation string)`      | Change the remediation of the in-band rule identified by the ID                                                                                                                         |
| `SetRemediationByName`         | `func(name str, remediation string)`    | Change the remediation of the in-band rule identified by the name                                                                                                                       |
| `LoadAPISchemaWithName`        | `func(ref str, filename str)`           | Load an OpenAPI schema from `<data_dir>/schemas/<filename>` and register it under `ref`. See [OpenAPI Schema Validation](https://docs.crowdsec.net/docs/next/appsec/api_validation.md). |
| `LoadAPISchemaWithOptions`     | `func(ref str, filename str, opts map)` | Same as `LoadAPISchemaWithName` but accepts per-schema policy overrides (`on_route_not_found`, `on_method_not_allowed`).                                                                |
| `RegisterAPISchemaBodyDecoder` | `func(content_type str, decoder str)`   | Enable a non-default body decoder for a Content-Type. See [available decoders](https://docs.crowdsec.net/docs/next/appsec/api_validation.md#body-decoders).                             |
| `SetMaxBodySize`               | `func(size int)`                        | Set the maximum request body size (in bytes) buffered and inspected by the engine. Defaults to 10MB. See [Request body size handling](#request-body-size-handling)                      |
| `SetBodySizeExceededAction`    | `func(action str)`                      | Set what happens when a request body exceeds the maximum size: `drop` (default), `partial`, or `allow`. See [Request body size handling](#request-body-size-handling)                   |

##### Example[​](#example "Direct link to Example")

YAMLCOPY

```
name: crowdsecurity/my-appsec-config
default_remediation: ban
inband_rules:
  - crowdsecurity/base-config
  - crowdsecurity/vpatch-*
on_load:
  - apply:
      - RemoveInBandRuleByName("my_rule")
      - SetRemediationByTag("my_tag", "captcha")
```

### `pre_eval`[​](#pre_eval "Direct link to pre_eval")

This hook is intended to be used to disable rules only for this particular request (eg, to disable a rule for a specific IP).

#### Available helpers[​](#available-helpers-1 "Direct link to Available helpers")

| Helper Name                 | Type                                 | Description                                                                                                                                                                                                                                                                                                |
| --------------------------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `RemoveInBandRuleByName`    | `func(tag str)`                      | Disable the named in-band rule                                                                                                                                                                                                                                                                             |
| `RemoveInBandRuleByTag`     | `func(tag str)`                      | Disable the in-band rule identified by the tag (multiple rules can have the same tag)                                                                                                                                                                                                                      |
| `RemoveInBandRuleByID`      | `func(id int)`                       | Disable the in-band rule identified by the ID                                                                                                                                                                                                                                                              |
| `RemoveOutBandRuleByName`   | `func(tag str)`                      | Disable the named out-of-band rule                                                                                                                                                                                                                                                                         |
| `RemoveOutBandRuleByTag`    | `func(tag str)`                      | Disable the out-of-band rule identified by the tag (multiple rules can have the same tag)                                                                                                                                                                                                                  |
| `RemoveOutBandRuleByID`     | `func(id int)`                       | Disable the out-of-band rule identified by the ID                                                                                                                                                                                                                                                          |
| `IsInBand`                  | `bool`                               | `true` if the request is in the in-band processing phase                                                                                                                                                                                                                                                   |
| `IsOutBand`                 | `bool`                               | `true` if the request is in the out-of-band processing phase                                                                                                                                                                                                                                               |
| `SetRemediationByTag`       | `func(tag str, remediation string)`  | Change the remediation of the in-band rule identified by the tag (multiple rules can have the same tag)                                                                                                                                                                                                    |
| `SetRemediationByID`        | `func(id int, remediation string)`   | Change the remediation of the in-band rule identified by the ID                                                                                                                                                                                                                                            |
| `SetRemediationByName`      | `func(name str, remediation string)` | Change the remediation of the in-band rule identified by the name                                                                                                                                                                                                                                          |
| `req`                       | `http.Request`                       | Original HTTP request received by the remediation component                                                                                                                                                                                                                                                |
| `DropRequest`               | `func(reason str)`                   | Stop processing the request immediately and instruct the remediation component to block the request                                                                                                                                                                                                        |
| `DisableBodyInspection`     | `func()`                             | Skip body inspection for the current request (also bypasses the maximum body size check). See [Request body size handling](#request-body-size-handling)                                                                                                                                                    |
| `ValidateRequestWithSchema` | `func(ref str) bool`                 | Validate the current request against an OpenAPI schema previously loaded under `ref` (returns `true` on success). On failure, structured details are published to `hook_vars` (see [OpenAPI Schema Validation](https://docs.crowdsec.net/docs/next/appsec/api_validation.md#validation-result-variables)). |
| `hook_vars`                 | `map[string]string`                  | Per-request scratch space shared with later hooks and propagated to the resulting event. Helpers such as `ValidateRequestWithSchema` publish their results here.                                                                                                                                           |

#### Example[​](#example-1 "Direct link to Example")

YAMLCOPY

```
name: crowdsecurity/my-appsec-config
default_remediation: ban
inband_rules:
  - crowdsecurity/base-config
  - crowdsecurity/vpatch-*
pre_eval:
  - filter: IsInBand == true && req.RemoteAddr == "192.168.1.1"
    apply:
      - RemoveInBandRuleByName("my_rule")
```

### `post_eval`[​](#post_eval "Direct link to post_eval")

This hook is mostly intended for debugging or threat-hunting purposes.

#### Available helpers[​](#available-helpers-2 "Direct link to Available helpers")

| Helper Name   | Type           | Description                                                  |
| ------------- | -------------- | ------------------------------------------------------------ |
| `IsInBand`    | `bool`         | `true` if the request is in the in-band processing phase     |
| `IsOutBand`   | `bool`         | `true` if the request is in the out-of-band processing phase |
| `DumpRequest` | `func()`       | Dump the request to a file                                   |
| `req`         | `http.Request` | Original HTTP request received by the remediation component  |

#### DumpRequest[​](#dumprequest "Direct link to DumpRequest")

In order to make `DumpRequest` write your request to a file, you have to call `DumpRequest().ToJSON()`, which will create a file in the OS temporary directory (eg, `/tmp` on Linux) with the following format: `crowdsec_req_dump_<RANDOM_PART>.json`.

You can configure what is dumped with the following options:

* `DumpRequest().NoFilters()`: Clear any previous filters (ie. dump everything)
* `DumpRequest().WithEmptyHeadersFilters()`: Clear the headers filters, ie. dump all the headers
* `DumpRequest().WithHeadersContentFilter(regexp string)`: Add a filter on the content of the headers, ie. dump only the headers that *do not* match the provided regular expression
* `DumpRequest().WithHeadersNameFilter(regexp string)`: Add a filter on the name of the headers, ie. dump only the headers that *do not* match the provided regular expression
* `DumpRequest().WithNoHeaders()`: Do not dump the request headers
* `DumpRequest().WithHeaders()`: Dump all the request headers (override any previous filter)
* `DumpRequest().WithBody()`: Dump the request body
* `DumpRequest().WithNoBody()`: Do not dump the request body
* `DumpRequest().WithEmptyArgsFilters()`: Clear the query parameters filters, ie. dump all the query parameters
* `DumpRequest().WithArgsContentFilter(regexp string)`: Add a filter on the content of the query parameters, ie. dump only the query parameters that *do not* match the provided regular expression
* `DumpRequest().WithArgsNameFilter(regexp string)`: Add a filter on the name of the query parameters, ie. dump only the query parameters that *do not* match the provided regular expression

By default, everything is dumped. All regexps are case-insensitive.

You can chain the options, for example:

TEXTCOPY

```
DumpRequest().WithNoBody().WithArgsNameFilter("var1").WithArgsNameFilter("var2").ToJSON()
```

This will discard the body of the request, remove the query parameters `var1` and `var2` from the dump, and dump everything else.

#### Example[​](#example-2 "Direct link to Example")

YAMLCOPY

```
name: crowdsecurity/my-appsec-config
default_remediation: ban
inband_rules:
  - crowdsecurity/base-config
  - crowdsecurity/vpatch-*
post_eval:
  - filter: IsInBand == true
    apply:
      - DumpRequest().NoFilters().WithBody().ToJSON()
```

### `on_match`[​](#on_match "Direct link to on_match")

This hook is intended to be used to change the behavior of the engine after a match (eg, to change the remediation that will be used dynamically).

#### Available helpers[​](#available-helpers-3 "Direct link to Available helpers")

| Helper Name      | Type                       | Description                                                                                                                                 |
| ---------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `SetRemediation` | `func(remediation string)` | Change the remediation that will be returned to the remediation component                                                                   |
| `SetReturnCode`  | `func(code int)`           | Change the HTTP code that will be returned to the remediation component                                                                     |
| `CancelAlert`    | `func()`                   | Prevent the Application Security Component to create a crowdsec alert                                                                       |
| `SendAlert`      | `func()`                   | Force the Application Security Component to create a crowdsec alert                                                                         |
| `CancelEvent`    | `func()`                   | Prevent the Application Security Component to create a crowdsec event                                                                       |
| `SendEvent`      | `func()`                   | Force the Application Security Component to create a crowdsec event                                                                         |
| `DumpRequest`    | `func()`                   | Dump the request to a file (see previous section for detailed usage)                                                                        |
| `IsInBand`       | `bool`                     | `true` if the request is in the in-band processing phase                                                                                    |
| `IsOutBand`      | `bool`                     | `true` if the request is in the out-of-band processing phase                                                                                |
| `evt`            | `types.Event`              | [The event that has been generated](https://docs.crowdsec.net/docs/next/expr/event.md#appsec-helpers) by the Application Security Component |
| `req`            | `http.Request`             | Original HTTP request received by the remediation component                                                                                 |

#### Example[​](#example-3 "Direct link to Example")

YAMLCOPY

```
name: crowdsecurity/my-appsec-config
default_remediation: ban
inband_rules:
 - crowdsecurity/base-config
 - crowdsecurity/vpatch-*
on_match:
  - filter: IsInBand == true && req.RemoteAddr == "192.168.1.1"
   apply:
    - CancelAlert()
    - CancelEvent()
  - filter: |
      any( evt.Appsec.MatchedRules, #.name == "crowdsecurity/vpatch-env-access") and
      req.RemoteAddr = "192.168.1.1"
    apply:
    - SetRemediation("allow")
  - filter: evt.Appsec.MatchedRules.GetURI() contains "/foobar/"
    apply:
     - SetRemediation("allow")
```

## Detailed Helpers Information[​](#detailed-helpers-information "Direct link to Detailed Helpers Information")

### `SetRemediation*`[​](#setremediation "Direct link to setremediation")

When using `SetRemediation*` helpers, the only special value is `allow`: the remediation component won't block the request. Any other values (including `ban` and `captcha`) are transmitted as-is to the remediation component.

### Request body size handling[​](#request-body-size-handling "Direct link to Request body size handling")

Before the request body is handed over to the rules engine, the Application Security Component reads it into memory itself. To protect the engine from oversized requests, the body is bounded by a maximum size (defaults to **10MB**).

This limit is independent from the Coraza-level [`request_body_in_memory_limit`](https://docs.crowdsec.net/docs/next/appsec/configuration.md#inband_options) option: it controls how much of the body CrowdSec buffers in the first place, before any rule is evaluated.

You can tune this behavior from an `on_load` hook:

* `SetMaxBodySize(size)` sets the maximum body size, in bytes. The value must be a positive integer.

* `SetBodySizeExceededAction(action)` controls what happens when a body exceeds the maximum size:

  | Action           | Behavior                                                                                                                                                   |
  | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | `drop` (default) | The request is blocked using the default remediation, without inspecting the body.                                                                         |
  | `partial`        | The body is truncated to the maximum size and the kept portion is inspected. Content beyond the truncation point is discarded and will not match any rule. |
  | `allow`          | The body is not inspected and the request is allowed to proceed (other zones are still evaluated).                                                         |

YAMLCOPY

```
name: crowdsecurity/my-appsec-config
default_remediation: ban
inband_rules:
  - crowdsecurity/base-config
on_load:
  - apply:
      - SetMaxBodySize(20971520) # 20MB
      - SetBodySizeExceededAction("partial")
```

#### `DisableBodyInspection`[​](#disablebodyinspection "Direct link to disablebodyinspection")

`DisableBodyInspection()` can be called from a `pre_eval` hook to skip body inspection for the current request only. When body inspection is disabled:

* the request body is not read or processed, so body-based zones (`BODY_ARGS`, `RAW_BODY`, …) won't match;
* the maximum body size check is bypassed as well: a request that would otherwise be dropped for exceeding the limit is allowed through, because the operator has explicitly accepted that this body won't be processed.

YAMLCOPY

```
pre_eval:
  - filter: req.URL.Path startsWith "/upload"
    apply:
      - DisableBodyInspection()
```

### `req` object[​](#req-object "Direct link to req-object")

The `pre_eval`, `on_match` and `post_eval` hooks have access to a `req` variable that represents the HTTP request that was forwarded to the appsec.

It's a Go [http.Request](https://pkg.go.dev/net/http#Request) object, so you can directly access all the details about the request.

For example:

* To get the requested URI: `req.URL.Path`
* To get the client IP: `req.RemoteAddr`
* To get the HTTP method: `req.Method`
* To get the FQDN: `req.Host`
