mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-04 19:21:51 +00:00
132 lines
4.8 KiB
Python
132 lines
4.8 KiB
Python
from collections.abc import Callable
|
|
from uuid import UUID
|
|
|
|
from config.env import env
|
|
from tasks.jobs.attack_paths import provider_config as _provider_config
|
|
|
|
# Re-export provider config objects so existing imports keep working.
|
|
AWS_CONFIG = _provider_config.AWS_CONFIG
|
|
NormalizedList = _provider_config.NormalizedList
|
|
PROVIDER_CONFIGS = _provider_config.PROVIDER_CONFIGS
|
|
ProviderConfig = _provider_config.ProviderConfig
|
|
|
|
# Batch size for Neo4j write operations (resource labeling, cleanup)
|
|
BATCH_SIZE = env.int("ATTACK_PATHS_BATCH_SIZE", 1000)
|
|
# Batch size for Postgres findings fetch (keyset pagination page size)
|
|
FINDINGS_BATCH_SIZE = env.int("ATTACK_PATHS_FINDINGS_BATCH_SIZE", 1000)
|
|
# Batch size for temp-to-tenant graph sync (nodes and relationships per cursor page)
|
|
SYNC_BATCH_SIZE = env.int("ATTACK_PATHS_SYNC_BATCH_SIZE", 1000)
|
|
|
|
# Neo4j internal labels (Prowler-specific, not provider-specific)
|
|
# - `Internet`: Singleton node representing external internet access for exposed-resource queries
|
|
# - `ProwlerFinding`: Label for finding nodes created by Prowler and linked to cloud resources
|
|
# - `_ProviderResource`: Added to ALL synced nodes for provider isolation and drop/query ops
|
|
INTERNET_NODE_LABEL = "Internet"
|
|
PROWLER_FINDING_LABEL = "ProwlerFinding"
|
|
PROVIDER_RESOURCE_LABEL = "_ProviderResource"
|
|
|
|
# Dynamic isolation labels that contain entity UUIDs and are added to every synced node during sync
|
|
# Format: `_Tenant_{uuid_no_hyphens}`, `_Provider_{uuid_no_hyphens}`
|
|
TENANT_LABEL_PREFIX = "_Tenant_"
|
|
PROVIDER_LABEL_PREFIX = "_Provider_"
|
|
DYNAMIC_ISOLATION_PREFIXES = [TENANT_LABEL_PREFIX, PROVIDER_LABEL_PREFIX]
|
|
|
|
|
|
# Labels added by Prowler that should be filtered from API responses
|
|
# Derived from provider configs + common internal labels
|
|
INTERNAL_LABELS: list[str] = [
|
|
"Tenant", # From Cartography, but it looks like it's ours
|
|
PROVIDER_RESOURCE_LABEL,
|
|
*[config.resource_label for config in PROVIDER_CONFIGS.values()],
|
|
]
|
|
|
|
# Provider isolation properties
|
|
PROVIDER_ELEMENT_ID_PROPERTY = "_provider_element_id"
|
|
|
|
PROVIDER_ISOLATION_PROPERTIES: list[str] = [
|
|
PROVIDER_ELEMENT_ID_PROPERTY,
|
|
]
|
|
|
|
# Cartography bookkeeping metadata
|
|
CARTOGRAPHY_METADATA_PROPERTIES: list[str] = [
|
|
"lastupdated",
|
|
"firstseen",
|
|
"_module_name",
|
|
"_module_version",
|
|
]
|
|
|
|
INTERNAL_PROPERTIES: list[str] = [
|
|
*PROVIDER_ISOLATION_PROPERTIES,
|
|
*CARTOGRAPHY_METADATA_PROPERTIES,
|
|
]
|
|
|
|
|
|
# Provider Config Accessors
|
|
|
|
|
|
def is_provider_available(provider_type: str) -> bool:
|
|
"""Check if a provider type is available for Attack Paths scans."""
|
|
return provider_type in PROVIDER_CONFIGS
|
|
|
|
|
|
def get_cartography_ingestion_function(provider_type: str) -> Callable | None:
|
|
"""Get the Cartography ingestion function for a provider type."""
|
|
config = PROVIDER_CONFIGS.get(provider_type)
|
|
return config.ingestion_function if config else None
|
|
|
|
|
|
def get_root_node_label(provider_type: str) -> str:
|
|
"""Get the root node label for a provider type (e.g., AWSAccount)."""
|
|
config = PROVIDER_CONFIGS.get(provider_type)
|
|
return config.root_node_label if config else "UnknownProviderAccount"
|
|
|
|
|
|
def get_node_uid_field(provider_type: str) -> str:
|
|
"""Get the UID field for a provider type (e.g., arn for AWS)."""
|
|
config = PROVIDER_CONFIGS.get(provider_type)
|
|
return config.uid_field if config else "UnknownProviderUID"
|
|
|
|
|
|
def get_provider_resource_label(provider_type: str) -> str:
|
|
"""Get the resource label for a provider type (e.g., `_AWSResource`)."""
|
|
config = PROVIDER_CONFIGS.get(provider_type)
|
|
return config.resource_label if config else "_UnknownProviderResource"
|
|
|
|
|
|
def _identity_short_uid(uid: str) -> str:
|
|
"""Fallback short-uid extractor for providers without a custom mapping."""
|
|
return uid
|
|
|
|
|
|
def get_short_uid_extractor(provider_type: str) -> Callable[[str], str]:
|
|
"""Get the short-uid extractor for a provider type.
|
|
|
|
Returns an identity function when the provider is unknown, so callers can
|
|
rely on a callable always being returned.
|
|
"""
|
|
config = PROVIDER_CONFIGS.get(provider_type)
|
|
return config.short_uid_extractor if config else _identity_short_uid
|
|
|
|
|
|
# Dynamic Isolation Label Helpers
|
|
|
|
|
|
def _normalize_uuid(value: str | UUID) -> str:
|
|
"""Strip hyphens from a UUID string for use in Neo4j labels."""
|
|
return str(value).replace("-", "")
|
|
|
|
|
|
def get_tenant_label(tenant_id: str | UUID) -> str:
|
|
"""Get the Neo4j label for a tenant (e.g., `_Tenant_019c41ee7df37deca684d839f95619f8`)."""
|
|
return f"{TENANT_LABEL_PREFIX}{_normalize_uuid(tenant_id)}"
|
|
|
|
|
|
def get_provider_label(provider_id: str | UUID) -> str:
|
|
"""Get the Neo4j label for a provider (e.g., `_Provider_019c41ee7df37deca684d839f95619f8`)."""
|
|
return f"{PROVIDER_LABEL_PREFIX}{_normalize_uuid(provider_id)}"
|
|
|
|
|
|
def is_dynamic_isolation_label(label: str) -> bool:
|
|
"""Check if a label is a dynamic tenant/provider isolation label."""
|
|
return any(label.startswith(prefix) for prefix in DYNAMIC_ISOLATION_PREFIXES)
|