API Reference

This section provides detailed documentation for all modules, classes, and functions in the Policy Inspector package.

Core Modules

CLI Interface

policy_inspector.cli.run_scenario_with_panorama(scenario_cls: type[~policy_inspector.scenario.Scenario], panorama_hostname: str, panorama_username: str, panorama_password: str, panorama_api_version: str = 'v11.1', panorama_verify_ssl: bool = False, export: tuple[str, ...] = (), export_dir: str | None = '.', show: tuple[str, ...] = ('text',), panorama_cls: type[~policy_inspector.panorama.PanoramaConnector] = <class 'policy_inspector.panorama.PanoramaConnector'>, **kwargs) None[source]

Common scenario execution logic for Panorama-based scenarios.

policy_inspector.cli.run_scenario_with_mock_data(scenario_cls: type[Scenario], data_dir: Path, device_group: str, device_groups: tuple[str] = (), show: tuple[str, ...] = (), export: tuple[str, ...] = (), export_dir: str | None = '.', **kwargs) None[source]

Run scenario using mock data from JSON files.

Configuration Management

policy_inspector.config.configure_from_yaml(ctx, param, filename)[source]

Callback for –config option that reads YAML configuration file and sets ctx.default_map for Click to use as parameter defaults.

Parameters:
  • ctx – Click context

  • param – The parameter object (not used)

  • filename – Path to the YAML configuration file

policy_inspector.config.config_option(config_file_name: str = '--config', default: str = 'config.yaml')[source]

Decorator that adds a –config option to read defaults from a YAML file.

This uses Click’s ctx.default_map mechanism to set parameter defaults from the configuration file. The configuration file is processed eagerly before other options.

Parameters:
  • config_file_name – The option name (default: “–config”)

  • default – Default config file name

  • help_text – Help text for the option

Returns:

policy_inspector.config.show_options(f)[source]

Decorator that adds –show click options to a command.

policy_inspector.config.export_options(f)[source]

Decorator that adds –export and –export-dir options to a command.

policy_inspector.config.panorama_options(f)[source]

Decorator that adds panorama connection click options to a command.

These options are for connecting to Panorama: - panorama_hostname: str - Panorama hostname/IP - panorama_username: str - Username for authentication - panorama_password: str - Password for authentication (hidden input) - panorama_api_version: str - PAN-OS API version (default: v11.1) - panorama_verify_ssl: bool - Whether to verify SSL certificates (default: False)

Parameters:

f – The function to decorate

Returns:

The decorated function with panorama options added

Panorama Integration

class policy_inspector.panorama.PanoramaConnector(hostname: str, username: str, password: str, port: int = 443, verify_ssl: bool = False, api_version: str = 'v1', timeout: int = 60)[source]

Bases: object

Connect to Panorama and retrieve objects using REST API.

Parameters:
  • hostname – Panorama hostname or IP address

  • username – API username

  • password – API password

  • port – API port (default: 443)

  • verify_ssl – Whether to verify SSL certificates

  • api_version – REST API version (default: v1)

  • timeout – Request timeout in seconds

__init__(hostname: str, username: str, password: str, port: int = 443, verify_ssl: bool = False, api_version: str = 'v1', timeout: int = 60)[source]
get_address_objects(device_group: str | None = None) list[AddressObject][source]

Retrieve address objects from Panorama using REST API.

Parameters:

device_group – Name of the Device Group or shared if None.

Returns:

List of AddressObject instances.

get_address_groups(device_group: str | None = None) list[AddressGroup][source]

Retrieve address groups from Panorama using REST API.

Parameters:

device_group – Name of the Device Group of shared if None.

Returns:

List of AddressGroup instances

get_security_rules(device_group: str | None = None, rulebase: Literal['pre', 'post'] = 'post') list[SecurityRule][source]

Retrieve security rules from Panorama using REST API.

Parameters:
  • device_group – Name of the Device Group of shared if None.

  • rulebase – Type of rulebase.

Returns:

List of SecurityRule instances.

get_device_groups() list[str][source]

Retrieve device groups from Panorama using REST API.

Returns:

List of device group names.

Filters System

