Source code for policy_inspector.scenario
import logging
from typing import TYPE_CHECKING, TypeVar
if TYPE_CHECKING:
from policy_inspector.panorama import PanoramaConnector
logger = logging.getLogger(__name__)
ScenarioResults = TypeVar("ScenarioResults")
AnalysisResult = TypeVar("AnalysisResult")
[docs]
class Scenario:
"""
Base class for defining security scenarios and checks.
Attributes:
name: Scenario display name.
panorama: PanoramaConnector instance for data retrieval.
_scenarios: A set of all registered subclasses of Scenario.
"""
name: str | None = None
_scenarios: dict[str, type["Scenario"]] = {}
[docs]
def __init__(self, panorama: "PanoramaConnector", **kwargs) -> None:
"""
Initialize a Scenario instance.
Args:
panorama: PanoramaConnector instance for data retrieval.
**kwargs: Additional keyword arguments for subclass customization.
"""
self.panorama = panorama
for key, value in kwargs.items():
setattr(self, key, value)
[docs]
def __init_subclass__(cls, **kwargs) -> None:
"""Registers subclasses automatically in the `scenarios` set."""
super().__init_subclass__(**kwargs)
cls._scenarios[str(cls)] = cls
def __str__(self):
return self.name or self.__class__.__name__
[docs]
@classmethod
def get_available(cls) -> dict[str, type["Scenario"]]:
"""
Retrieve all registered ``Scenario`` subclasses.
Returns:
A set containing all subclasses of ``Scenario``.
"""
return cls._scenarios
[docs]
@classmethod
def from_name(cls, name: str) -> type["Scenario"]:
return cls._scenarios[name]
[docs]
def show(self, formats, *args, **kwargs):
"""
Show scenario results in the given formats using registered show functions.
"""
for fmt in formats:
show_func = None
from policy_inspector.utils import get_show_func
show_func = get_show_func(self, fmt)
if show_func:
show_func(self, *args, **kwargs)
else:
logger.warning(
f"No show function registered for {type(self).__name__} and format '{fmt}'"
)
[docs]
def export(self, formats, *args, output_dir: str = None, **kwargs):
"""
Export scenario results in the given formats using registered export functions.
If exporting to HTML, save the file to output_dir or current directory.
"""
from pathlib import Path
for fmt in formats:
export_func = None
from policy_inspector.utils import get_export_func
export_func = get_export_func(self, fmt)
if export_func:
if fmt == "html":
# Determine output path
out_dir = Path(output_dir) if output_dir else Path.cwd()
out_dir.mkdir(parents=True, exist_ok=True)
filename = f"{type(self).__name__.lower()}_report.html"
output_path = out_dir / filename
export_func(
self, *args, output_path=str(output_path), **kwargs
)
logger.info(f"HTML report saved to: {output_path}")
else:
export_func(self, *args, **kwargs)
else:
logger.warning(
f"No export function registered for {type(self).__name__} and format '{fmt}'"
)
[docs]
def execute(self) -> ScenarioResults:
"""
Execute the scenario logic.
Warnings:
This method must be implemented by subclasses.
Returns:
The results of executing.
"""
raise NotImplementedError
[docs]
def analyze(self, results: ScenarioResults) -> AnalysisResult:
"""
Analyze the results obtained from executing a scenario.
Warnings:
This method must be implemented by subclasses.
Args:
results: The results to analyze.
Returns:
The analysis outcome.
"""
raise NotImplementedError
[docs]
def execute_and_analyze(self) -> AnalysisResult:
"""Execute the scenario and analyze the results."""
results = self.execute()
return self.analyze(results)