mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 21:11:53 +00:00
refactor(sdk): extract is_tool_wrapper_provider to leaf module to break import cycle
This commit is contained in:
@@ -2,10 +2,10 @@ import sys
|
||||
|
||||
from colorama import Fore, Style
|
||||
|
||||
from prowler.config.config import EXTERNAL_TOOL_PROVIDERS
|
||||
from prowler.lib.check.check import parse_checks_from_file
|
||||
from prowler.lib.check.compliance_models import Compliance
|
||||
from prowler.lib.check.models import CheckMetadata, Severity
|
||||
from prowler.lib.check.tool_wrapper import is_tool_wrapper_provider
|
||||
from prowler.lib.logger import logger
|
||||
|
||||
|
||||
@@ -25,8 +25,13 @@ def load_checks_to_execute(
|
||||
) -> set:
|
||||
"""Generate the list of checks to execute based on the cloud provider and the input arguments given"""
|
||||
try:
|
||||
# Bypass check loading for providers that use external tools directly
|
||||
if provider in EXTERNAL_TOOL_PROVIDERS:
|
||||
# Bypass check loading for tool-wrapper providers — they delegate
|
||||
# scanning to an external tool and have no checks to recover.
|
||||
# Single source of truth across __main__, the CheckMetadata validators,
|
||||
# check discovery and this loader, covering both built-in tool wrappers
|
||||
# (iac/llm/image) and external plug-ins that declare
|
||||
# `is_external_tool_provider = True` via the contract.
|
||||
if is_tool_wrapper_provider(provider):
|
||||
return set()
|
||||
|
||||
# Local subsets
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
"""Standalone helper for tool-wrapper provider detection.
|
||||
|
||||
A provider is a "tool wrapper" if it delegates scanning to an external tool
|
||||
(Trivy, promptfoo, etc.) instead of running checks/services through the
|
||||
standard Prowler engine. This module is the single source of truth for that
|
||||
classification across the codebase.
|
||||
|
||||
Kept as a leaf module with no Prowler imports beyond the leaf
|
||||
`external_tool_providers` so it can be referenced from `prowler.lib.check.*`
|
||||
and `prowler.providers.common.provider` without forming an import cycle.
|
||||
"""
|
||||
|
||||
import importlib.metadata
|
||||
|
||||
from prowler.lib.check.external_tool_providers import EXTERNAL_TOOL_PROVIDERS
|
||||
|
||||
# Module-level cache for entry-point classes consulted by this helper.
|
||||
# Independent of `Provider._ep_providers` to keep this module leaf — the cost
|
||||
# of a duplicate cache entry is negligible (one class object per external
|
||||
# provider, loaded lazily on first lookup).
|
||||
_ep_class_cache: dict = {}
|
||||
|
||||
|
||||
def _load_ep_class(provider: str):
|
||||
"""Return the entry-point provider class for `provider`, or None.
|
||||
|
||||
Caches the result in `_ep_class_cache`. Errors during entry-point loading
|
||||
are swallowed (returning None) so a broken plug-in never crashes the
|
||||
is-tool-wrapper check; it just falls through to "not a tool wrapper".
|
||||
"""
|
||||
if provider in _ep_class_cache:
|
||||
return _ep_class_cache[provider]
|
||||
for ep in importlib.metadata.entry_points(group="prowler.providers"):
|
||||
if ep.name == provider:
|
||||
try:
|
||||
cls = ep.load()
|
||||
except Exception:
|
||||
cls = None
|
||||
_ep_class_cache[provider] = cls
|
||||
return cls
|
||||
_ep_class_cache[provider] = None
|
||||
return None
|
||||
|
||||
|
||||
def is_tool_wrapper_provider(provider: str) -> bool:
|
||||
"""Return True if the provider delegates scanning to an external tool.
|
||||
|
||||
Combines the built-in `EXTERNAL_TOOL_PROVIDERS` frozenset (fast path for
|
||||
iac/llm/image) with the `is_external_tool_provider` class attribute of
|
||||
external plug-ins registered via entry points. This is the single source
|
||||
of truth consulted by `__main__`, the `CheckMetadata` validators, the
|
||||
check-loading utilities, and the checks loader.
|
||||
"""
|
||||
if provider in EXTERNAL_TOOL_PROVIDERS:
|
||||
return True
|
||||
cls = _load_ep_class(provider)
|
||||
return bool(cls and getattr(cls, "is_external_tool_provider", False))
|
||||
@@ -4,7 +4,7 @@ import os
|
||||
import sys
|
||||
from pkgutil import walk_packages
|
||||
|
||||
from prowler.lib.check.external_tool_providers import EXTERNAL_TOOL_PROVIDERS
|
||||
from prowler.lib.check.tool_wrapper import is_tool_wrapper_provider
|
||||
from prowler.lib.logger import logger
|
||||
|
||||
|
||||
@@ -52,9 +52,7 @@ def recover_checks_from_provider(
|
||||
# Single source of truth: combines the EXTERNAL_TOOL_PROVIDERS
|
||||
# frozenset (built-ins) with the per-provider `is_external_tool_provider`
|
||||
# class attribute (so external plug-ins opt in via the contract).
|
||||
from prowler.providers.common.provider import Provider
|
||||
|
||||
if Provider.is_tool_wrapper_provider(provider):
|
||||
if is_tool_wrapper_provider(provider):
|
||||
return []
|
||||
|
||||
checks = []
|
||||
@@ -124,12 +122,7 @@ def recover_checks_from_service(service_list: list, provider: str) -> set:
|
||||
try:
|
||||
# Bypass check loading for tool-wrapper providers — symmetric with
|
||||
# `recover_checks_from_provider` above, using the same source of truth.
|
||||
# NOTE: master gated this on `provider in EXTERNAL_TOOL_PROVIDERS`
|
||||
# (covering iac/llm/image). The PR temporarily narrowed it to `== "iac"`;
|
||||
# restoring the full set via the helper.
|
||||
from prowler.providers.common.provider import Provider
|
||||
|
||||
if Provider.is_tool_wrapper_provider(provider):
|
||||
if is_tool_wrapper_provider(provider):
|
||||
return set()
|
||||
|
||||
checks = set()
|
||||
|
||||
@@ -9,10 +9,7 @@ from argparse import Namespace
|
||||
from importlib import import_module
|
||||
from typing import Any, Optional
|
||||
|
||||
from prowler.config.config import (
|
||||
EXTERNAL_TOOL_PROVIDERS,
|
||||
load_and_validate_config_file,
|
||||
)
|
||||
from prowler.config.config import load_and_validate_config_file
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.lib.mutelist.mutelist import Mutelist
|
||||
|
||||
@@ -592,16 +589,15 @@ class Provider(ABC):
|
||||
def is_tool_wrapper_provider(provider: str) -> bool:
|
||||
"""Return True if the provider delegates scanning to an external tool.
|
||||
|
||||
Combines the built-in EXTERNAL_TOOL_PROVIDERS frozenset (fast path for
|
||||
iac/llm/image) with the `is_external_tool_provider` class attribute of
|
||||
external plug-in providers registered via entry points. This is the
|
||||
single source of truth consulted by the execution flow and the
|
||||
CheckMetadata validators.
|
||||
Delegates to `prowler.lib.check.tool_wrapper.is_tool_wrapper_provider`,
|
||||
the leaf module that holds the actual logic. Kept on `Provider` as a
|
||||
convenience entry point for callers that already import `Provider`.
|
||||
"""
|
||||
if provider in EXTERNAL_TOOL_PROVIDERS:
|
||||
return True
|
||||
ep_cls = Provider._load_ep_provider(provider)
|
||||
return bool(ep_cls and getattr(ep_cls, "is_external_tool_provider", False))
|
||||
from prowler.lib.check.tool_wrapper import (
|
||||
is_tool_wrapper_provider as _impl,
|
||||
)
|
||||
|
||||
return _impl(provider)
|
||||
|
||||
@staticmethod
|
||||
def is_builtin(provider: str) -> bool:
|
||||
|
||||
Reference in New Issue
Block a user