# Modifying Detections with Inline Filters

## Overview

You can easily tune existing [rules](https://docs.panther.com/detections/rules), including [Panther-managed rules](https://docs.panther.com/detections/panther-managed), by adding Inline Filters. An Inline Filter is a condition that must pass in order for the detection logic to then be run. Inline Filters are available only on rules, not scheduled rules nor policies.

In the [Panther Console,](#creating-filters-in-the-panther-console) you can create Inline Filters using a no-code builder. In the [CLI workflow](#creating-filters-in-the-cli-workflow), you can create Inline Filters by adding the `InlineFilters` YAML key.

A common use case for filters is to add an allowlist or denylist.

### How Inline Filters work

Filter statements are evaluated before a detection's logic. A filter must return `true` (i.e., match the event) for the detection logic itself to then be run. In other words, Inline Filters are inclusion filters.

In both the Console and CLI workflow, filters can be grouped using `AND` or `OR` logic. Inline Filters follow [De Morgan's Laws](https://en.wikipedia.org/wiki/De_Morgan's_laws). See logic examples in this Knowledge Base article: [Troubleshooting guide for Panther Detection Inline Filters](https://help.panther.com/articles/9840276836-why-is-my-panther-inline-filter-not-working-as-expected).

If an event does not contain the field the filter is evaluating, the filter will pass. If the field the filter is evaluating has a value of `none`, the filter will return `false` on positive comparators or on comparators that don't apply, and `true` for inverse comparators.

In the Console, filters are not available during new rule creation. In the CLI workflow, you can include `InlineFilters` on new rules.

{% hint style="info" %}
While it is broadly discouraged to manage detection content using both CLI workflows and the Console simultaneously, it *is* possible to use Inline Filters in the Console alongside the CLI workflow. Filters created in the Console will not be overwritten or deleted when an update to detection content is made in the CLI workflow.
{% endhint %}

## Creating filters in the Panther Console

You can add filters to a rule from its edit page, or within an alert triggered by that rule.

{% tabs %}
{% tab title="From a rule" %}
**Add filters from a rule's edit page**

1. In the left-hand navigation bar of your Panther Console, click **Detections**.
2. In the list of detections, click a rule's name to view its details page.
3. Within the **Detect** section, under **Filter to only include events:** and to the right of **Where**, click **+**.
   * In the menu that appears, select either **Add Filter** or **Add Filter Group**.\
     ![There is a plus button (+) next to the word "Where." A menu is open below the plus, with options "Add Filter" and "Add Filter Group."](https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-080abe7b720a0ae4ff0403b9d68b999c1354f66b%2FScreenshot%202023-10-24%20at%2011.50.00%20AM.png?alt=media)
4. For each filter (either on its own or within a group), define the logic:
   1. Click **Key**, then select an event key the condition will apply to.
      * To indicate a nested field, use JSON path notation.
      * Some options may include `[*]`, indicating the key is an array of objects. [Learn more about indexing an array of objects below](#indexing-an-array-of-objects).
   2. Click **Condition**, then select a condition.
   3. If the selected **Condition** requires an inputted value(s) (e.g., `is` or `contains`), provide a value or list of values.
      * If the values(s) field takes in an array, see the [Inputting array values instructions below](#inputting-array-values).
5. Between each filter and filter group, ensure the correct combinator (either **and** or **or**) is selected.
6. Run unit tests to ensure they pass with the added filter(s).
7. In the upper-right corner of the page, click **Deploy** to save your changes.
   {% endtab %}

{% tab title="From an alert event" %}
**Add filters from an alert event**

You can add Inline Filters to a rule directly from an event in an associated alert. This is particularly helpful if you've received a false positive alert, and want to tune the triggered detection so it won't match on similar events in the future.

1. In the left-hand navigation bar of your Panther Console, click **Alerts**.
2. Locate the alert whose associated rule you'd like to tune, and click its name.
3. On the alert's detail page, scroll down to the **Event** section.
4. In the event's JSON, hover over the indicator you'd like the new filter to target, and click the target icon.\
   ![While viewing an alert, the event JSON is shown. One event field is hovered over, and three icons have appeared. The third is a target, and the tooltip reads "Add to Rule Filter"](https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-368226658c600fe0886798da4f73a0ff926f5596%2FScreenshot%202023-10-24%20at%2012.43.34%20PM.png?alt=media)
   * The **Add Filter** slide-out panel will open on the right-hand side of the window.
5. In the **Add Filter** slide-out panel, a new filter will be pre-populated in the following way:
   * **Key**: defaults to the field on which you clicked the target icon in the event JSON.
   * **Condition**: defaults to **is not**, assuming you would *not* like to receive alerts for events like this in the future.
   * **String**: defaults to the value of the selected field in the event JSON.\
     ![The Ad Filter panel is shown. It displays a Target, Filters section (with a new filter pre-populated with "actionName is not SIGN\_IN", a read-only Rule Function section, and a Unit Test section. at the bottom there are buttons for Close, Discard Changes, and Save & Run Test](https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-45530cd79335ddedacc5417626a7240e4504ac58%2FScreenshot%202023-10-24%20at%2012.50.45%20PM.png?alt=media)
6. Make any desired changes to the filter. All pre-populated fields (i.e., **Key**, **Condition** and **String**) are editable.
7. Locate the **Unit Test** section near the bottom of the panel. If the rule is not [Panther-managed](https://docs.panther.com/detections/panther-managed) and you'd like to create a new unit test for the rule using the current event, click the checkbox labeled **Add the current alert event as a unit test**.
   * The toggle labeled **The detection should trigger based on the example event** is editable. It defaults to **No**, as you are likely trying to prevent alerts like this in the future.\
     ![The Unit Test section of the panel is shown. A checkbox labeled "Add the current alert event as a unit test" is checked. There is a toggle below with the label "The detection should trigger based on the example event:"](https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-7bcb121b77d597d525c31838145ba1d699d5b421%2FScreenshot%202023-03-29%20at%203.46.30%20PM.png?alt=media)
   * If the rule is [Panther-managed](https://docs.panther.com/detections/panther-managed), this option will be greyed out.\
     ![The Unit Test section is greyed out. The informational tooltip says "Unit Test is created and managed by the Panther team."](https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-9be15106e11ae71b3f0811062c23e978df1d1c74%2FScreenshot%202023-03-29%20at%204.35.25%20PM.png?alt=media)
8. Click **Save & Run Test**.
   * This runs all of the target rule's unit tests. If you created a new unit test in step 7, it is also run.
   * In order for the new filter(s) to be saved, all of the rule's unit tests must pass. If any of the unit tests fail:
     * If the rule is not [Panther-managed](https://docs.panther.com/detections/panther-managed), click **View Detection** to be taken to the rule's detail page to edit unit tests. From there, you can click **Update** to save your changes to the rule.\
       ![An error message reads "There were errors trying to save the rule with additional filter(s). Changes have not been saved to your detection." A button reads "View Detection"](https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-7345f665f2c180691b21a1bdf0a6ceaaa8293c1a%2Fimage.png?alt=media)
     * If the rule is [Panther-managed](https://docs.panther.com/detections/panther-managed), its unit tests are read-only, meaning you can't alter failing tests to make them pass. To be able to add the filter successfully, instead follow the [Working with failed unit tests with filters](#working-with-failed-unit-tests-with-filters) workflow.
       {% endtab %}
       {% endtabs %}

### Inputting array values

If the Rule Filter operator you've selected requires the value field to take in an array (such as the `is in` operator), you'll input the array values in a modal that pops up when you click into the value field.

To add values to an array:

1. After selecting a **Key** and **Condition** for your Filter, click into the values field.

   <figure><img src="https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-2319fbf70d57e27d9bed8ea00b692fbc6b4ae830%2FScreenshot%202023-10-25%20at%2012.49.32%20PM.png?alt=media" alt="In the Filters to only include: section, Field, Operator and List inputs are shown. Field has an &#x22;EventId&#x22; value, Operator has an &#x22;is in&#x22; value, and (empty list) doesn&#x27;t yet have a value, but the field is circled."><figcaption></figcaption></figure>

   * This will open the array input modal.
2. In the modal, enter the array value(s) in the input field.
   * If your input is comma-delimited, check the **Values entered above are comma-delimited** checkbox.
     * When this field is checked, the text inputted into the values field will be separated (using a comma delimiter) into multiple values. For example, entering "User 1,User 2,User 3" will result in three values added.

       <img src="https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-34cfe6cf52ba7ee827eea50a487862b972635108%2Fimage.png?alt=media" alt="The array input modal says &#x22;Enter a list of strings...&#x22; at the top. It has an input textfield, and a checkbox that says &#x22;Values entered above are comma-delimited,&#x22; which is checked. Three values have been entered: User 3, User 2, and User 1." data-size="original">
   * If your input is not comma-delimited, leave **Values entered above are comma-delimited** unchecked.
     * When this field is unchecked, you can add values that contain commas one at a time. For example, entering "1,000" will add just one value.\
       ![The array input modal says "Enter a list of strings..." at the top. It has an input textfield, and a checkbox that says "Values entered above are comma-delimited," which is unchecked. One value has been entered: 1,000](https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-78ffba5ea238da820ffc127321d06b86f3cfc251%2Fimage.png?alt=media)
3. Click **Add**.
4. Repeat steps 2-3 as needed, until all values have been added to the array.
5. Click **Apply**.

### Indexing an array of objects

While creating a filter expression, if there is an event key whose value is an array of objects, that key will be shown in the dropdown selector with the array indexing symbol `[*]`, along with the fields in the object. You can use `[*]` to target the selected field in all objects in the array, or replace `*` with an integer to index the array, targeting a single field.

#### Example

For example, take this `resources` field in the [`AWS.CloudTrail` schema](https://docs.panther.com/data-onboarding/supported-logs/aws/cloudtrail#aws.cloudtrail):\
![A "resources" field is shown, with three nested fields: arn, accountId, and type. "resources" and the nested fields are circled.](https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-ad080341283a6014fc622ff8073c62c63e6282a5%2FScreenshot%202024-03-04%20at%2010.39.15%20AM.png?alt=media)

It will be represented in the event field selector as:

![In a field prepended by "Where," an empty filter expression is shown. The field selector is open, and the following fields are circled: resources\[\*\].accountId, resources\[\*\].arn, and resources\[\*\].type.](https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-e112e881d8680b29d3f8da2a3f23c10eb2d29876%2FScreenshot%202024-03-04%20at%2010.42.22%20AM.png?alt=media)

By default, Panther applies a wildcard array index (`[*]`) that will search across values for the chosen field in all objects in the array. When `[*]` is used, an array of these values is created and searched. Because of this, only the array conditions are available: `is empty`, `is not empty`, `contains`, `does not contain`.

![A filter expression has "resources\[\*\].type" selected for the key, and the condition selector is open. Four conditions are shown and circled: is empty, is not empty, contains, and does not contain.](https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-06416cc986addb20dd5f031094bcf1d7057d061c%2FScreenshot%202024-03-04%20at%2010.59.48%20AM.png?alt=media)

For example, the below filter expression (`resources[*].type` `contains` `AWS::IAM::Role`) means an event will match if *any* value of `type` in the `resources` array is `AWS::IAM::Role`.

![A filter chip reads, "resources\[\*\].type contains AWS::IAM::Role"](https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-1057f83d0ad34ef7d34a7ccbc2457850e5fa942b%2FScreenshot%202024-03-04%20at%2012.43.27%20PM.png?alt=media)

However, you can replace the `*` with an integer to index the array, which specifies a single object in the array. In this case, Panther will only evaluate the value of the nested field at that index.

The conditions shown will be updated to those which are applicable to the data type of the nested field chosen:

![In a filter expression, the key selector reads "resources\[0\].type" and the condition dropdown is open. Various conditions are shown, e.g., is not, is in, and is public.](https://4011785613-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-9256492762f07da4b099bdb852d35e6dfeb4e0da%2FScreenshot%202024-03-04%20at%2011.10.22%20AM.png?alt=media)

## Creating filters in the CLI workflow

In addition to creating no-code rule filters in the Panther Console, you can also create YAML filters on your rules written as [Simple Detections](https://docs.panther.com/detections/rules/writing-simple-detections) or [Python detections](https://docs.panther.com/detections/rules/python).

Like the filters created in the Console, YAML filters are evaluated before the detection logic of a rule. If the filter returns `true`, the detection logic will be executed. If the filter returns `false`, the evaluation of the detection will stop, and the detection will return `false` altogether.

### YAML `InlineFilter` syntax

A YAML filter is denoted by the `InlineFilters` key. Within `InlineFilters`, list one or more match expressions. You can use the `All` and `Any` [combinators](https://docs.panther.com/detections/writing-simple-detections/match-expression#combinators) to specify AND or OR logic, respectively, and nest combinators to create filter groups. If a combinator is not specified directly under `InlineFilters`, `All` is assumed.

See [Simple Detection Match Expression Reference](https://docs.panther.com/detections/rules/writing-simple-detections/match-expression) to learn how to construct different types of match expressions.

Example:

```yaml
InlineFilters: 
  - KeyPath: environment
    Condition: StartsWith
    Value: "Sandbox"
```

### Limitations of YAML Inline Filters

Some match expression functionality described in [Simple Detection Match Expression Reference](https://docs.panther.com/detections/rules/writing-simple-detections/match-expression) is not possible in `InlineFilters`. These limitations include:

* `InlineFilters` cannot be used on scheduled rules or policies, only rules.
* The following [match expression types](https://docs.panther.com/detections/writing-simple-detections/match-expression#types-of-match-expressions) cannot be used within `InlineFilters`:
  * [Multi-key match expressions](https://docs.panther.com/detections/writing-simple-detections/match-expression#multi-key-match-expressions)
  * [List comprehension match expressions](https://docs.panther.com/detections/writing-simple-detections/match-expression#list-comprehension-match-expressions)
  * [Absolute match expressions](https://docs.panther.com/detections/writing-simple-detections/match-expression#absolute-match-expressions)
  * [Enrichment match expressions](https://docs.panther.com/detections/writing-simple-detections/match-expression#enrichment-match-expressions)
* The `Key` and `DeepKey` [key specifiers](https://docs.panther.com/detections/writing-simple-detections/match-expression#key-specifiers) cannot be used within `InlineFilters`—only `KeyPath` may be used.
* The `OnlyOne` and `None` [combinators](https://docs.panther.com/detections/writing-simple-detections/match-expression#combinators) cannot be used within `InlineFilters`—only `All` and `Any` may be used.
* Certain [`Condition`](https://docs.panther.com/detections/writing-simple-detections/match-expression#condition) values cannot be used within `InlineFilters`. The following conditions are not supported:
  * `Exists`
  * `DoestNotExist`
  * `IsNull`
  * `IsNotNull`
  * `IsIPAddress`
  * `IsIPv4Address`
  * `IsIPv6Address`
  * `AnyElement`
  * `AllElements`
  * `OnlyOneElement`
  * `NoElement`

### How to create an Inline Filter in the CLI workflow

To create an Inline Filter in the CLI workflow on a rule created as either a Python or Simple Detection, in the detection's YAML file, include the `InlineFilters` key. Within `InlineFilters`, include one or more [match expressions](https://docs.panther.com/detections/rules/writing-simple-detections/match-expression).

## Working with failed unit tests with filters

For [Panther-managed](https://docs.panther.com/detections/panther-managed) rules with filters, you currently cannot add or edit unit tests. You cannot save a rule if the unit test does not pass.

If a unit test fails, take the following steps:

1. Clone the Panther-managed rule.
2. Add your filter(s) to the cloned rule.
3. Edit the unit tests for the cloned rule so that they pass.

## Inline filter reference

Refer to the below operators and value types when building out your filters in the Console.

### Supported Console operators

<table data-full-width="false"><thead><tr><th width="138">Operation</th><th width="280">Usage guidelines</th><th width="195">Supported field types</th><th>Examples</th></tr></thead><tbody><tr><td>is / is not</td><td>An event will match when the field matches/does not match the value in the filter</td><td>string, ip, bool, int</td><td>username is “root”</td></tr><tr><td>is / is not (case insensitive)</td><td>An event will match when the field matches/does not match the value in the filter in a case insensitive fashion</td><td>string, ip, bool, int</td><td>username is (case insensitive) “rOot”</td></tr><tr><td>is in / is not in</td><td>An event will match when the field matches/does not match an entry in the list of values in the filter</td><td>string, int</td><td><p>username is in [ “root”, “admin” ]<br></p><p>port is in [25, 553]</p></td></tr><tr><td>is empty</td><td>An event will match when the field's value is not specified. The operator tests only for the absence of data</td><td>string, int array, ip array, float array, bool array, string array</td><td>errors_list is empty</td></tr><tr><td>is not empty</td><td>An event will match when the field's value is specified. The operator tests only for the presence of data</td><td>string, int array, ip array, float array, bool array, string array</td><td>errors_list is not empty</td></tr><tr><td>contains</td><td><p>An event will match when the value of the field specified contains the value provided</p><p>When the event value is a string or a string array, partial matching is supported</p></td><td>string, int array, ip array, bool array, string array</td><td><p>domain contains “.google.com”<br></p><p>p_any_port contains 22</p></td></tr><tr><td>contains (case insensitive)</td><td><p>An event will match when the value of the field specified contains the value provided in a case insensitive fashion</p><p>When the event value is a string or a string array, partial matching is supported</p></td><td>string, string array</td><td>username contains (case insensitive) "bad"<br><br>p_any_email contains (case insensitive) "bad"</td></tr><tr><td>does not contain</td><td><p>An event will match when the value of the field specified does not contain the value provided</p><p>When the event value is a string or a string array, partial matching is supported</p></td><td>string, int array, ip array, bool array, string array</td><td><p>domain !contains “.google.com”<br></p><p>p_any_port !contains 22</p></td></tr><tr><td>does not contain (case insensitive)</td><td><p>An event will match when the value of the field specified does not contain the value provided in a case insensitive fashion</p><p>When the event value is a string or a string array, partial matching is supported</p></td><td>string, string array</td><td><p>domain !contains (case insensitive) “.gOogle.com”<br></p><p>p_any_email !contains "good"</p></td></tr><tr><td>starts with</td><td>An event will match when the value of the field specified begins with the value provided</td><td>string</td><td>role starts with “admin_”</td></tr><tr><td>starts with (case insensitive)</td><td>An event will match when the value of the field specified begins with the value provided in a case insensitive fashion</td><td>string</td><td>role starts with (case insensitive) “aDmin_”</td></tr><tr><td>does not start with</td><td>An event will match when the value of the field specified does not begin with the value provided</td><td>string</td><td>role does not start with "admin_"</td></tr><tr><td>does not start with (case insensitive)</td><td>An event will match when the value of the field specified does not begin with the value provided in a case insensitive fashion</td><td>string</td><td>role does not start with (case insensitive) "aDmin_"</td></tr><tr><td>ends with</td><td>An event will match when the value of the field specified ends with the value provided</td><td>string</td><td>domain ends with “.cc”</td></tr><tr><td>ends with (case insensitive)</td><td>An event will match when the value of the field specified ends with the value provided in a case insensitive fashion</td><td>string</td><td>domain ends with (case insensitive) “.Cc”</td></tr><tr><td>does not end with</td><td>An event will match when the value of the field specified does not end with the value provided</td><td>string</td><td>domain does not end with ".com"</td></tr><tr><td>does not end with (case insensitive)</td><td>An event will match when the value of the field specified does not end with the value provided in a case insensitive fashion</td><td>string</td><td>domain does not end with ".coM"</td></tr><tr><td>is greater than</td><td>An event will match when the field's value is greater than the value provided in the filter</td><td>int, float</td><td>port > 1023</td></tr><tr><td>is less than</td><td>An event will match when the field's value is less than the value provided in the filter</td><td>int, float</td><td>port &#x3C; 1024</td></tr><tr><td>is greater than or equal</td><td>An event will match when the field's value is greater than or equal to the value provided in the filter</td><td>int</td><td>count ≥ 1</td></tr><tr><td>is less than or equal</td><td>An event will match when the field's value is less than or equal to the value provided in the filter</td><td>int</td><td>count ≤ 100</td></tr><tr><td>is private</td><td>An event will match when the IP address specified is private</td><td>IP</td><td>dst_ip is_private</td></tr><tr><td>is public</td><td>An event will match when the IP address specified is public</td><td>IP</td><td>src_ip is_public</td></tr><tr><td>is in CIDR / is not in CIDR</td><td>An event will match when the IP address specified is/is not within a provided CIDR (Classless Inter-Domain Routing) block</td><td>IP</td><td>src_ip in_cidr 192.168.0.0/16</td></tr><tr><td>does not contain IP in CIDR</td><td>An event will match when the IP array specified does not contain any IP address within the CIDR block provided</td><td>ip array</td><td><p>p_any_ip_address !contains_ip 8.8.0.0/16<br></p><p>p_any_ip_address !contains_ip 1.1.1.1/32</p></td></tr><tr><td>contains IP in CIDR</td><td>An event will match when the IP array specified contains an IP address within the CIDR block provided</td><td>ip array</td><td><p>p_any_ip_address contains_ip 8.8.0.0/16<br></p><p>p_any_ip_address contains_ip 1.1.1.1/32</p></td></tr></tbody></table>

### Supported value types

<table><thead><tr><th width="153">Value types</th><th>Description</th></tr></thead><tbody><tr><td><code>string</code></td><td>A string value</td></tr><tr><td><code>int</code></td><td>A 32-bit integer number in the range <code>-2147483648</code>, <code>2147483647</code></td></tr><tr><td><code>float</code></td><td>A 64-bit floating point number</td></tr><tr><td><code>boolean</code></td><td>A boolean value <code>true</code> / <code>false</code></td></tr><tr><td><code>array</code></td><td>A JSON array where each element is of the same type</td></tr><tr><td><code>ip</code></td><td>A single valid IPv4 or IPv6 address</td></tr><tr><td><code>CIDR</code></td><td>A classless inter-domain routing block</td></tr></tbody></table>
