In my detection, there are scenarios where I want the alert deduplication period to be different from my default. Does Panther provide a function like dedup_minutes that I can use?
Panther does not currently provide such a function, but you can work around this requirement by being creative with your dedup function.
First, at the top of your detection code, import the time module.
Write your dedup function as you normally would.
Add some logic to calculate when how long the dedup period (in minutes) should be.
Adjust the return value to return the usual dedup string, plus the suffix shown in the example.
Note that using this method, you can make the effective dedup period shorter, but not longer.
Example:
import time
... other detection code here ...
def dedup(event):
# Determine the dedup string as you normally would:
dedup_str = ...
# Specify the dynamic dedup period, using whatever logic is necessary
dedup_minutes = ...
# Return the dedup_str, plus the special suffix
dedup_seconds = dedup_minutes * 60 # Convert dedup period from minutes to seconds
return dedup_str + str(time.time() // dedup_seconds * dedup_seconds)
This process works by using the current time inside the dedup string, grouping alerts over specific intervals. If the time passes from one interval to the next, the dedup string will change, triggering a new alert to be raised.
Let's walk through the code more directly:
dedup_str = ...
This line is a placeholder where you would normally determine a string that represents how you want to group the events. It could be something like a description or identifier for the group. Oftentimes, you can simply use the title by invoking title(event)
.
dedup_minutes = ...
This line is another placeholder where you would decide how long you want the grouping time period to be, measured in minutes. You can set this value based on your needs, like 5 minutes, 10 minutes, etc.
dedup_seconds = dedup_minutes * 60
Since time.time()
returns the current time in seconds, we also need to convert our dedup period to seconds.
return dedup_str + str(time.time() // dedup_seconds * dedup_seconds)
In this line, the code calculates a special value by combining the dedup_str
(the grouping description) with a time-based suffix.
time.time()
This part fetches the current time in seconds since a specific reference point, typically January 1, 1970. Think of it like checking the time on a clock.
time.time() // dedup_seconds
This division and flooring operation finds out how many whole dedup_seconds
periods have occurred since the reference time. It's like counting how many times your chosen time interval fits within the current time, considering only complete intervals. (For example, 5 goes into 12 only 2 times, but 3 times into 15!)
* dedup_seconds
This step multiplies the result from the previous step by dedup_seconds
, converting the count of intervals back to seconds. Combined with the last step, we're basically rounding down the seconds count. For example, 12 // 5 * 5 would be 10, since 10 is the last full multiple of 5 before 12.
str(...)
This converts the calculated time (in seconds) into a string so that it can be combined with the dedup_str
.