Registering, Testing, and Uploading PyPanther Detections

Overview

PyPanther Detections are in closed beta starting with Panther version 1.108. Please share any bug reports and feature requests with your Panther support team.

A PyPanther Detection must be registered before it is uploaded to your Panther instance. It's also recommended to define tests for your PyPanther Detections.

Registering PyPanther Detections

Registering a PyPanther rule means including it in your Panther deployment package.

To register a rule, pass it in to register() in your main.py file. When you run the pypanther upload or test commands, only rules passed in to register() will be uploaded to your Panther instance or tested.

Registering a rule more than once is not allowed. This means each collection passed into register() must contain unique rules.

It’s not required to register rules used as base rules with inheritance so long as you do not want the base rules themselves uploaded into your Panther instance.

# Sample main.py file

from pypanther import register, get_panther_rules

# Register a single rule (that was defined as a class called "BoxNewLogin")
register(BoxNewLogin)

# Register all Panther-managed rules
register(get_panther_rules())

Viewing registered rules

  • To see all rules that are registered given your currently configured repository, run the pypanther list rules CLI command.

Importing instances of Rule

You can use the get_rules() function to easily fetch rules you might want to register in main.py. get_rules() takes in an imported package (folder) or module (file) name, and returns all rules defined in that package or module that inherit the pypanther Rule class.

Each folder containing rules imported with get_rules() must contain an __init__.py file. Learn more about recommended repository structure in the PyPanther Detections Style Guide.

Example using get_rules():

Registering vs. enabling a rule

All rules have an enabled property, which is different from being registered. See the table below for all possible outcomes:

enabled = True
enabled = False

Registered

Uploaded to Panther and enabled. Unit tests are run during testing.

Uploaded to Panther and disabled. Unit tests are run during testing.

Not registered

Not uploaded to Panther. If uploaded previously, deleted. Unit tests are not run during testing.

Not uploaded to Panther. If uploaded previously, deleted. Unit tests are not run during testing.

Testing PyPanther Detections

Defining tests

Define tests for PyPanther Detections by creating instances of the pypanther RuleTest class. Tests are associated with rules by assigning them to a rule’s tests field. Tests can be defined directly within a rule, or separately set to a variable (that's either local or imported).

Each instance of RuleTest must set a name, expected_result, and log. Each test can also optionally define:

  • A mocks field, which takes a list of RuleMocks.

  • (When expected_result=True) Additional fields, prepended with expected_, that verify the output of alert functions. For example, expected_severity and expected_title.

    • These alert function output verification fields should only be included on RuleTests where expected_result=True. Including them on a test where expected_result=False will cause the test to fail. This is because alert functions are only run when a detection returns True.

See a full list of available fields in the RuleTest property reference.

Example: Rule with tests

Note that this rule's tests (defined above the rule class) use mocks, expected_title, expected_dedup, and expected_alert_context.

Example: Rule with tests

Examples: Mocking entities defined both inside and outside the rule class

You can use RuleMock to define mock values for variables and methods defined either inside or outside of the rule class.

Example: Mocking a variable and function inside the rule class

In the example below, both the INSIDE variable and inside method are defined inside of the rule class, MockTestRule, and mocked in RuleMocks.

Example: Mocking a variable and function defined outside the rule class

In the example below, both the OUTSIDE variable and outside method are defined outside of the rule class, OutsideMockTest, and mocked in RuleMocks.

Running tests

To run all tests defined for your PyPanther Detections, run:

To run only a subset of tests, filter the detections for which tests are run by using a filter flag with test, such as --id or --log-types. See a full list of filter flags by running pypanther test --help.

The test command:

  • Only tests those detections passed into register()

  • Skips tests for Panther-managed rules

If any rules fail a test, the error will print next to the rule name that was tested:

Testing custom helper functions and data fixtures

The pypanther test command tests all PyPanther Detection classes that have a tests attribute set. In addition to testing rule() and alert function output in this way, if you have created custom helper functions for use in rules, you may want to write targeted tests for these helper functions. To do this, it's recommended to use a common Python testing framework, such as pytest or unittest.

Example: using pytest to test a custom function

Say you'd like to write a reusable function that reads a list of AWS account IDs from a file and filters it to include only the production ones. (This function could then be used in an include or exclude filter.) This function might look like the following:

Given this function, there are a few ways in which you may want to test both the data fixture (AWS_ACCOUNTS) itself, as well as the function is_prod_aws_account(). For instance:

  • To verify that the AWS_ACCOUNTS data fixture:

    • Has a certain number of rows

    • Has a certain required field

  • To verify that certain account IDs are included in the prod list (and others are not)

To test these characteristics using pytest, you would add the following functions (all with the test_ prefix):

After you define these tests, you can invoke them by running pytest from your command line.

Uploading PyPanther Detections to Panther

To upload all PyPanther Detections that are registered, run:

The upload command will also upload schemas. Learn more in Uploading schemas to Panther.

You must authenticate when using upload—see Authenticating CLI commands.

You can see all upload options by running pypanther upload -h, but may find the following options particularly useful:

  • --verbose: Generates verbose output, which includes a list of tests by detection (and their pass/fail statuses), a list of registered detections, and a list of included files

  • --dry-run: Do not upload, but show a summary of the changes that will be applied in the next upload

  • --output {text, json}: Prints output in the provided format

    • text is the default value, but json can be useful if you plan to port the output into another workflow

Sample output for pypanther upload --verbose --output json

If a previously uploaded rule is not passed in to register() on a subsequent invocation of upload, it will be deleted in your Panther instance.

Uploading a PyPanther rule with the same id as an existing v1 rule's RuleId will overwrite it. The same is true in the other direction—i.e., uploading a PyPanther rule with the same RuleId as an existing PyPanther rule's id will overwrite it.

upload limitations

The following limitations currently apply to pypanther upload:

  • A maximum of 500 custom rules can be uploaded at once.

    • This limit does not include Panther-managed rules (i.e., those returned by the get_panther_rules() function)

  • The total size of the zip file pypanther produces for upload cannot exceed 10MB.

  • Only Python files may be uploaded.

Last updated

Was this helpful?