policy_inspector.filters.apply_filters(filters: Iterable[Callable[[SecurityRule], bool]], policies: Iterable[SecurityRule]) Iterator[SecurityRule][source]

Apply a set of filter functions to security policies.

Parameters:
  • filters – An iterable of filter functions that take a SecurityRule object and return a boolean indicating whether the rule should be included.

  • policies – An iterable of SecurityRule objects to be filtered.

Returns:

An iterator over SecurityRule objects that pass all filter conditions.

Example

>>> filters = [exclude_disabled, exclude_deny]
>>> filtered_policies = apply_filters(filters, policies)
>>> for policy in filtered_policies:
...     print(policy)
policy_inspector.filters.exclude_disabled(policy: SecurityRule) bool[source]

Exclude security rules that are disabled.

policy_inspector.filters.exclude_deny(policy: SecurityRule) bool[source]

Exclude security rules with a ‘deny’ action.

Scenario Framework

class policy_inspector.scenario.Scenario(panorama: PanoramaConnector, **kwargs)[source]

Bases: object

Base class for defining security scenarios and checks.

name

Scenario display name.

Type:

str | None

panorama

PanoramaConnector instance for data retrieval.

_scenarios

A set of all registered subclasses of Scenario.

Type:

dict[str, type[Scenario]]

name: str | None = None
__init__(panorama: PanoramaConnector, **kwargs) None[source]

Initialize a Scenario instance.

Parameters:
  • panorama – PanoramaConnector instance for data retrieval.

  • **kwargs – Additional keyword arguments for subclass customization.

classmethod __init_subclass__(**kwargs) None[source]

Registers subclasses automatically in the scenarios set.

classmethod get_available() dict[str, type[Scenario]][source]

Retrieve all registered Scenario subclasses.

Returns:

A set containing all subclasses of Scenario.

classmethod from_name(name: str) type[Scenario][source]
show(formats, *args, **kwargs)[source]

Show scenario results in the given formats using registered show functions.

export(formats, *args, output_dir: str | None = None, **kwargs)[source]

Export scenario results in the given formats using registered export functions. If exporting to HTML, save the file to output_dir or current directory.

execute() ScenarioResults[source]

Execute the scenario logic.

Warning

This method must be implemented by subclasses.

Returns:

The results of executing.

analyze(results: ScenarioResults) AnalysisResult[source]

Analyze the results obtained from executing a scenario.

Warning

This method must be implemented by subclasses.

Parameters:

results – The results to analyze.

Returns:

The analysis outcome.

execute_and_analyze() AnalysisResult[source]

Execute the scenario and analyze the results.

Address Resolution

class policy_inspector.resolver.Resolver(address_objects: list[AddressObject], address_groups: list[AddressGroup])[source]

Bases: object

Process Address Groups into their Address Objects or IP Network object.

It expands Address Groups (AG) recursively.

Parameters:
  • address_objects – A list of AddressObject.

  • address_groups – A list of AddressGroup.

__init__(address_objects: list[AddressObject], address_groups: list[AddressGroup])[source]
resolve(names: Iterable[str]) list[AddressObject][source]

Resolve given names.

Parameters:

names – Names of Address Groups or Address Objects

Utilities

policy_inspector.utils.load_json(path: Path) list[dict[str, Any]][source]

Load and parse a JSON file, returning its contents as a list of dictionaries.

policy_inspector.utils.register_export(scenario_cls: type, fmt: str)[source]

Decorator to register an export function for a scenario and format.

policy_inspector.utils.get_export_func(scenario, fmt: str)[source]

Get export function for scenario instance and format.

policy_inspector.utils.register_show(scenario_cls: type, fmt: str)[source]

Decorator to register a show function for a scenario and format.

policy_inspector.utils.get_show_func(scenario, fmt: str)[source]

Get show function for scenario instance and format.

policy_inspector.utils.load_jinja_template(template_dir: Path, template_name: str)[source]

Load a Jinja2 template from the current directory.

class policy_inspector.utils.VerboseGroup(name=None, commands=None, **attrs)[source]

Bases: RichGroup

Click Group that automatically adds verbose option to all commands.

