46 lines
1.2 KiB
Python
46 lines
1.2 KiB
Python
"""Detector base class and registration mechanism."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Iterable
|
|
|
|
from ..context import CaptureContext
|
|
from ..findings import Finding
|
|
|
|
|
|
class BaseDetector:
|
|
#: Short unique name referenced by findings and reports.
|
|
name: str = "BaseDetector"
|
|
#: Human-readable description of the detector scope.
|
|
description: str = ""
|
|
|
|
def detect(self, ctx: CaptureContext) -> Iterable[Finding]: # pragma: no cover
|
|
raise NotImplementedError
|
|
|
|
def _tag(self, findings: Iterable[Finding]) -> list[Finding]:
|
|
out = []
|
|
for f in findings:
|
|
f.detector = self.name
|
|
out.append(f)
|
|
return out
|
|
|
|
|
|
# ---- Registry ------------------------------------------------------------- #
|
|
|
|
_REGISTRY: dict[str, type[BaseDetector]] = {}
|
|
|
|
|
|
def register(cls: type[BaseDetector]) -> type[BaseDetector]:
|
|
"""Register a detector class."""
|
|
_REGISTRY[cls.name] = cls
|
|
return cls
|
|
|
|
|
|
def all_detector_classes() -> list[type[BaseDetector]]:
|
|
return list(_REGISTRY.values())
|
|
|
|
|
|
def build_all() -> list[BaseDetector]:
|
|
"""Instantiate all registered detector classes."""
|
|
return [cls() for cls in _REGISTRY.values()]
|