QUESTION

How do I use breakpoints and other Python debugging tools on Panther detection functions and tests? I want to see information in addition to whether a test passed or failed.

ANSWER

While there is not debug functionality in Panther tools, it's possible to download detections to a local or dev system and run them locally with your preferred debugging tools, such as Python debugger (pdb).

pdb is Python's built-in debugger and is an interactive source code debugger for Python programs. It includes features to let you pause your program, look at the values of variables, and watch program execution step-by-step, so you can understand what your code is doing and why.

This may not replicate every situation exactly as it runs in the Panther Console (e.g., it's difficult to simulate interactions with the panther_kv_store cache from a local machine), but it can help clarify complexities in many code scenarios.


Example Python file and steps to debug 
import json

# -- Your Rule Code Here -- #

from panther_base_helpers import deep_get

def rule(event):
    if(alert-condition-is-met):
        return True
    else:
        return False

def title(event):
    pass

# -- End Your Code -- #

if __name__ == "__main__":
    with open('event.json') as f:
        event = json.load(f)
    rule(event)
    if 'title' in dir(): print('-- Title --\n' + str(title(event)) + '\n')
    if 'dedup' in dir(): print('-- Dedup --\n' + str(dedup(event)) + '\n')
    if 'alert_context' in dir(): print('-- Alert Context --\n' + str(alert_context(event)) + '\n')
    if 'severity' in dir(): print('-- Severity --\n' + str(severity(event)) + '\n')
    if 'description' in dir(): print('-- Description --\n' + str(description(event)) + '\n')
    if 'reference' in dir(): print('-- Reference --\n' + str(reference(event)) + '\n')
    if 'runbook' in dir(): print('-- Runbook --\n' + str(runbook(event)) + '\n')
    if 'destinations' in dir(): print('-- Destinations --\n' + str(destinations(event)) + '\n')
  1. Create a file containing the code above.

  2. Copy your test case into another file in your working directory. In the example code, this file is event.json.

  3. If you are using Panther helpers, link to them or copy them into the same directory.

  4. Run this with Python and add pdb or any other debugging statements or tools.

Using pdb in your detection code

import pdb

def rule(event):
    ip_address = event.get('ip_address')
    
    # Set a breakpoint here, validate that the value was pulled correctly
    pdb.set_trace()
    
    # Continue your detection code...

When you run this detection, it will pause at the breakpoint, and you can inspect your data and step through your detection code.

Using pdb with PAT

When testing your Panther detections locally using the Panther Analysis Tool, you can take advantage of pdb breakpoints to step through your detection logic and better understand how your detection is working with the provided data.

Once you run your Panther analysis with the code described above, execution will stop at the pdb.set_trace() line, dropping you into an interactive prompt where you can examine variables, step through your code, and try out new code in real time.

Here are some of the most useful pdb commands:

See the pdb documentation for more useful commands.

Using pdb to debug Panther rules

1. Import pdb in your Python detection code

At the start of your detection script, you will need to import the pdb module by adding this line of code:

import pdb

2. Set a Breakpoint

You can set a breakpoint in your code where you want the execution to pause by adding this line of code:

pdb.set_trace()

When the Python interpreter hits this line of code, it will pause execution and wait for your input.

3. Run your Detection Code

When running your detection code, the program will stop at the line of code where you set the breakpoint. At this point, you will enter into an interactive mode where you can type in pdb commands.

Deployment considerations

While pdb is useful for debugging during development, it is crucial to ensure that all pdb-related lines of code are removed or commented out before deploying your detection to a live production environment. This is because pdb pauses program execution and awaits user input.

One automated way to ensure pdb is not present in your production code is to use a code linter, such as pylint, with a custom rule that alerts you if any pdb imports are found. You could integrate such a linter into your continuous integration/continuous deployment (CI/CD) pipeline.

You can use the bad-imports option to accomplish this:

[MAIN] bad-imports=pdb

By placing the above in your pylint configuration file, you will receive a linting error any time pdb is imported, preventing you from accidentally deploying code with pdb statements to production.

Please remember to manually inspect your code as well, ensuring there are no stray pdb statements that could interrupt your detections in a live setting. It's a good practice to make this check part of your code review process.