__init__(name=None, commands=None, **attrs)[source]

Initialize RichGroup class.

add_command(cmd, name=None)[source]

Override to add verbose option to all commands.

policy_inspector.utils.config_logger(logger_name: str = 'policy_inspector', default_level: str = 'INFO', log_format: str = '%(message)s', date_format: str = '[%X]') None[source]

Configure logger with RichHandler

Parameters:
  • logger – Instance of a logging.Logger

  • level – Default level of a logger.

  • log_format – Logs format.

  • date_format – Date format in logs.

class policy_inspector.utils.Example(*, name: str, scenario: type, data_dir: str, device_group: str, show: tuple[str, ...] = ('text',), export: tuple[str, ...] = (), args: dict[str, Any] = {})[source]

Bases: BaseModel

Represents an example that can be run.

model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

name: str
scenario: type
data_dir: str
device_group: str
show: tuple[str, ...]
export: tuple[str, ...]
args: dict[str, Any]
get_data_dir() Path[source]

Get the absolute path to the data directory.

class policy_inspector.utils.ExampleChoice(examples: list[Example])[source]

Bases: Choice

__init__(examples: list[Example]) None[source]
convert(value: Any, param: Parameter | None, ctx: Context | None) Any[source]

For a given value from the parser, normalize it and find its matching normalized value in the list of choices. Then return the matched “original” choice.

policy_inspector.utils.get_example_file_path(name: str) Path[source]

Get the path to an example file.

Data Models

Base Models

class policy_inspector.model.base.MainModel[source]

Bases: BaseModel

Base class for all models.

singular: ClassVar[str | None] = None

Display name of a single model.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

plural: ClassVar[str | None] = None

Display name of a many models.

Security Rules

class policy_inspector.model.security_rule.SecurityRule(*, index: int = 1, name: str, enabled: bool = True, action: ~typing.Literal['allow', 'deny', 'monitor'] = 'allow', source_zones: set[str] | set[~typing.Literal['any']] = {'any'}, destination_zones: set[str] | set[~typing.Literal['any']] = {'any'}, source_addresses: set[str] | set[~typing.Literal['any']] = {'any'}, destination_addresses: set[str] | set[~typing.Literal['any']] = {'any'}, applications: set[str] | set[~typing.Literal['any']] = {'any'}, services: set[str] | set[~typing.Literal['any']] | set[~typing.Literal['application-default']] = <factory>, category: set[str] | set[~typing.Literal['any']] = {'any'})[source]

Bases: MainModel

singular: ClassVar[str] = 'Security Rule'

Display name of a single model.

plural: ClassVar[str] = 'Security Rules'

Display name of a many models.

index: int
name: str
enabled: bool
action: Literal['allow', 'deny', 'monitor']
source_zones: set[str] | set[Literal['any']]
destination_zones: set[str] | set[Literal['any']]
source_addresses: set[str] | set[Literal['any']]
destination_addresses: set[str] | set[Literal['any']]
applications: set[str] | set[Literal['any']]
services: set[str] | set[Literal['any']] | set[Literal['application-default']]
category: set[str] | set[Literal['any']]
classmethod parse_json(elements: list[dict]) list[SecurityRule][source]

Map a JSON object to a SecurityRule.

classmethod parse_csv(elements: list[dict]) list[SecurityRule][source]

Map a CSV row to a SecurityRule.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class policy_inspector.model.security_rule.AdvancedSecurityRule(*, index: int = 1, name: str, enabled: bool = True, action: ~typing.Literal['allow', 'deny', 'monitor'] = 'allow', source_zones: set[str] | set[~typing.Literal['any']] = {'any'}, destination_zones: set[str] | set[~typing.Literal['any']] = {'any'}, source_addresses: set[str] | set[~typing.Literal['any']] = {'any'}, destination_addresses: set[str] | set[~typing.Literal['any']] = {'any'}, applications: set[str] | set[~typing.Literal['any']] = {'any'}, services: set[str] | set[~typing.Literal['any']] | set[~typing.Literal['application-default']] = <factory>, category: set[str] | set[~typing.Literal['any']] = {'any'}, resolved_source_addresses: list[~policy_inspector.model.address_object.AddressObjectIPNetwork | ~policy_inspector.model.address_object.AddressObjectIPRange | ~policy_inspector.model.address_object.AddressObjectFQDN] | None = None, resolved_destination_addresses: list[~policy_inspector.model.address_object.AddressObjectIPNetwork | ~policy_inspector.model.address_object.AddressObjectIPRange | ~policy_inspector.model.address_object.AddressObjectFQDN] | None = None)[source]

