How can I filter a Panther rule using an allow list or a deny list?
Panther supports several different methodologies for filtering detections with static lists.
Panther allows for simple tuning of both managed and unmanaged detections using Rule Filters in the Panther Console. Rule Filters are processed before the Python code is run as a prerequisite. You can use Rule Filters to easily configure inclusion or exclusion criteria within the Panther Console GUI. These filters can perform a number of operators against fields denoted in the Panther schema, checking for things like equality or relativity with a wide range of primitive data types (strings, numbers, lists, etc.).
It is generally good practice to rule out certain events as early in the code as possible. For example, let's say we want to exclude known VPN IP addresses from a rule. The code could look something like this:
VPN_IPs = {
"1.1.1.1",
"2.2.2.2",
"3.3.3.3"
# etc...
}
def rule(event):
if event.get("ip_address") in VPN_IPs:
return False
# Proceed with the rest of the rule...
If you wish to apply filter logic to multiple detections, it may be a good idea to consider a Global Helper. Any filter criteria can be serialized in code inside of a Global Helper and re-imported wherever they are needed. This results is less duplicate code, and creates standardization within your Panther instance.
There are several ways this can be implemented:
You can store static variables inside of a Global Helper and then import them into detection code for any inclusion or exclusion criteria.
The Global Helper (my_global_helper
) could look like this:
VPN_IPs = {
"1.1.1.1",
"2.2.2.2",
"3.3.3.3"
# etc...
}
Then, the detection code could look like this:
from my_global_helper import VPN_IPs
def rule(event):
if event.get("ip_address") in VPN_IPs:
return False
# Proceed with the rest of the rule...
Similarly to how static constants allow you centralize values, functions can also be written in Global Helpers and subsequently imported.
Building off the previous example, we could include not only the list of VPN IP addresses, but also a function that will return True or False depending on if there is a match.
In this case, the Global Helper (my_global_helper
) could look like this:
VPN_IPs = {
"1.1.1.1",
"2.2.2.2",
"3.3.3.3"
# etc...
}
def is_vpn_ip(ip_address):
if ip_address in VPN_IPs:
return True
return False
Then, in the detection, it could be implemented like this:
from my_global_helper import is_vpn_ip
def rule(event):
if is_vpn_ip(event.get("ip_address")):
return False
# Proceed with the rest of the rule...
You can make use of Lookup Tables for this use case as well. By configuring a Lookup Table with a known list, you can use the p_enrichment
field in your detection code to make decisions such as inclusion or exclusion.
This is best done when the list of data to be filtered is large enough that managing it in code is unruly (tens, hundreds or thousands of items).
For example, let's say we have a Lookup Table (my_vpn_ip_addresses
) that contains a list of VPN IP addresses by region. The Lookup Table might look something like this:
vpn_ip_address | region |
1.1.1.1 | US |
2.2.2.2 | Europe |
3.3.3.3 | Asia |
etc. | etc. |
The ip_address
field of the Log Type can then be checked against this Lookup Table by configuring the proper selector.
The resulting event might look something like this (truncated for brevity):
{
# ...
"ip_address": "1.1.1.1",
"p_enrichment": {
"my_vpn_ip_addresses": {
"ip_address": {
"vpn_ip_address": "1.1.1.1",
"region": "US"
}
}
}
# ...
}
The detection code could then check for the presence of my_vpn_ip_addresses
in the p_enrichment
field, indicating a match and thereby an escape condition:
from panther_base_helpers import deep_get
def rule(event):
if deep_get(event, "p_enrichment", "my_vpn_ip_addresses"):
return False
# Proceed with the rest of the rule...