Panther enables easy scanning and evaluation of cloud infrastructure configurations.
Policies are Python functions used to identify misconfigured cloud infrastructure and generate alerts for your team.

How to write a policy

Policy Body

The policy body must:
  • Be valid Python3.
  • Define a policy() function that accepts one resource argument.
    • Each policy takes a resource input of a given resource type from the supported resources page.
  • Return a bool from the policy function.
def policy(resource):
return True
The Python body should name the argument to the policy() function resource and also may do the following:
  • Import standard Python3 libraries
  • Import from the user defined aws_globals module
  • Import from the Panther defined panther module
  • Define additional helper functions as needed
  • Define variables and classes outside the scope of the rule function
Using the schemas in supported resources provides details on all available fields in resources. Top level keys are always present, although they may contain NoneType values.

Writing policies locally and in the Panther Console

You can write and deploy policies in the Panther Console or you can write them locally and upload them to Panther via Panther Analysis Tool (PAT).
Panther Console
Write policies locally
  1. 1.
    Log in to the Panther Console.
  2. 2.
    In the left sidebar, click Build > Detections.
  3. 3.
    In the upper right, click Create New.
  4. 4.
    At the top of the page, choose Policy as the detection type.
  5. 5.
    Fill out the Basic Info form:
    • Name: Enter a descriptive name.
    • Severity: Select a detection severity from the drop-down options.
    • Resource Types: Select the resource you want to apply this policy to. Leave empty to apply to all resources.
  6. 6.
    Scroll down to Policy Settings. Optionally add a Description, Runbook, Reference, Ignore Patterns, Custom Tags, and Destination Overrides.
  7. 7.
    Click the Functions & Tests tab. Write a Python function to define your policy in the Policy Function text editor.
  8. 8.
    Click {} Create Test to run a test against the Policy you defined in the previous step.
  9. 9.
    In the upper right corner of the page, click Save.

File setup

Each detection consists of:
  • A Python file (a file with a .py extension) containing your detection/audit logic
  • A YAML or JSON specification file (a file with a .yml or .json extension) containing metadata attributes of the detection.
    • By convention, we give this file the same name as the Python file.
We recommend creating folders based on log/resource type to group your detections, such as suricata_rules or aws_s3_policies. You can use the open source Panther Analysis repo as a reference.
We also recommend managing these files in a version control system (VCS). Most commonly we see GitHub or GitLab used, which are managed git providers.
It's best practice to create a fork of Panther's open-source analysis repository, but you can also create your own repo from scratch.

Writing policies locally

  1. 1.
    Write your policy and save it (in your folder of choice) as def polcy(resource):
    def policy(resource):
    return resource['Region'] != 'us-east-1'
  2. 2.
    Create a specification file using the template below:
    AnalysisType: policy
    Enabled: true
    PolicyID: Category.Type.MoreInfo
    - Resource.Type.Here
    Severity: Info|Low|Medium|High|Critical
    DisplayName: Example Policy to Check the Format of the Spec
    - Tags
    - Go
    - Here
    Runbook: Find out who changed the spec format.
See Policy Specification Reference below for a complete list of required and optional fields.

Policy Writing Best Practices

Constructing Test Resources

Manually building test cases can be prone to human error. We suggest one of the following methods:
  • Option 1: In the Panther Console, navigate to Investigate > Cloud Resources. Apply a filter of the resource type you intend to emulate in your test. Select a resource in your environment, and on the Attributes card you can copy the full JSON representation of that resource by selecting copy button next to the word root.
  • Option 2: Open the Panther Resources documentation, and navigate to the section for the resource you are trying to emulate. Copy the provided example resource. Paste this in to the resource editor if you're working in the web UI, or into the Resource field if you are working locally. Now you can manually modify the fields relevant to your policy and the specific test case you are trying to emulate.
Option 1 is best when it is practical, as this can provide real test data for your policies. Additionally, it is often the case that you are writing/modifying a policy specifically because of an offending resource in your account. Using that exact resource's JSON representation as your test case can guarantee that similar resources will be caught by your policy in the future.

Debugging Exceptions

Debugging exceptions can be difficult, as you do not have direct access to the Python environment running the policies.
When you see a policy that is showing the state Error on a given resource, that means that the policy threw an exception. The best method for troubleshooting these errors is to use option 1 in the Constructing test resources section above and create a test case from the resource causing the exception.
Running this test case either locally or in the Panther Console should provide more context for the issue, and allow you to modify the policy to debug the exception without having to run the policy against all resources in your environment.
Note: Anything printed to stdout or stderr by your Python code will end up in CloudWatch. For SaaS/CPaaS customers, Panther engineers can see these CloudWatch logs during routine application monitoring.

Policy examples

S3 public read access

In the example below, the policy checks if an S3 bucket allows public read access:
# A list of grantees that represent public access
def policy(resource):
for grant in resource['Grants']:
if grant['Grantee']['URI'] in GRANTEES and grant[
'Permission'] in PERMISSIONS:
return False
return True

IAM Password Policy

This example policy alerts when the password policy does not enforce a maximum password age:
def policy(resource):
if resource['MaxPasswordAge'] is None:
return False
return resource['MaxPasswordAge'] <= 90
In the policy() body, returning a value of True indicates the resource is compliant and no alert should be sent. Returning a value of False indicates the resource is non-compliant.
The policy is based on an IAM Password Policy resource:
"AccountId": "123456789012",
"AllowUsersToChangePassword": true,
"AnyExist": true,
"ExpirePasswords": true,
"HardExpiry": null,
"MaxPasswordAge": 90,
"MinimumPasswordLength": 14,
"Name": "AWS.PasswordPolicy",
"PasswordReusePrevention": 24,
"Region": "global",
"RequireLowercaseCharacters": true,
"RequireNumbers": true,
"RequireSymbols": true,
"RequireUppercaseCharacters": true,
"ResourceId": "123456789012::AWS.PasswordPolicy",
"ResourceType": "AWS.PasswordPolicy",
"Tags": null,
"TimeCreated": null

Policy Specification Reference

Required fields are in bold.
A complete list of policy specification fields:
Field Name
Expected Value
Indicates whether this specification is defining a policy or a rule
Whether this policy is enabled
The path (with file extension) to the python policy body
The unique identifier of the policy
What resource types this policy will apply to
List of strings
What severity this policy is
One of the following strings: Info, Low, Medium, High, or Critical
A brief description of the policy
What name to display in the UI and alerts. The PolicyID will be displayed if this field is not set.
The reason this policy exists, often a link to documentation
A mapping of framework or report names to values this policy covers for that framework
Map of strings to list of strings
The actions to be carried out if this policy fails, often a link to documentation
Tags used to categorize this policy
List of strings
Unit tests for this policy.
List of maps