Bases: SecurityRule

resolved_source_addresses: list[AddressObjectIPNetwork | AddressObjectIPRange | AddressObjectFQDN] | None
resolved_destination_addresses: list[AddressObjectIPNetwork | AddressObjectIPRange | AddressObjectFQDN] | None
classmethod from_security_rule(rule: SecurityRule, **kwargs) AdvancedSecurityRule[source]

Convert a base SecurityRule to an AdvancedSecurityRule.

Parameters:

ruleSecurityRule instance to convert

Returns:

New AdvancedSecurityRule instance with same field values

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

Address Objects

class policy_inspector.model.address_object.AddressObject(*, name: str, description: str = '', tags: set[str] = <factory>)[source]

Bases: MainModel

Base class representing a network address object.

singular: ClassVar[str] = 'Address Object'

Display name of a single model.

plural: ClassVar[str] = 'Address Objects'

Display name of a many models.

name: str
description: str
tags: set[str]
is_covered_by(other: AddressObject) bool[source]
classmethod parse_json(elements: list[dict]) list[AddressObject][source]

Parse JSON data from PAN-OS API response

classmethod parse_csv(elements: list[dict]) list[AddressObject][source]

Parse CSV row from spreadsheet import

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class policy_inspector.model.address_object.AddressObjectIPNetwork(*, name: str, description: str = '', tags: set[str] = <factory>, value: ~ipaddress.IPv4Network)[source]

Bases: AddressObject

Represents an IPv4 network range using CIDR notation.

value: IPv4Network
classmethod convert(v) IPv4Network[source]

Convert string to IPv4Network instance.

Raises:

ValueError – For invalid network formats

is_covered_by(other: AddressObject) bool[source]

Check if this network is fully contained within another object.

Returns:

  • Contained within another IP network

  • Fully inside an IP range

Return type:

True if either

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class policy_inspector.model.address_object.AddressObjectIPRange(*, name: str, description: str = '', tags: set[str] = <factory>, value: tuple[~ipaddress.IPv4Address, ~ipaddress.IPv4Address])[source]

Bases: AddressObject

Represents a contiguous range of IPv4 addresses.

value: tuple[IPv4Address, IPv4Address]
classmethod convert(v) tuple[IPv4Address, IPv4Address][source]

Convert string or list to IPv4Address tuple.

classmethod validate(v)[source]

Ensure valid IP range ordering.

Raises:

ValueError – If end address precedes start address

is_covered_by(other: AddressObject) bool[source]

Check if this range is fully contained within another object.

Returns:

  • Fully inside another IP network

  • Contained within another IP range

Return type:

True if either

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class policy_inspector.model.address_object.AddressObjectFQDN(*, name: str, description: str = '', tags: set[str] = <factory>, value: str)[source]

Bases: AddressObject

Represents a fully qualified domain name.

value: str
is_covered_by(other: AddressObject) bool[source]

Check FQDN equivalence.

Returns:

True if both FQDNs match exactly (case-insensitive)

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

Address Groups

class policy_inspector.model.address_group.AddressGroup(*, name: str, description: str = '', tag: set[str] = <factory>, static: set[str] = <factory>)[source]

Bases: MainModel

singular: ClassVar[str] = 'Address Group'

Display name of a single model.

plural: ClassVar[str] = 'Address Groups'

Display name of a many models.

name: str
description: str
tag: set[str]
static: set[str]
classmethod parse_json(elements: list[dict]) list[AddressGroup][source]

Map a JSON object to an AddressGroup.

classmethod parse_csv(elements: list[dict]) list[AddressGroup][source]

Map a JSON object to an AddressObject.

model_config: ClassVar[ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

Scenarios

Shadowing Detection