feat(attack-paths): prowler to cartography data model - WIP - IAM WIP - Pt. II

This commit is contained in:
Josema Camacho
2025-10-23 18:50:01 +02:00
parent 479867fca7
commit 063bb7c875
4 changed files with 59 additions and 9 deletions
@@ -1,3 +1,5 @@
from operator import ne
from cartography.intel import create_indexes as cartography_indexes
from celery.utils.log import get_task_logger
from neo4j import GraphDatabase
@@ -21,7 +23,7 @@ def cartography_sync_scan(
with GraphDatabase.driver("bolt://neo4j:7687", auth=("neo4j", "neo4j_password")) as driver:
with driver.session() as neo4j_session:
# TODO: Add `cartography.intel.create_indexes.run` here, before `sync_aws`
cartography_indexes.run(neo4j_session, None)
return sync_aws( # TODO: Depending on the provider type use the appropriate sync function
tenant_id=tenant_id,
@@ -30,4 +32,4 @@ def cartography_sync_scan(
neo4j_session=neo4j_session,
)
# TODO: Add `cartography.intel.analysis.run` here, after `sync_aws`
# TODO: Check if we need to add `cartography.intel.analysis.run` here, after `sync_aws`
@@ -3,6 +3,8 @@ from typing import Any
import neo4j
from cartography.intel import aws as cartography_aws
from api.db_utils import rls_transaction
from api.models import Provider, ResourceScanSummary
from tasks.jobs.cartography.aws.s3 import sync_aws_s3
@@ -29,14 +31,11 @@ def sync_aws(
common_job_parameters = {"UPDATE_TAG": update_tag, "AWS_ID": account_id}
return {
# "iam": sync_aws_iam(tenant_id, provider_id, account_id, scan_id, regions, neo4j_session, update_tag, common_job_parameters), # noqa: E501
"iam": sync_aws_iam(tenant_id, provider_id, account_id, scan_id, regions, neo4j_session, update_tag, common_job_parameters), # noqa: E501
"s3": sync_aws_s3(tenant_id, provider_id, account_id, scan_id, regions, neo4j_session, update_tag, common_job_parameters), # noqa: E501
"ecs": sync_aws_ecs(tenant_id, provider_id, account_id, scan_id, regions, neo4j_session, update_tag, common_job_parameters), # noqa: E501
}
# TODO: Add `cartography.intel.aws._perform_aws_analysis` here, after all the sync functions
def get_aws_provider_account_id(tenant_id: str, provider_id: str) -> str:
"""
Getting AWS account ID from Prowler DB for a provider.
@@ -240,7 +240,7 @@ def _get_inline_resource_policy_data(resource_data: list[dict[str, Any]]) -> dic
for resource in resource_data:
inline_policies[resource.get("Arn")] = {
policy_name: None # TODO: The policy document statement is missing
policy_name: {} # TODO: The policy document statement is missing
for policy_name in resource.get("InlinePolicies", [])
}
@@ -282,7 +282,7 @@ def _get_resource_managed_policy_data(resource_data: list[dict[str, Any]]) -> di
for resource in resource_data:
attached_policies[resource.get("Arn")] = {
policy.get("PolicyArn"): None # TODO: The policy default version document statement is missing
policy.get("PolicyArn"): {} # TODO: The policy default version document statement is missing
for policy in resource.get("AttachedPolicies", [])
}
@@ -465,6 +465,9 @@ def _get_roles_metadata(
roles_metadata = []
for role in roles_qs:
role_metadata = json.loads(role.metadata)
if not role_metadata.get("name"):
continue
role_metadata["inserted_at"] = role.inserted_at
roles_metadata.append(role_metadata)
@@ -156,6 +156,16 @@ def _get_and_load_s3_bucket_details(
parsed_policy = None # TODO
parsed_statements = None # TODO
parsed_acls = _parse_s3_bucket_acl(bucket_metadata, account_id)
if parsed_acls is not None:
acls.append(parsed_acls)
parsed_policy = _parse_s3_bucket_policy(bucket_metadata)
if parsed_policy is not None:
policies.append(parsed_policy)
# TODO: More work
parsed_encryption = _parse_s3_bucket_encryption(bucket_metadata)
if parsed_encryption is not None:
encryption_configs.append(parsed_encryption)
@@ -190,10 +200,46 @@ def _get_and_load_s3_bucket_details(
cartography_s3._set_default_values(neo4j_session, account_id)
def _parse_s3_bucket_acl(bucket_metadata: dict[str, Any], account_id: str) -> dict[str, Any] | None:
"""
Code based on `cartography.intel.aws.s3.parse_acl`.
# TODO: Key `EmailAddress` is not implemented yet
"""
if not bucket_metadata.get("acl_grantees"):
return None
acl = {
"Grants": [],
"Owner": {
"ID": bucket_metadata.get("owner_id"),
"DisplayName": None,
}
}
for grantee in bucket_metadata.get("acl_grantees"):
acl["Grants"].append({
"Grantee": {
"DisplayName": grantee.get("display_name"),
# "EmailAddress" # TODO: Grantee.EmailAddress
"ID": grantee.get("ID"),
"Type": grantee.get("type"),
"URI": grantee.get("URI"),
},
"Permission": grantee.get("permission"),
})
return cartography_s3.parse_acl(bucket_metadata.get("name"), bucket_metadata.get("acl_grantees"), account_id)
def _parse_s3_bucket_policy(bucket_metadata: dict[str, Any]) -> dict[str, Any] | None:
return None # TODO
def _parse_s3_bucket_encryption(bucket_metadata: dict[str, Any]) -> dict[str, Any] | None:
"""
Code based on `cartography.intel.aws.s3.parse_encryption`.
# TODO: Keys `encryption_key_id` and `bucket_key_enabled` are implemented yet.
# TODO: Keys `encryption_key_id` and `bucket_key_enabled` are not implemented yet
"""
if not bucket_metadata.get("encryption"):