Global Helper Functions
A common pattern in programming is to extract repeated code into helper functions, and Panther supports this pattern with the global analysis type.
Global helpers are not best suited to frequent changes. Lookup Tables support automatic syncing with S3, which means they don't require code changes within Panther for updates.

Built-in Globals

By default, Panther comes with built-in global helpers such as panther and panther_oss_helpers. panther is a default and already set up for you to define your custom logic, and panther_oss_helpers provides boilerplate helpers to common caching and other use cases.

Using Globals

Import global helpers in your detections by declared ID at the top of your analysis function body then call the global as if it were any other python library.
For example:
1
import panther_oss_helpers
2
3
4
def rule(event):
5
return event['name'] == 'test-bucket'
6
7
8
def title(event):
9
# Lookup the account name from an account Id
10
account_name = panther_oss_helpers.lookup_aws_account_name(event['accountId'])
11
return 'Suspicious request made to account ' + account_name
Copied!

Adding New Globals

New globals can be created from the Panther Analysis Tool or in your Panther Console.
To create a new global, navigate to Analysis > Helpers:
List Globals
Click CREATE NEW:
Create New Global
Type your Python functions, then click CREATE. This global can now be imported in your rules or policies.

Popular Helpers

deep_get()

deep_get() can be used to return keys that are nested within the python dictionaries. This function is useful for safely returning nested keys and avoiding an AttributeError when a key is not present.
1
def deep_get(dictionary: dict, *keys, default=None):
2
"""Safely return the value of an arbitrarily nested map
3
Inspired by https://bit.ly/3a0hq9E
4
"""
5
return reduce(
6
lambda d, key: d.get(key, default) if isinstance(d, Mapping) else default, keys, dictionary
7
)
Copied!
Example:
With the following JSON, the deep_get function would return the value of result.
1
{ "outcome": { "reason": "VERIFICATION_ERROR", "result": "FAILURE" }}
Copied!
1
deep_get(event, "outcome", "result") == "FAILURE"
Copied!
An example of this can be found in the Geographically Improbable Okta Login detection.

is_ip_in_network()

is_ip_in_network() is a function to check if an IP address is within a list of IP ranges. This function can be used with a list of known internal networks for added context to the detection.
1
def is_ip_in_network(ip_addr, networks):
2
"""Check that a given IP is within a list of IP ranges"""
3
return any(ip_address(ip_addr) in ip_network(network) for network in networks)
Copied!
Example:
1
SHARED_IP_SPACE = [
2
"192.168.0.0/16",
3
]
4
5
if is_ip_in_network(event.get("ipaddr"), SHARED_IP_SPACE):
6
...
Copied!
An example can be found in the OneLogin Active Login Activity detection.

pattern_match()

Wrapper around fnmatch for basic pattern globs. This can be used when simple pattern matching is needed without the requirement of using regex.
1
def pattern_match(string_to_match: str, pattern: str):
2
"""Wrapper around fnmatch for basic pattern globs"""
3
return fnmatch(string_to_match, pattern)
Copied!
Example:
With the following JSON the pattern_match() function would return true.
1
{ "operation": "REST.PUT.OBJECT" }
Copied!
1
pattern_match(event.get("operation", ""), "REST.*.OBJECT")
Copied!
An example can be found in the AWS S3 Access Error detection.

pattern_match_list()

Similar to pattern_match(), pattern_match_list() can check that a string matches any pattern in a given list.
1
def pattern_match_list(string_to_match: str, patterns: Sequence[str]):
2
"""Check that a string matches any pattern in a given list"""
3
return any(fnmatch(string_to_match, p) for p in patterns)
Copied!
Example:
With the following JSON the pattern_match_list() function would return true.
1
{ "userAgent": "aws-sdk-go/1.29.7 (go1.13.7; darwin; amd64) APN/1.0 HashiCorp/1.0 Terraform/0.12.24 (+https://www.terraform.io)" }
Copied!
1
ALLOWED_USER_AGENTS = {
2
"* HashiCorp/?.0 Terraform/*",
3
# 'console.ec2.amazonaws.com',
4
# 'cloudformation.amazonaws.com',
5
}
6
7
pattern_match_list(event.get("userAgent"), ALLOWED_USER_AGENTS)
Copied!
An example can be found in the AWS EC2 Manual Security Group Change detection.

aws_strip_role_session_id()

aws_strip_role_session_id() strips the session ID our of the arn.
1
def aws_strip_role_session_id(user_identity_arn):
2
# The ARN structure is arn:aws:sts::123456789012:assumed-role/RoleName/<sessionId>
3
arn_parts = user_identity_arn.split("/")
4
if arn_parts:
5
return "/".join(arn_parts[:2])
6
return user_identity_arn
Copied!
Example:
With the following value, aws_strip_role_session_id() would return arn:aws:sts::123456789012:assumed-role/demo
1
{ "arn": "arn:aws:sts::123456789012:assumed-role/demo/sessionName" }
Copied!
1
aws_strip_role_session_id(user_identity.get("arn", ""))
Copied!
An example can be found in the AWS Unauthorized API Call detection.