# Policies

## Overview

Policies are Python functions used to identify misconfigured cloud infrastructure and generate alerts for your team. Panther provides a number of already written and continuously updated [Panther-managed policies](https://docs.panther.com/~/changes/15ann7vKLltCCAGHtdQr/detections/panther-managed).

## How to write a policy

Before you start writing a new policy, remember to check to see if there's an existing [Panther-managed policy](https://docs.panther.com/~/changes/15ann7vKLltCCAGHtdQr/detections/panther-managed) that meets your needs.

### 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](https://docs.panther.com/~/changes/15ann7vKLltCCAGHtdQr/cloud-scanning/cloud-resource-attributes) page.&#x20;
* Return a `bool` from the policy function.

```python
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](https://docs.panther.com/~/changes/15ann7vKLltCCAGHtdQr/cloud-scanning/cloud-resource-attributes) 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)](https://docs.panther.com/~/changes/15ann7vKLltCCAGHtdQr/panther-developer-workflows/ci-cd/deployment-workflows/pat). For detailed instructions on configuring PAT, see [Panther Developer Workflows](https://docs.panther.com/~/changes/15ann7vKLltCCAGHtdQr/panther-developer-workflows/ci-cd/deployment-workflows/pat).

{% tabs %}
{% tab title="Console" %}
{% hint style="info" %}
This consolidated user interface for viewing and editing detections is in open beta starting with Panther version 1.74. Please share any bug reports and feature requests with your Panther support team.
{% endhint %}

#### How to write policies in the Panther Console

1. In the left-hand navigation bar of your Panther Console, click **Build** > **Detections**.
2. In the upper-right corner, click **Create New**.
3. At the top of the page, choose **Policy** as the detection type.
4. In the **Basic Info** section, provide values for the following fields:
   * **Name**: Enter a descriptive name for the policy.
   * **ID** (optional)**:** Click the pen icon and enter a unique ID for your policy.
5. In the upper-right corner, click **Continue**.
6. On the next page, configure your policy:
   * In the upper-right corner, the **Enabled** toggle will be set to `ON` by default. If you'd like to disable the policy, flip the toggle to `OFF`.
   * In the **For the Following Resource Types** section:
     * **Resource Types**: Select one or more resource types this policy should apply to. Leave empty to apply to all resources.
   * In the **Detect** section:
     * In the **Policy Function** text editor, write a Python `policy` function to define your detection.
       * For detection templates and examples, see the [panther\_analysis GitHub repository](https://github.com/panther-labs/panther-analysis/tree/master/templates)
   * In the **Set Alert Fields** section:
     * **Severity**: Select a [severity level](#alert-severity) for the alerts triggered by this detection.
     * In the **Optional Fields** section, optionally provide values for the following fields:
       * **Description**: Enter additional context about the policy.
       * **Runbook**: Enter the procedures and operations relating to this policy. &#x20;
         * To see examples of runbooks for built-in rules, see [Alert Runbooks](https://docs.panther.com/~/changes/15ann7vKLltCCAGHtdQr/alerts/alert-runbooks).
       * **Reference**: Enter an external link to more information relating to this rule.
       * **Destination Overrides:** Choose destinations to receive alerts for this detection, regardless of severity. Note that destinations can also be set dynamically, in the rule function. See [Routing Order Precedence](https://docs.panther.com/~/changes/15ann7vKLltCCAGHtdQr/alerts/destinations#routing-order-precedence) to learn more about routing precedence.
       * **Ignore Patterns**: Enter patterns to ignore.
       * **Custom Tags**: Enter custom tags to help you understand the rule at a glance (e.g., `HIPAA`.)&#x20;
       * In the **Framework Mapping** section:&#x20;
         1. Click **Add New** to enter a report.
         2. Provide values for the following fields:
            * **Report Key**: Enter a key relevant to your report.
            * **Report Values**: Enter values for that report.
   * In the **Test** section:
     * In the **Unit Test** section, click **Add New** to [create a test](https://docs.panther.com/~/changes/15ann7vKLltCCAGHtdQr/detections/testing) for the policy you defined in the previous step.
7. In the upper-right corner, click **Save**.
   {% endtab %}

{% tab title="Write policies locally" %}
We recommend managing your local detection files in a version control system like GitHub or GitLab.

{% hint style="info" %}
It's best practice to create a fork of Panther's [open-source analysis repository](https://github.com/panther-labs/panther-analysis), but you can also create your own repo from scratch.
{% endhint %}

###

### File setup

Each detection consists of:

* A Python file (a file with a `.py` extension) containing your detection/audit logic&#x20;
* A YAML or JSON specification file (a file with a `.yml` or `.json` extension) containing metadata attributes of the detection.&#x20;
  * By convention, we give this file the same name as the Python file.

### Folder setup

If you group your policies into folders, each folder name must contain `policies` in order for them to be found during upload (using either PAT or the bulk uploader in the Console).

We recommend grouping policies into folders based on log/resource type, e.g., `suricata_rules` or `aws_s3_policies`. You can use the open source [Panther Analysis](https://github.com/panther-labs/panther-analysis) repo as a reference.

###

### Writing policies locally

1. Write your policy and save it (in your folder of choice) as `my_new_policy.py`:\
   def polcy(resource):

   ```
   def policy(resource):  
     return resource['Region'] != 'us-east-1'
   ```
2. Create a specification file using the template below:

   ```
   AnalysisType: policy
   Enabled: true
   Filename: my_new_policy.py
   PolicyID: Category.Type.MoreInfo
   ResourceType:
     - Resource.Type.Here
   Severity: Info|Low|Medium|High|Critical
   DisplayName: Example Policy to Check the Format of the Spec
   Tags:
     - Tags
     - Go
     - Here
   Runbook: Find out who changed the spec format.
   Reference: https://www.link-to-info.io
   ```

See [Policy Specification Reference](#policy-specification-reference) below for a complete list of required and optional fields.
{% endtab %}
{% endtabs %}

## 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](https://docs.panther.com/~/changes/15ann7vKLltCCAGHtdQr/cloud-scanning/cloud-resource-attributes), 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**](#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.

{% hint style="warning" %}
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.
{% endhint %}

## Policy examples

### S3 public read access

In the example below, the policy checks if an S3 bucket allows public read access:

```python
# A list of grantees that represent public access
GRANTEES = {
    'http://acs.amazonaws.com/groups/global/AuthenticatedUsers',
    'http://acs.amazonaws.com/groups/global/AllUsers'
}
PERMISSIONS = {'READ'}


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:

```python
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](https://docs.panther.com/~/changes/15ann7vKLltCCAGHtdQr/cloud-scanning/cloud-resource-attributes/aws/password-policy) resource:

```javascript
{
    "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:

<table data-header-hidden><thead><tr><th width="150">Field Name</th><th width="262.45454545454544">Description</th><th>Expected Value</th></tr></thead><tbody><tr><td>Field Name</td><td>Description</td><td>Expected Value</td></tr><tr><td><strong><code>AnalysisType</code></strong></td><td>Indicates whether this specification is defining a policy or a rule</td><td><code>policy</code></td></tr><tr><td><strong><code>Enabled</code></strong></td><td>Whether this policy is enabled</td><td>Boolean</td></tr><tr><td><strong><code>FileName</code></strong></td><td>The path (with file extension) to the python policy body</td><td>String</td></tr><tr><td><strong><code>PolicyID</code></strong></td><td>The unique identifier of the policy</td><td>String</td></tr><tr><td><strong><code>ResourceTypes</code></strong></td><td>What resource types this policy will apply to</td><td>List of strings</td></tr><tr><td><strong><code>Severity</code></strong></td><td>What severity this policy is</td><td>One of the following strings: <code>Info</code>, <code>Low</code>, <code>Medium</code>, <code>High</code>, or <code>Critical</code></td></tr><tr><td><code>Description</code></td><td>A brief description of the policy</td><td>String</td></tr><tr><td><code>DisplayName</code></td><td>What name to display in the UI and alerts. The <code>PolicyID</code> will be displayed if this field is not set.</td><td>String</td></tr><tr><td><code>Reference</code></td><td>The reason this policy exists, often a link to documentation</td><td>String</td></tr><tr><td></td><td></td><td></td></tr><tr><td><code>Reports</code></td><td>A mapping of framework or report names to values this policy covers for that framework</td><td>Map of strings to list  of strings</td></tr><tr><td><code>Runbook</code></td><td>The actions to be carried out if this policy fails, often a link to documentation</td><td>String</td></tr><tr><td><code>Tags</code></td><td>Tags used to categorize this policy</td><td>List of strings</td></tr><tr><td><code>Tests</code></td><td>Unit tests for this policy.</td><td>List of maps</td></tr></tbody></table>
