cartography 0.104.0rc2__py3-none-any.whl → 0.123.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cartography/_version.py +16 -3
- cartography/cli.py +466 -5
- cartography/client/aws/__init__.py +19 -0
- cartography/client/aws/ecr.py +51 -0
- cartography/client/core/tx.py +357 -8
- cartography/config.py +153 -0
- cartography/data/azure_permission_relationships.yaml +20 -0
- cartography/data/gcp_permission_relationships.yaml +21 -0
- cartography/data/indexes.cypher +0 -186
- cartography/data/jobs/analysis/aws_ec2_keypair_analysis.json +2 -2
- cartography/data/jobs/analysis/keycloak_inheritance.json +30 -0
- cartography/data/jobs/cleanup/gcp_compute_vpc_cleanup.json +0 -12
- cartography/data/jobs/cleanup/github_repos_cleanup.json +2 -0
- cartography/driftdetect/cli.py +3 -2
- cartography/graph/cleanupbuilder.py +198 -41
- cartography/graph/job.py +54 -6
- cartography/graph/querybuilder.py +528 -27
- cartography/graph/statement.py +5 -1
- cartography/intel/airbyte/__init__.py +105 -0
- cartography/intel/airbyte/connections.py +120 -0
- cartography/intel/airbyte/destinations.py +81 -0
- cartography/intel/airbyte/organizations.py +59 -0
- cartography/intel/airbyte/sources.py +78 -0
- cartography/intel/airbyte/tags.py +64 -0
- cartography/intel/airbyte/users.py +106 -0
- cartography/intel/airbyte/util.py +122 -0
- cartography/intel/airbyte/workspaces.py +63 -0
- cartography/intel/aws/__init__.py +24 -9
- cartography/intel/aws/acm.py +124 -0
- cartography/intel/aws/apigateway.py +253 -22
- cartography/intel/aws/apigatewayv2.py +116 -0
- cartography/intel/aws/cloudtrail.py +17 -39
- cartography/intel/aws/cloudtrail_management_events.py +962 -0
- cartography/intel/aws/cloudwatch.py +150 -4
- cartography/intel/aws/codebuild.py +132 -0
- cartography/intel/aws/cognito.py +201 -0
- cartography/intel/aws/config.py +7 -3
- cartography/intel/aws/ec2/elastic_ip_addresses.py +3 -1
- cartography/intel/aws/ec2/instances.py +25 -1
- cartography/intel/aws/ec2/internet_gateways.py +4 -2
- cartography/intel/aws/ec2/load_balancer_v2s.py +11 -5
- cartography/intel/aws/ec2/network_interfaces.py +5 -1
- cartography/intel/aws/ec2/reserved_instances.py +3 -1
- cartography/intel/aws/ec2/security_groups.py +140 -122
- cartography/intel/aws/ec2/snapshots.py +47 -84
- cartography/intel/aws/ec2/subnets.py +37 -63
- cartography/intel/aws/ec2/tgw.py +11 -5
- cartography/intel/aws/ec2/volumes.py +1 -1
- cartography/intel/aws/ec2/vpc.py +140 -124
- cartography/intel/aws/ec2/vpc_peerings.py +262 -125
- cartography/intel/aws/ecr.py +269 -98
- cartography/intel/aws/ecr_image_layers.py +923 -0
- cartography/intel/aws/ecs.py +251 -380
- cartography/intel/aws/efs.py +179 -11
- cartography/intel/aws/elasticache.py +102 -79
- cartography/intel/aws/elasticsearch.py +13 -4
- cartography/intel/aws/eventbridge.py +164 -0
- cartography/intel/aws/glue.py +181 -0
- cartography/intel/aws/guardduty.py +443 -0
- cartography/intel/aws/iam.py +750 -493
- cartography/intel/aws/identitycenter.py +605 -83
- cartography/intel/aws/inspector.py +221 -105
- cartography/intel/aws/kms.py +173 -201
- cartography/intel/aws/lambda_function.py +272 -189
- cartography/intel/aws/organizations.py +10 -9
- cartography/intel/aws/permission_relationships.py +10 -20
- cartography/intel/aws/rds.py +337 -446
- cartography/intel/aws/redshift.py +9 -4
- cartography/intel/aws/resourcegroupstaggingapi.py +78 -19
- cartography/intel/aws/resources.py +18 -0
- cartography/intel/aws/route53.py +386 -332
- cartography/intel/aws/s3.py +322 -14
- cartography/intel/aws/secretsmanager.py +81 -49
- cartography/intel/aws/securityhub.py +3 -1
- cartography/intel/aws/sns.py +62 -2
- cartography/intel/aws/sqs.py +36 -90
- cartography/intel/aws/ssm.py +3 -5
- cartography/intel/azure/__init__.py +202 -48
- cartography/intel/azure/aks.py +175 -0
- cartography/intel/azure/app_service.py +105 -0
- cartography/intel/azure/compute.py +59 -112
- cartography/intel/azure/container_instances.py +95 -0
- cartography/intel/azure/cosmosdb.py +222 -361
- cartography/intel/azure/data_factory.py +85 -0
- cartography/intel/azure/data_factory_dataset.py +128 -0
- cartography/intel/azure/data_factory_linked_service.py +119 -0
- cartography/intel/azure/data_factory_pipeline.py +142 -0
- cartography/intel/azure/data_lake.py +124 -0
- cartography/intel/azure/event_grid.py +94 -0
- cartography/intel/azure/functions.py +124 -0
- cartography/intel/azure/load_balancers.py +263 -0
- cartography/intel/azure/logic_apps.py +101 -0
- cartography/intel/azure/monitor.py +105 -0
- cartography/intel/azure/network.py +467 -0
- cartography/intel/azure/permission_relationships.py +466 -0
- cartography/intel/azure/rbac.py +309 -0
- cartography/intel/azure/resource_groups.py +82 -0
- cartography/intel/azure/security_center.py +106 -0
- cartography/intel/azure/sql.py +145 -292
- cartography/intel/azure/storage.py +185 -262
- cartography/intel/azure/subscription.py +21 -43
- cartography/intel/azure/tenant.py +39 -30
- cartography/intel/azure/util/common.py +13 -0
- cartography/intel/azure/util/credentials.py +49 -174
- cartography/intel/azure/util/tag.py +41 -0
- cartography/intel/create_indexes.py +2 -1
- cartography/intel/crowdstrike/spotlight.py +5 -2
- cartography/intel/dns.py +5 -2
- cartography/intel/entra/__init__.py +100 -1
- cartography/intel/entra/app_role_assignments.py +284 -0
- cartography/intel/entra/applications.py +182 -0
- cartography/intel/entra/federation/__init__.py +0 -0
- cartography/intel/entra/federation/aws_identity_center.py +77 -0
- cartography/intel/entra/groups.py +198 -0
- cartography/intel/entra/ou.py +48 -24
- cartography/intel/entra/service_principals.py +217 -0
- cartography/intel/entra/users.py +105 -57
- cartography/intel/gcp/__init__.py +334 -396
- cartography/intel/gcp/bigtable_app_profile.py +101 -0
- cartography/intel/gcp/bigtable_backup.py +91 -0
- cartography/intel/gcp/bigtable_cluster.py +93 -0
- cartography/intel/gcp/bigtable_instance.py +86 -0
- cartography/intel/gcp/bigtable_table.py +87 -0
- cartography/intel/gcp/cai.py +292 -0
- cartography/intel/gcp/clients.py +112 -0
- cartography/intel/gcp/compute.py +128 -119
- cartography/intel/gcp/crm/__init__.py +0 -0
- cartography/intel/gcp/crm/folders.py +114 -0
- cartography/intel/gcp/crm/orgs.py +70 -0
- cartography/intel/gcp/crm/projects.py +120 -0
- cartography/intel/gcp/dns.py +83 -169
- cartography/intel/gcp/gke.py +72 -113
- cartography/intel/gcp/iam.py +111 -91
- cartography/intel/gcp/permission_relationships.py +394 -0
- cartography/intel/gcp/policy_bindings.py +225 -0
- cartography/intel/gcp/storage.py +75 -159
- cartography/intel/github/__init__.py +62 -25
- cartography/intel/github/commits.py +423 -0
- cartography/intel/github/repos.py +463 -85
- cartography/intel/github/teams.py +3 -3
- cartography/intel/github/users.py +5 -0
- cartography/intel/github/util.py +12 -0
- cartography/intel/googleworkspace/__init__.py +193 -0
- cartography/intel/googleworkspace/devices.py +254 -0
- cartography/intel/googleworkspace/groups.py +568 -0
- cartography/intel/googleworkspace/oauth_apps.py +259 -0
- cartography/intel/googleworkspace/tenant.py +85 -0
- cartography/intel/googleworkspace/users.py +138 -0
- cartography/intel/gsuite/__init__.py +17 -9
- cartography/intel/gsuite/groups.py +291 -0
- cartography/intel/gsuite/users.py +142 -0
- cartography/intel/jamf/computers.py +7 -1
- cartography/intel/keycloak/__init__.py +153 -0
- cartography/intel/keycloak/authenticationexecutions.py +322 -0
- cartography/intel/keycloak/authenticationflows.py +77 -0
- cartography/intel/keycloak/clients.py +187 -0
- cartography/intel/keycloak/groups.py +126 -0
- cartography/intel/keycloak/identityproviders.py +94 -0
- cartography/intel/keycloak/organizations.py +163 -0
- cartography/intel/keycloak/realms.py +61 -0
- cartography/intel/keycloak/roles.py +202 -0
- cartography/intel/keycloak/scopes.py +73 -0
- cartography/intel/keycloak/users.py +70 -0
- cartography/intel/keycloak/util.py +47 -0
- cartography/intel/kubernetes/__init__.py +60 -14
- cartography/intel/kubernetes/clusters.py +86 -0
- cartography/intel/kubernetes/eks.py +402 -0
- cartography/intel/kubernetes/namespaces.py +59 -57
- cartography/intel/kubernetes/pods.py +168 -75
- cartography/intel/kubernetes/rbac.py +597 -0
- cartography/intel/kubernetes/secrets.py +95 -45
- cartography/intel/kubernetes/services.py +131 -67
- cartography/intel/kubernetes/util.py +142 -14
- cartography/intel/oci/iam.py +23 -9
- cartography/intel/oci/organizations.py +3 -1
- cartography/intel/oci/utils.py +28 -5
- cartography/intel/okta/applications.py +15 -5
- cartography/intel/okta/awssaml.py +14 -10
- cartography/intel/okta/factors.py +3 -1
- cartography/intel/okta/groups.py +5 -2
- cartography/intel/okta/organization.py +3 -1
- cartography/intel/okta/origins.py +3 -1
- cartography/intel/okta/roles.py +5 -2
- cartography/intel/okta/users.py +10 -2
- cartography/intel/ontology/__init__.py +44 -0
- cartography/intel/ontology/devices.py +54 -0
- cartography/intel/ontology/users.py +54 -0
- cartography/intel/ontology/utils.py +176 -0
- cartography/intel/pagerduty/escalation_policies.py +13 -6
- cartography/intel/pagerduty/schedules.py +9 -4
- cartography/intel/pagerduty/services.py +7 -3
- cartography/intel/pagerduty/teams.py +5 -2
- cartography/intel/pagerduty/users.py +3 -1
- cartography/intel/pagerduty/vendors.py +3 -1
- cartography/intel/scaleway/__init__.py +127 -0
- cartography/intel/scaleway/iam/__init__.py +0 -0
- cartography/intel/scaleway/iam/apikeys.py +71 -0
- cartography/intel/scaleway/iam/applications.py +71 -0
- cartography/intel/scaleway/iam/groups.py +71 -0
- cartography/intel/scaleway/iam/users.py +71 -0
- cartography/intel/scaleway/instances/__init__.py +0 -0
- cartography/intel/scaleway/instances/flexibleips.py +86 -0
- cartography/intel/scaleway/instances/instances.py +92 -0
- cartography/intel/scaleway/projects.py +79 -0
- cartography/intel/scaleway/storage/__init__.py +0 -0
- cartography/intel/scaleway/storage/snapshots.py +86 -0
- cartography/intel/scaleway/storage/volumes.py +84 -0
- cartography/intel/scaleway/utils.py +37 -0
- cartography/intel/sentinelone/__init__.py +75 -0
- cartography/intel/sentinelone/account.py +140 -0
- cartography/intel/sentinelone/agent.py +139 -0
- cartography/intel/sentinelone/api.py +124 -0
- cartography/intel/sentinelone/application.py +248 -0
- cartography/intel/sentinelone/cve.py +119 -0
- cartography/intel/sentinelone/utils.py +28 -0
- cartography/intel/slack/__init__.py +78 -0
- cartography/intel/slack/channels.py +80 -0
- cartography/intel/slack/groups.py +90 -0
- cartography/intel/slack/teams.py +65 -0
- cartography/intel/slack/users.py +57 -0
- cartography/intel/slack/utils.py +29 -0
- cartography/intel/spacelift/__init__.py +161 -0
- cartography/intel/spacelift/account.py +73 -0
- cartography/intel/spacelift/ec2_ownership.py +280 -0
- cartography/intel/spacelift/runs.py +463 -0
- cartography/intel/spacelift/spaces.py +112 -0
- cartography/intel/spacelift/stacks.py +119 -0
- cartography/intel/spacelift/util.py +122 -0
- cartography/intel/spacelift/workerpools.py +131 -0
- cartography/intel/spacelift/workers.py +128 -0
- cartography/intel/trivy/__init__.py +272 -0
- cartography/intel/trivy/scanner.py +386 -0
- cartography/models/airbyte/__init__.py +0 -0
- cartography/models/airbyte/connection.py +138 -0
- cartography/models/airbyte/destination.py +75 -0
- cartography/models/airbyte/organization.py +19 -0
- cartography/models/airbyte/source.py +75 -0
- cartography/models/airbyte/stream.py +74 -0
- cartography/models/airbyte/tag.py +69 -0
- cartography/models/airbyte/user.py +115 -0
- cartography/models/airbyte/workspace.py +46 -0
- cartography/models/anthropic/apikey.py +4 -0
- cartography/models/anthropic/user.py +4 -0
- cartography/models/aws/acm/__init__.py +0 -0
- cartography/models/aws/acm/certificate.py +75 -0
- cartography/models/aws/apigateway/__init__.py +0 -0
- cartography/models/aws/apigateway/apigatewaydeployment.py +74 -0
- cartography/models/aws/apigateway/apigatewayintegration.py +79 -0
- cartography/models/aws/apigateway/apigatewaymethod.py +74 -0
- cartography/models/aws/apigatewayv2/__init__.py +0 -0
- cartography/models/aws/apigatewayv2/apigatewayv2.py +53 -0
- cartography/models/aws/cloudtrail/management_events.py +153 -0
- cartography/models/aws/cloudtrail/trail.py +45 -0
- cartography/models/aws/cloudwatch/log_metric_filter.py +79 -0
- cartography/models/aws/cloudwatch/metric_alarm.py +53 -0
- cartography/models/aws/codebuild/__init__.py +0 -0
- cartography/models/aws/codebuild/project.py +49 -0
- cartography/models/aws/cognito/__init__.py +0 -0
- cartography/models/aws/cognito/identity_pool.py +70 -0
- cartography/models/aws/cognito/user_pool.py +47 -0
- cartography/models/aws/dynamodb/tables.py +2 -0
- cartography/models/aws/ec2/instances.py +25 -1
- cartography/models/aws/ec2/networkinterfaces.py +4 -0
- cartography/models/aws/ec2/security_group_rules.py +109 -0
- cartography/models/aws/ec2/security_groups.py +90 -0
- cartography/models/aws/ec2/snapshots.py +58 -0
- cartography/models/aws/ec2/subnet_instance.py +2 -0
- cartography/models/aws/ec2/subnet_networkinterface.py +2 -0
- cartography/models/aws/ec2/subnets.py +65 -0
- cartography/models/aws/ec2/volumes.py +20 -0
- cartography/models/aws/ec2/vpc.py +46 -0
- cartography/models/aws/ec2/vpc_cidr.py +102 -0
- cartography/models/aws/ec2/vpc_peering.py +157 -0
- cartography/models/aws/ecr/__init__.py +0 -0
- cartography/models/aws/ecr/image.py +146 -0
- cartography/models/aws/ecr/image_layer.py +107 -0
- cartography/models/aws/ecr/repository.py +72 -0
- cartography/models/aws/ecr/repository_image.py +95 -0
- cartography/models/aws/ecs/__init__.py +0 -0
- cartography/models/aws/ecs/clusters.py +64 -0
- cartography/models/aws/ecs/container_definitions.py +93 -0
- cartography/models/aws/ecs/container_instances.py +84 -0
- cartography/models/aws/ecs/containers.py +101 -0
- cartography/models/aws/ecs/services.py +134 -0
- cartography/models/aws/ecs/task_definitions.py +135 -0
- cartography/models/aws/ecs/tasks.py +134 -0
- cartography/models/aws/efs/access_point.py +77 -0
- cartography/models/aws/efs/file_system.py +60 -0
- cartography/models/aws/efs/mount_target.py +29 -2
- cartography/models/aws/elasticache/__init__.py +0 -0
- cartography/models/aws/elasticache/cluster.py +65 -0
- cartography/models/aws/elasticache/topic.py +67 -0
- cartography/models/aws/eventbridge/__init__.py +0 -0
- cartography/models/aws/eventbridge/rule.py +77 -0
- cartography/models/aws/eventbridge/target.py +71 -0
- cartography/models/aws/glue/__init__.py +0 -0
- cartography/models/aws/glue/connection.py +51 -0
- cartography/models/aws/glue/job.py +69 -0
- cartography/models/aws/guardduty/__init__.py +1 -0
- cartography/models/aws/guardduty/detectors.py +50 -0
- cartography/models/aws/guardduty/findings.py +121 -0
- cartography/models/aws/iam/access_key.py +103 -0
- cartography/models/aws/iam/account_role.py +24 -0
- cartography/models/aws/iam/federated_principal.py +60 -0
- cartography/models/aws/iam/group.py +60 -0
- cartography/models/aws/iam/group_membership.py +27 -0
- cartography/models/aws/iam/inline_policy.py +78 -0
- cartography/models/aws/iam/managed_policy.py +51 -0
- cartography/models/aws/iam/policy_statement.py +57 -0
- cartography/models/aws/iam/role.py +83 -0
- cartography/models/aws/iam/root_principal.py +52 -0
- cartography/models/aws/iam/service_principal.py +30 -0
- cartography/models/aws/iam/sts_assumerole_allow.py +38 -0
- cartography/models/aws/iam/user.py +59 -0
- cartography/models/aws/identitycenter/awsidentitycenter.py +1 -0
- cartography/models/aws/identitycenter/awspermissionset.py +70 -0
- cartography/models/aws/identitycenter/awssogroup.py +70 -0
- cartography/models/aws/identitycenter/awsssouser.py +49 -9
- cartography/models/aws/inspector/findings.py +37 -0
- cartography/models/aws/inspector/packages.py +1 -31
- cartography/models/aws/kms/__init__.py +0 -0
- cartography/models/aws/kms/aliases.py +86 -0
- cartography/models/aws/kms/grants.py +65 -0
- cartography/models/aws/kms/keys.py +88 -0
- cartography/models/aws/lambda_function/__init__.py +0 -0
- cartography/models/aws/lambda_function/alias.py +74 -0
- cartography/models/aws/lambda_function/event_source_mapping.py +88 -0
- cartography/models/aws/lambda_function/lambda_function.py +91 -0
- cartography/models/aws/lambda_function/layer.py +72 -0
- cartography/models/aws/rds/__init__.py +0 -0
- cartography/models/aws/rds/cluster.py +91 -0
- cartography/models/aws/rds/event_subscription.py +146 -0
- cartography/models/aws/rds/instance.py +156 -0
- cartography/models/aws/rds/snapshot.py +108 -0
- cartography/models/aws/rds/subnet_group.py +101 -0
- cartography/models/aws/route53/__init__.py +0 -0
- cartography/models/aws/route53/dnsrecord.py +235 -0
- cartography/models/aws/route53/nameserver.py +63 -0
- cartography/models/aws/route53/subzone.py +40 -0
- cartography/models/aws/route53/zone.py +47 -0
- cartography/models/aws/s3/notification.py +24 -0
- cartography/models/aws/secretsmanager/secret.py +106 -0
- cartography/models/aws/secretsmanager/secret_version.py +0 -2
- cartography/models/aws/sns/topic_subscription.py +74 -0
- cartography/models/aws/sqs/__init__.py +0 -0
- cartography/models/aws/sqs/queue.py +89 -0
- cartography/models/azure/__init__.py +0 -0
- cartography/models/azure/aks_cluster.py +54 -0
- cartography/models/azure/aks_nodepool.py +54 -0
- cartography/models/azure/app_service.py +59 -0
- cartography/models/azure/container_instance.py +57 -0
- cartography/models/azure/cosmosdb/__init__.py +0 -0
- cartography/models/azure/cosmosdb/account.py +77 -0
- cartography/models/azure/cosmosdb/accountfailoverpolicy.py +77 -0
- cartography/models/azure/cosmosdb/cassandrakeyspace.py +82 -0
- cartography/models/azure/cosmosdb/cassandratable.py +81 -0
- cartography/models/azure/cosmosdb/corspolicy.py +74 -0
- cartography/models/azure/cosmosdb/dblocation.py +120 -0
- cartography/models/azure/cosmosdb/mongodbcollection.py +82 -0
- cartography/models/azure/cosmosdb/mongodbdatabase.py +78 -0
- cartography/models/azure/cosmosdb/privateendpointconnection.py +81 -0
- cartography/models/azure/cosmosdb/sqlcontainer.py +88 -0
- cartography/models/azure/cosmosdb/sqldatabase.py +78 -0
- cartography/models/azure/cosmosdb/tableresource.py +76 -0
- cartography/models/azure/cosmosdb/virtualnetworkrule.py +78 -0
- cartography/models/azure/data_factory/__init__.py +0 -0
- cartography/models/azure/data_factory/data_factory.py +51 -0
- cartography/models/azure/data_factory/data_factory_dataset.py +94 -0
- cartography/models/azure/data_factory/data_factory_linked_service.py +78 -0
- cartography/models/azure/data_factory/data_factory_pipeline.py +93 -0
- cartography/models/azure/data_lake_filesystem.py +51 -0
- cartography/models/azure/event_grid_topic.py +57 -0
- cartography/models/azure/function_app.py +59 -0
- cartography/models/azure/load_balancer/__init__.py +0 -0
- cartography/models/azure/load_balancer/load_balancer.py +49 -0
- cartography/models/azure/load_balancer/load_balancer_backend_pool.py +73 -0
- cartography/models/azure/load_balancer/load_balancer_frontend_ip.py +75 -0
- cartography/models/azure/load_balancer/load_balancer_inbound_nat_rule.py +78 -0
- cartography/models/azure/load_balancer/load_balancer_rule.py +108 -0
- cartography/models/azure/logic_apps.py +56 -0
- cartography/models/azure/monitor.py +54 -0
- cartography/models/azure/network_interface.py +112 -0
- cartography/models/azure/network_security_group.py +50 -0
- cartography/models/azure/permission_relationships.py +60 -0
- cartography/models/azure/principal.py +41 -0
- cartography/models/azure/public_ip_address.py +50 -0
- cartography/models/azure/rbac.py +268 -0
- cartography/models/azure/resource_groups.py +52 -0
- cartography/models/azure/security_center.py +50 -0
- cartography/models/azure/sql/__init__.py +0 -0
- cartography/models/azure/sql/databasethreatdetectionpolicy.py +85 -0
- cartography/models/azure/sql/elasticpool.py +77 -0
- cartography/models/azure/sql/failovergroup.py +73 -0
- cartography/models/azure/sql/recoverabledatabase.py +75 -0
- cartography/models/azure/sql/replicationlink.py +81 -0
- cartography/models/azure/sql/restorabledroppeddatabase.py +82 -0
- cartography/models/azure/sql/restorepoint.py +74 -0
- cartography/models/azure/sql/serveradadministrator.py +74 -0
- cartography/models/azure/sql/serverdnsalias.py +71 -0
- cartography/models/azure/sql/sqldatabase.py +85 -0
- cartography/models/azure/sql/sqlserver.py +50 -0
- cartography/models/azure/sql/transparentdataencryption.py +76 -0
- cartography/models/azure/storage/__init__.py +0 -0
- cartography/models/azure/storage/account.py +59 -0
- cartography/models/azure/storage/blobcontainer.py +85 -0
- cartography/models/azure/storage/blobservice.py +71 -0
- cartography/models/azure/storage/fileservice.py +71 -0
- cartography/models/azure/storage/fileshare.py +82 -0
- cartography/models/azure/storage/queue.py +71 -0
- cartography/models/azure/storage/queueservice.py +73 -0
- cartography/models/azure/storage/table.py +72 -0
- cartography/models/azure/storage/tableservice.py +73 -0
- cartography/models/azure/subnet.py +101 -0
- cartography/models/azure/subscription.py +47 -0
- cartography/models/azure/tags/__init__.py +0 -0
- cartography/models/azure/tags/storage_tag.py +40 -0
- cartography/models/azure/tags/tag.py +37 -0
- cartography/models/azure/tenant.py +17 -0
- cartography/models/azure/virtual_network.py +49 -0
- cartography/models/azure/vm/__init__.py +0 -0
- cartography/models/azure/vm/datadisk.py +80 -0
- cartography/models/azure/vm/disk.py +55 -0
- cartography/models/azure/vm/snapshot.py +56 -0
- cartography/models/azure/vm/virtualmachine.py +59 -0
- cartography/models/bigfix/bigfix_computer.py +1 -1
- cartography/models/cloudflare/member.py +4 -0
- cartography/models/core/common.py +1 -0
- cartography/models/core/nodes.py +15 -2
- cartography/models/core/relationships.py +44 -0
- cartography/models/crowdstrike/hosts.py +1 -1
- cartography/models/digitalocean/droplet.py +2 -0
- cartography/models/duo/endpoint.py +1 -1
- cartography/models/duo/phone.py +2 -2
- cartography/models/duo/user.py +4 -0
- cartography/models/entra/app_role_assignment.py +115 -0
- cartography/models/entra/application.py +49 -0
- cartography/models/entra/entra_user_to_aws_sso.py +41 -0
- cartography/models/entra/group.py +117 -0
- cartography/models/entra/service_principal.py +104 -0
- cartography/models/entra/user.py +42 -51
- cartography/models/gcp/__init__.py +0 -0
- cartography/models/gcp/bigtable/__init__.py +0 -0
- cartography/models/gcp/bigtable/app_profile.py +94 -0
- cartography/models/gcp/bigtable/backup.py +91 -0
- cartography/models/gcp/bigtable/cluster.py +73 -0
- cartography/models/gcp/bigtable/instance.py +52 -0
- cartography/models/gcp/bigtable/table.py +69 -0
- cartography/models/gcp/compute/__init__.py +0 -0
- cartography/models/gcp/compute/subnet.py +74 -0
- cartography/models/gcp/compute/vpc.py +50 -0
- cartography/models/gcp/crm/__init__.py +0 -0
- cartography/models/gcp/crm/folders.py +98 -0
- cartography/models/gcp/crm/organizations.py +21 -0
- cartography/models/gcp/crm/projects.py +100 -0
- cartography/models/gcp/dns.py +109 -0
- cartography/models/gcp/gke.py +69 -0
- cartography/models/gcp/iam.py +3 -0
- cartography/models/gcp/permission_relationships.py +61 -0
- cartography/models/gcp/policy_bindings.py +93 -0
- cartography/models/gcp/storage/__init__.py +0 -0
- cartography/models/gcp/storage/bucket.py +119 -0
- cartography/models/github/commits.py +63 -0
- cartography/models/github/dependencies.py +73 -0
- cartography/models/github/manifests.py +49 -0
- cartography/models/github/users.py +10 -0
- cartography/models/googleworkspace/__init__.py +0 -0
- cartography/models/googleworkspace/device.py +132 -0
- cartography/models/googleworkspace/group.py +382 -0
- cartography/models/googleworkspace/oauth_app.py +124 -0
- cartography/models/googleworkspace/tenant.py +30 -0
- cartography/models/googleworkspace/user.py +113 -0
- cartography/models/gsuite/__init__.py +0 -0
- cartography/models/gsuite/group.py +218 -0
- cartography/models/gsuite/tenant.py +29 -0
- cartography/models/gsuite/user.py +107 -0
- cartography/models/kandji/device.py +1 -2
- cartography/models/keycloak/__init__.py +0 -0
- cartography/models/keycloak/authenticationexecution.py +160 -0
- cartography/models/keycloak/authenticationflow.py +54 -0
- cartography/models/keycloak/client.py +179 -0
- cartography/models/keycloak/group.py +101 -0
- cartography/models/keycloak/identityprovider.py +89 -0
- cartography/models/keycloak/organization.py +116 -0
- cartography/models/keycloak/organizationdomain.py +73 -0
- cartography/models/keycloak/realm.py +173 -0
- cartography/models/keycloak/role.py +126 -0
- cartography/models/keycloak/scope.py +73 -0
- cartography/models/keycloak/user.py +55 -0
- cartography/models/kubernetes/__init__.py +0 -0
- cartography/models/kubernetes/clusterrolebindings.py +138 -0
- cartography/models/kubernetes/clusterroles.py +52 -0
- cartography/models/kubernetes/clusters.py +26 -0
- cartography/models/kubernetes/containers.py +133 -0
- cartography/models/kubernetes/groups.py +107 -0
- cartography/models/kubernetes/namespaces.py +51 -0
- cartography/models/kubernetes/oidc.py +51 -0
- cartography/models/kubernetes/pods.py +80 -0
- cartography/models/kubernetes/rolebindings.py +159 -0
- cartography/models/kubernetes/roles.py +76 -0
- cartography/models/kubernetes/secrets.py +79 -0
- cartography/models/kubernetes/serviceaccounts.py +77 -0
- cartography/models/kubernetes/services.py +108 -0
- cartography/models/kubernetes/users.py +105 -0
- cartography/models/lastpass/user.py +4 -0
- cartography/models/ontology/__init__.py +0 -0
- cartography/models/ontology/device.py +137 -0
- cartography/models/ontology/mapping/__init__.py +76 -0
- cartography/models/ontology/mapping/data/__init__.py +0 -0
- cartography/models/ontology/mapping/data/apikeys.py +93 -0
- cartography/models/ontology/mapping/data/computeinstance.py +95 -0
- cartography/models/ontology/mapping/data/containers.py +88 -0
- cartography/models/ontology/mapping/data/databases.py +182 -0
- cartography/models/ontology/mapping/data/devices.py +194 -0
- cartography/models/ontology/mapping/data/thirdpartyapps.py +140 -0
- cartography/models/ontology/mapping/data/useraccounts.py +416 -0
- cartography/models/ontology/mapping/data/users.py +63 -0
- cartography/models/ontology/mapping/specs.py +85 -0
- cartography/models/ontology/user.py +51 -0
- cartography/models/openai/adminapikey.py +4 -0
- cartography/models/openai/apikey.py +4 -0
- cartography/models/openai/user.py +4 -0
- cartography/models/scaleway/__init__.py +0 -0
- cartography/models/scaleway/iam/__init__.py +0 -0
- cartography/models/scaleway/iam/apikey.py +100 -0
- cartography/models/scaleway/iam/application.py +52 -0
- cartography/models/scaleway/iam/group.py +95 -0
- cartography/models/scaleway/iam/user.py +64 -0
- cartography/models/scaleway/instance/__init__.py +0 -0
- cartography/models/scaleway/instance/flexibleip.py +52 -0
- cartography/models/scaleway/instance/instance.py +120 -0
- cartography/models/scaleway/organization.py +19 -0
- cartography/models/scaleway/project.py +48 -0
- cartography/models/scaleway/storage/__init__.py +0 -0
- cartography/models/scaleway/storage/snapshot.py +78 -0
- cartography/models/scaleway/storage/volume.py +51 -0
- cartography/models/sentinelone/__init__.py +1 -0
- cartography/models/sentinelone/account.py +40 -0
- cartography/models/sentinelone/agent.py +50 -0
- cartography/models/sentinelone/application.py +44 -0
- cartography/models/sentinelone/application_version.py +96 -0
- cartography/models/sentinelone/cve.py +73 -0
- cartography/models/slack/__init__.py +0 -0
- cartography/models/slack/channels.py +92 -0
- cartography/models/slack/group.py +129 -0
- cartography/models/slack/team.py +22 -0
- cartography/models/slack/user.py +62 -0
- cartography/models/snipeit/asset.py +2 -0
- cartography/models/snipeit/user.py +4 -0
- cartography/models/spacelift/__init__.py +0 -0
- cartography/models/spacelift/cloudtrailevent.py +120 -0
- cartography/models/spacelift/run.py +162 -0
- cartography/models/spacelift/space.py +131 -0
- cartography/models/spacelift/spaceliftaccount.py +31 -0
- cartography/models/spacelift/spaceliftgitcommit.py +157 -0
- cartography/models/spacelift/stack.py +96 -0
- cartography/models/spacelift/user.py +63 -0
- cartography/models/spacelift/worker.py +97 -0
- cartography/models/spacelift/workerpool.py +90 -0
- cartography/models/tailscale/device.py +2 -1
- cartography/models/tailscale/user.py +6 -1
- cartography/models/trivy/__init__.py +0 -0
- cartography/models/trivy/findings.py +66 -0
- cartography/models/trivy/fix.py +66 -0
- cartography/models/trivy/package.py +71 -0
- cartography/rules/README.md +1 -0
- cartography/rules/__init__.py +0 -0
- cartography/rules/cli.py +261 -0
- cartography/rules/data/__init__.py +0 -0
- cartography/rules/data/rules/__init__.py +46 -0
- cartography/rules/data/rules/cloud_security_product_deactivated.py +49 -0
- cartography/rules/data/rules/compute_instance_exposed.py +51 -0
- cartography/rules/data/rules/database_instance_exposed.py +53 -0
- cartography/rules/data/rules/delegation_boundary_modifiable.py +90 -0
- cartography/rules/data/rules/identity_administration_privileges.py +100 -0
- cartography/rules/data/rules/inactive_user_active_accounts.py +48 -0
- cartography/rules/data/rules/malicious_npm_dependencies_shai_hulud.py +2222 -0
- cartography/rules/data/rules/mfa_missing.py +46 -0
- cartography/rules/data/rules/object_storage_public.py +100 -0
- cartography/rules/data/rules/policy_administration_privileges.py +104 -0
- cartography/rules/data/rules/unmanaged_accounts.py +43 -0
- cartography/rules/data/rules/workload_identity_admin_capabilities.py +193 -0
- cartography/rules/formatters.py +108 -0
- cartography/rules/runners.py +216 -0
- cartography/rules/spec/__init__.py +0 -0
- cartography/rules/spec/model.py +267 -0
- cartography/rules/spec/result.py +38 -0
- cartography/sync.py +25 -5
- cartography/util.py +101 -31
- {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/METADATA +61 -22
- cartography-0.123.0.dist-info/RECORD +856 -0
- {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/entry_points.txt +1 -0
- cartography/data/jobs/cleanup/aws_dns_cleanup.json +0 -65
- cartography/data/jobs/cleanup/aws_import_account_access_key_cleanup.json +0 -17
- cartography/data/jobs/cleanup/aws_import_ec2_security_groupinfo_cleanup.json +0 -24
- cartography/data/jobs/cleanup/aws_import_groups_cleanup.json +0 -13
- cartography/data/jobs/cleanup/aws_import_identity_center_cleanup.json +0 -16
- cartography/data/jobs/cleanup/aws_import_lambda_cleanup.json +0 -50
- cartography/data/jobs/cleanup/aws_import_principals_cleanup.json +0 -30
- cartography/data/jobs/cleanup/aws_import_rds_clusters_cleanup.json +0 -23
- cartography/data/jobs/cleanup/aws_import_rds_instances_cleanup.json +0 -47
- cartography/data/jobs/cleanup/aws_import_rds_snapshots_cleanup.json +0 -23
- cartography/data/jobs/cleanup/aws_import_roles_cleanup.json +0 -13
- cartography/data/jobs/cleanup/aws_import_secrets_cleanup.json +0 -8
- cartography/data/jobs/cleanup/aws_import_snapshots_cleanup.json +0 -30
- cartography/data/jobs/cleanup/aws_import_users_cleanup.json +0 -8
- cartography/data/jobs/cleanup/aws_import_vpc_cleanup.json +0 -23
- cartography/data/jobs/cleanup/aws_import_vpc_peering_cleanup.json +0 -45
- cartography/data/jobs/cleanup/aws_kms_details.json +0 -10
- cartography/data/jobs/cleanup/azure_cosmosdb_cassandra_keyspace_cleanup.json +0 -25
- cartography/data/jobs/cleanup/azure_cosmosdb_cors_details.json +0 -15
- cartography/data/jobs/cleanup/azure_cosmosdb_mongodb_database_cleanup.json +0 -25
- cartography/data/jobs/cleanup/azure_cosmosdb_sql_database_cleanup.json +0 -25
- cartography/data/jobs/cleanup/azure_cosmosdb_table_resources_cleanup.json +0 -15
- cartography/data/jobs/cleanup/azure_database_account_cleanup.json +0 -85
- cartography/data/jobs/cleanup/azure_import_disks_cleanup.json +0 -15
- cartography/data/jobs/cleanup/azure_import_snapshots_cleanup.json +0 -15
- cartography/data/jobs/cleanup/azure_import_virtual_machines_cleanup.json +0 -25
- cartography/data/jobs/cleanup/azure_sql_server_cleanup.json +0 -125
- cartography/data/jobs/cleanup/azure_storage_account_cleanup.json +0 -95
- cartography/data/jobs/cleanup/azure_subscriptions_cleanup.json +0 -14
- cartography/data/jobs/cleanup/azure_tenant_cleanup.json +0 -9
- cartography/data/jobs/cleanup/gcp_compute_vpc_subnet_cleanup.json +0 -35
- cartography/data/jobs/cleanup/gcp_crm_folder_cleanup.json +0 -23
- cartography/data/jobs/cleanup/gcp_crm_organization_cleanup.json +0 -17
- cartography/data/jobs/cleanup/gcp_crm_project_cleanup.json +0 -23
- cartography/data/jobs/cleanup/gcp_dns_cleanup.json +0 -29
- cartography/data/jobs/cleanup/gcp_gke_cluster_cleanup.json +0 -17
- cartography/data/jobs/cleanup/gcp_storage_bucket_cleanup.json +0 -29
- cartography/data/jobs/cleanup/gsuite_ingest_groups_cleanup.json +0 -23
- cartography/data/jobs/cleanup/gsuite_ingest_users_cleanup.json +0 -11
- cartography/data/jobs/cleanup/kubernetes_import_cleanup.json +0 -70
- cartography/intel/gcp/crm.py +0 -355
- cartography/intel/gsuite/api.py +0 -342
- cartography-0.104.0rc2.dist-info/RECORD +0 -455
- /cartography/data/jobs/{analysis → scoped_analysis}/aws_s3acl_analysis.json +0 -0
- /cartography/models/aws/{apigateway.py → apigateway/apigateway.py} +0 -0
- /cartography/models/aws/{apigatewaycertificate.py → apigateway/apigatewaycertificate.py} +0 -0
- /cartography/models/aws/{apigatewayresource.py → apigateway/apigatewayresource.py} +0 -0
- /cartography/models/aws/{apigatewaystage.py → apigateway/apigatewaystage.py} +0 -0
- {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/WHEEL +0 -0
- {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import boto3
|
|
4
|
+
import neo4j
|
|
5
|
+
import requests
|
|
6
|
+
|
|
7
|
+
from cartography.config import Config
|
|
8
|
+
from cartography.intel.spacelift.account import sync_account
|
|
9
|
+
from cartography.intel.spacelift.ec2_ownership import sync_ec2_ownership
|
|
10
|
+
from cartography.intel.spacelift.runs import sync_runs
|
|
11
|
+
from cartography.intel.spacelift.spaces import sync_spaces
|
|
12
|
+
from cartography.intel.spacelift.stacks import sync_stacks
|
|
13
|
+
from cartography.intel.spacelift.util import get_spacelift_token
|
|
14
|
+
from cartography.intel.spacelift.workerpools import sync_worker_pools
|
|
15
|
+
from cartography.intel.spacelift.workers import sync_workers
|
|
16
|
+
from cartography.stats import get_stats_client
|
|
17
|
+
from cartography.util import merge_module_sync_metadata
|
|
18
|
+
from cartography.util import timeit
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
stat_handler = get_stats_client(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@timeit
|
|
25
|
+
def start_spacelift_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
|
|
26
|
+
"""
|
|
27
|
+
Perform ingestion of Spacelift data.
|
|
28
|
+
:param neo4j_session: Neo4j session for database interface
|
|
29
|
+
:param config: A cartography.config object
|
|
30
|
+
:return: None
|
|
31
|
+
"""
|
|
32
|
+
# Validate configuration
|
|
33
|
+
if not config.spacelift_api_endpoint:
|
|
34
|
+
logger.info("Spacelift API endpoint not configured - skipping this module.")
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
# Determine authentication method and obtain token
|
|
38
|
+
token = None
|
|
39
|
+
if config.spacelift_api_token:
|
|
40
|
+
# Method 1: Use provided token directly
|
|
41
|
+
logger.info("Using provided Spacelift API token for authentication")
|
|
42
|
+
token = config.spacelift_api_token
|
|
43
|
+
elif config.spacelift_api_key_id and config.spacelift_api_key_secret:
|
|
44
|
+
# Method 2: Exchange API key ID and secret for a token
|
|
45
|
+
logger.info("Exchanging Spacelift API key for authentication token")
|
|
46
|
+
try:
|
|
47
|
+
token = get_spacelift_token(
|
|
48
|
+
config.spacelift_api_endpoint,
|
|
49
|
+
config.spacelift_api_key_id,
|
|
50
|
+
config.spacelift_api_key_secret,
|
|
51
|
+
)
|
|
52
|
+
except Exception as e:
|
|
53
|
+
logger.error(f"Failed to obtain Spacelift authentication token: {e}")
|
|
54
|
+
logger.info("Skipping Spacelift module due to authentication failure.")
|
|
55
|
+
return
|
|
56
|
+
else:
|
|
57
|
+
logger.info(
|
|
58
|
+
"Spacelift authentication not configured. "
|
|
59
|
+
"Provide either --spacelift-api-token-env-var or both "
|
|
60
|
+
"--spacelift-api-key-id-env-var and --spacelift-api-key-secret-env-var. "
|
|
61
|
+
"Skipping this module."
|
|
62
|
+
)
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
logger.info("Starting Spacelift ingestion")
|
|
66
|
+
|
|
67
|
+
# Set up common job parameters
|
|
68
|
+
common_job_parameters = {
|
|
69
|
+
"UPDATE_TAG": config.update_tag,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Create authenticated session for Spacelift API
|
|
73
|
+
spacelift_session = requests.Session()
|
|
74
|
+
spacelift_session.headers.update(
|
|
75
|
+
{
|
|
76
|
+
"Authorization": f"Bearer {token}",
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
account_id = sync_account(
|
|
81
|
+
neo4j_session,
|
|
82
|
+
config.spacelift_api_endpoint,
|
|
83
|
+
common_job_parameters,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
common_job_parameters["spacelift_account_id"] = account_id
|
|
87
|
+
|
|
88
|
+
sync_spaces(
|
|
89
|
+
neo4j_session,
|
|
90
|
+
spacelift_session,
|
|
91
|
+
config.spacelift_api_endpoint,
|
|
92
|
+
account_id,
|
|
93
|
+
common_job_parameters,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
sync_stacks(
|
|
97
|
+
neo4j_session,
|
|
98
|
+
spacelift_session,
|
|
99
|
+
config.spacelift_api_endpoint,
|
|
100
|
+
account_id,
|
|
101
|
+
common_job_parameters,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
sync_worker_pools(
|
|
105
|
+
neo4j_session,
|
|
106
|
+
spacelift_session,
|
|
107
|
+
config.spacelift_api_endpoint,
|
|
108
|
+
account_id,
|
|
109
|
+
common_job_parameters,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
sync_workers(
|
|
113
|
+
neo4j_session,
|
|
114
|
+
spacelift_session,
|
|
115
|
+
config.spacelift_api_endpoint,
|
|
116
|
+
account_id,
|
|
117
|
+
common_job_parameters,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Note: Users are synced as part of runs sync (derived from run.triggeredBy)
|
|
121
|
+
sync_runs(
|
|
122
|
+
neo4j_session,
|
|
123
|
+
spacelift_session,
|
|
124
|
+
config.spacelift_api_endpoint,
|
|
125
|
+
account_id,
|
|
126
|
+
common_job_parameters,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# Sync EC2 ownership relationships from CloudTrail data (optional)
|
|
130
|
+
if all(
|
|
131
|
+
hasattr(config, attr)
|
|
132
|
+
for attr in [
|
|
133
|
+
"spacelift_ec2_ownership_s3_bucket",
|
|
134
|
+
"spacelift_ec2_ownership_s3_prefix",
|
|
135
|
+
]
|
|
136
|
+
):
|
|
137
|
+
if hasattr(config, "spacelift_ec2_ownership_aws_profile"):
|
|
138
|
+
aws_session = boto3.Session(
|
|
139
|
+
profile_name=config.spacelift_ec2_ownership_aws_profile
|
|
140
|
+
)
|
|
141
|
+
else:
|
|
142
|
+
aws_session = boto3.Session()
|
|
143
|
+
sync_ec2_ownership(
|
|
144
|
+
neo4j_session,
|
|
145
|
+
aws_session,
|
|
146
|
+
config.spacelift_ec2_ownership_s3_bucket,
|
|
147
|
+
config.spacelift_ec2_ownership_s3_prefix,
|
|
148
|
+
config.update_tag,
|
|
149
|
+
account_id,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
merge_module_sync_metadata(
|
|
153
|
+
neo4j_session,
|
|
154
|
+
group_type="Spacelift",
|
|
155
|
+
group_id=account_id,
|
|
156
|
+
synced_type="SpaceliftData",
|
|
157
|
+
update_tag=config.update_tag,
|
|
158
|
+
stat_handler=stat_handler,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
logger.info("Completed Spacelift ingestion")
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
from urllib.parse import urlparse
|
|
4
|
+
|
|
5
|
+
import neo4j
|
|
6
|
+
|
|
7
|
+
from cartography.client.core.tx import load
|
|
8
|
+
from cartography.graph.job import GraphJob
|
|
9
|
+
from cartography.models.spacelift.spaceliftaccount import SpaceliftAccountSchema
|
|
10
|
+
from cartography.util import timeit
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@timeit
|
|
16
|
+
def get_account(api_endpoint: str) -> str:
|
|
17
|
+
# Parse URL to extract subdomain (account ID)
|
|
18
|
+
parsed = urlparse(api_endpoint)
|
|
19
|
+
hostname = parsed.hostname or ""
|
|
20
|
+
# Extract subdomain (everything before .app.spacelift.io)
|
|
21
|
+
account_id = hostname.split(".")[0] if hostname else ""
|
|
22
|
+
|
|
23
|
+
return account_id
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def load_account(
|
|
27
|
+
neo4j_session: neo4j.Session,
|
|
28
|
+
account_data: dict,
|
|
29
|
+
update_tag: int,
|
|
30
|
+
) -> None:
|
|
31
|
+
|
|
32
|
+
load(
|
|
33
|
+
neo4j_session,
|
|
34
|
+
SpaceliftAccountSchema(),
|
|
35
|
+
[account_data],
|
|
36
|
+
lastupdated=update_tag,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
logger.info(f"Loaded Spacelift account: {account_data['id']}")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@timeit
|
|
43
|
+
def cleanup_account(
|
|
44
|
+
neo4j_session: neo4j.Session,
|
|
45
|
+
common_job_parameters: dict[str, Any],
|
|
46
|
+
) -> None:
|
|
47
|
+
|
|
48
|
+
logger.debug("Running SpaceliftAccount cleanup job")
|
|
49
|
+
GraphJob.from_node_schema(SpaceliftAccountSchema(), common_job_parameters).run(
|
|
50
|
+
neo4j_session
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@timeit
|
|
55
|
+
def sync_account(
|
|
56
|
+
neo4j_session: neo4j.Session,
|
|
57
|
+
api_endpoint: str,
|
|
58
|
+
common_job_parameters: dict[str, Any],
|
|
59
|
+
) -> str:
|
|
60
|
+
|
|
61
|
+
account_id = get_account(api_endpoint)
|
|
62
|
+
|
|
63
|
+
# Transform account data (all fields are just spacelift_account_id)
|
|
64
|
+
account_data = {
|
|
65
|
+
"id": account_id,
|
|
66
|
+
"spacelift_account_id": account_id,
|
|
67
|
+
"name": account_id,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
load_account(neo4j_session, account_data, common_job_parameters["UPDATE_TAG"])
|
|
71
|
+
cleanup_account(neo4j_session, common_job_parameters)
|
|
72
|
+
logger.info(f"Synced Spacelift account: {account_id}")
|
|
73
|
+
return account_id
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import re
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import boto3
|
|
7
|
+
import neo4j
|
|
8
|
+
|
|
9
|
+
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.graph.job import GraphJob
|
|
11
|
+
from cartography.models.spacelift.cloudtrailevent import CloudTrailSpaceliftEventSchema
|
|
12
|
+
from cartography.util import aws_handle_regions
|
|
13
|
+
from cartography.util import timeit
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
# Regex pattern to match EC2 instance IDs
|
|
18
|
+
INSTANCE_ID_PATTERN = re.compile(r"\b(i-[0-9a-f]{8,17})\b")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@aws_handle_regions
|
|
22
|
+
@timeit
|
|
23
|
+
def get_ec2_ownership(
|
|
24
|
+
aws_session: boto3.Session, bucket_name: str, object_prefix: str
|
|
25
|
+
) -> list[dict[str, Any]]:
|
|
26
|
+
"""
|
|
27
|
+
Fetch EC2 ownership data from all JSON files under an S3 prefix containing Athena query results.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
aws_session: AWS session for making S3 requests
|
|
31
|
+
bucket_name: S3 bucket name
|
|
32
|
+
object_prefix: S3 prefix to search for JSON files. Trailing slash is optional and will be normalized.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Aggregated list of CloudTrail records from all JSON files under the prefix
|
|
36
|
+
"""
|
|
37
|
+
# Normalize prefix - ensure it ends with '/' to treat it as a directory
|
|
38
|
+
# This prevents matching prefixes like "data" matching "data-backup" objects
|
|
39
|
+
normalized_prefix = object_prefix.rstrip("/") + "/" if object_prefix else ""
|
|
40
|
+
|
|
41
|
+
logger.info(
|
|
42
|
+
f"Fetching EC2 ownership data from s3://{bucket_name}/{normalized_prefix}"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Create S3 client from the boto3 session
|
|
46
|
+
s3_client = aws_session.client("s3")
|
|
47
|
+
|
|
48
|
+
paginator = s3_client.get_paginator("list_objects_v2")
|
|
49
|
+
page_iterator = paginator.paginate(Bucket=bucket_name, Prefix=normalized_prefix)
|
|
50
|
+
|
|
51
|
+
all_records = []
|
|
52
|
+
total_files_processed = 0
|
|
53
|
+
|
|
54
|
+
for page in page_iterator:
|
|
55
|
+
if "Contents" not in page:
|
|
56
|
+
logger.warning(
|
|
57
|
+
f"No objects found under prefix s3://{bucket_name}/{normalized_prefix}"
|
|
58
|
+
)
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
# Filter for JSON files and exclude the prefix itself if it appears as an object
|
|
62
|
+
json_files = [
|
|
63
|
+
obj
|
|
64
|
+
for obj in page["Contents"]
|
|
65
|
+
if obj["Key"].endswith(".json") and obj["Key"] != normalized_prefix
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
if not json_files:
|
|
69
|
+
continue
|
|
70
|
+
|
|
71
|
+
logger.info(f"Found {len(json_files)} JSON files to process in this page")
|
|
72
|
+
|
|
73
|
+
for s3_object in json_files:
|
|
74
|
+
object_key = s3_object["Key"]
|
|
75
|
+
logger.info(f"Processing {object_key}")
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
response = s3_client.get_object(Bucket=bucket_name, Key=object_key)
|
|
79
|
+
object_body = response["Body"].read()
|
|
80
|
+
json_content = json.loads(object_body.decode("utf-8"))
|
|
81
|
+
|
|
82
|
+
# Handle both single objects and arrays
|
|
83
|
+
if isinstance(json_content, list):
|
|
84
|
+
all_records.extend(json_content)
|
|
85
|
+
records_added = len(json_content)
|
|
86
|
+
else:
|
|
87
|
+
all_records.append(json_content)
|
|
88
|
+
records_added = 1
|
|
89
|
+
|
|
90
|
+
total_files_processed += 1
|
|
91
|
+
logger.info(
|
|
92
|
+
f"Successfully processed {object_key}, added {records_added} records"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
except Exception as e:
|
|
96
|
+
logger.error(f"Failed to process {object_key}: {e}")
|
|
97
|
+
continue
|
|
98
|
+
|
|
99
|
+
logger.info(
|
|
100
|
+
f"Successfully processed {total_files_processed} files and fetched {len(all_records)} total CloudTrail records from S3"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
return all_records
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def extract_spacelift_run_id(useridentity_str: str) -> str | None:
|
|
107
|
+
"""
|
|
108
|
+
Extract Spacelift run ID from the useridentity field.
|
|
109
|
+
"""
|
|
110
|
+
# Only process if 'spacelift' is in the useridentity
|
|
111
|
+
if "spacelift" not in useridentity_str.lower():
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
# Extract the ARN using regex
|
|
115
|
+
# Format: arn=arn:aws:sts::ACCOUNT:assumed-role/ROLE_NAME/SESSION_NAME
|
|
116
|
+
arn_match = re.search(
|
|
117
|
+
r"arn=arn:aws:sts::[^:]+:assumed-role/[^/]+/([^,}]+)", useridentity_str
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
if not arn_match:
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
session_name = arn_match.group(1).strip()
|
|
124
|
+
|
|
125
|
+
# Run ID is the first part before the @ symbol
|
|
126
|
+
run_id = session_name.split("@")[0]
|
|
127
|
+
|
|
128
|
+
return run_id if run_id else None
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def extract_instance_ids(record: dict[str, Any]) -> list[str]:
|
|
132
|
+
"""
|
|
133
|
+
Extract EC2 instance IDs from a CloudTrail record.
|
|
134
|
+
"""
|
|
135
|
+
instance_ids = set()
|
|
136
|
+
|
|
137
|
+
# Check resources field
|
|
138
|
+
resources = record.get("resources")
|
|
139
|
+
if resources:
|
|
140
|
+
resources_str = str(resources)
|
|
141
|
+
instance_ids.update(INSTANCE_ID_PATTERN.findall(resources_str))
|
|
142
|
+
|
|
143
|
+
# Check requestparameters field
|
|
144
|
+
request_params = record.get("requestparameters")
|
|
145
|
+
if request_params:
|
|
146
|
+
instance_ids.update(INSTANCE_ID_PATTERN.findall(str(request_params)))
|
|
147
|
+
|
|
148
|
+
# Check responseelements field
|
|
149
|
+
response_elements = record.get("responseelements")
|
|
150
|
+
if response_elements:
|
|
151
|
+
instance_ids.update(INSTANCE_ID_PATTERN.findall(str(response_elements)))
|
|
152
|
+
|
|
153
|
+
return list(instance_ids)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@timeit
|
|
157
|
+
def transform_ec2_ownership(
|
|
158
|
+
cloudtrail_data: list[dict[str, Any]],
|
|
159
|
+
) -> list[dict[str, Any]]:
|
|
160
|
+
"""
|
|
161
|
+
Transform CloudTrail data to create CloudTrailSpaceliftEvent nodes.
|
|
162
|
+
|
|
163
|
+
This function filters CloudTrail records to find those that have BOTH:
|
|
164
|
+
1. A Spacelift run ID (from the useridentity field)
|
|
165
|
+
2. One or more EC2 instance IDs (from resources, requestparameters, or responseelements)
|
|
166
|
+
|
|
167
|
+
Each CloudTrail record becomes one CloudTrailSpaceliftEvent node that can connect to multiple instances.
|
|
168
|
+
"""
|
|
169
|
+
logger.info(
|
|
170
|
+
f"Transforming {len(cloudtrail_data)} CloudTrail records to create CloudTrailSpaceliftEvent nodes"
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
events = []
|
|
174
|
+
|
|
175
|
+
for record in cloudtrail_data:
|
|
176
|
+
# Extract run ID from useridentity
|
|
177
|
+
useridentity = record.get("useridentity", "")
|
|
178
|
+
run_id = extract_spacelift_run_id(useridentity)
|
|
179
|
+
|
|
180
|
+
if not run_id:
|
|
181
|
+
# Skip records without Spacelift run IDs
|
|
182
|
+
continue
|
|
183
|
+
|
|
184
|
+
# Extract instance IDs
|
|
185
|
+
instance_ids = extract_instance_ids(record)
|
|
186
|
+
|
|
187
|
+
if not instance_ids:
|
|
188
|
+
# Skip records without instance IDs
|
|
189
|
+
continue
|
|
190
|
+
|
|
191
|
+
event_id = record["eventid"]
|
|
192
|
+
|
|
193
|
+
event = {
|
|
194
|
+
"id": event_id,
|
|
195
|
+
"run_id": run_id,
|
|
196
|
+
"instance_ids": instance_ids,
|
|
197
|
+
"event_time": record.get("eventtime"),
|
|
198
|
+
"event_name": record.get("eventname"),
|
|
199
|
+
"aws_account": record.get("account"),
|
|
200
|
+
"aws_region": record.get("awsregion"),
|
|
201
|
+
}
|
|
202
|
+
events.append(event)
|
|
203
|
+
|
|
204
|
+
logger.info(
|
|
205
|
+
f"Created {len(events)} CloudTrailSpaceliftEvent records affecting EC2 instances"
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
return events
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@timeit
|
|
212
|
+
def load_cloudtrail_events(
|
|
213
|
+
neo4j_session: neo4j.Session,
|
|
214
|
+
events: list[dict[str, Any]],
|
|
215
|
+
update_tag: int,
|
|
216
|
+
account_id: str,
|
|
217
|
+
) -> None:
|
|
218
|
+
"""
|
|
219
|
+
Load CloudTrailSpaceliftEvent nodes with relationships to SpaceliftRun and EC2Instance nodes.
|
|
220
|
+
"""
|
|
221
|
+
logger.info(
|
|
222
|
+
f"Loading {len(events)} CloudTrailSpaceliftEvent nodes with relationships into Neo4j"
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
load(
|
|
226
|
+
neo4j_session,
|
|
227
|
+
CloudTrailSpaceliftEventSchema(),
|
|
228
|
+
events,
|
|
229
|
+
lastupdated=update_tag,
|
|
230
|
+
spacelift_account_id=account_id,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
logger.info(f"Successfully loaded {len(events)} CloudTrailSpaceliftEvent nodes")
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
@timeit
|
|
237
|
+
def cleanup_cloudtrail_events(
|
|
238
|
+
neo4j_session: neo4j.Session,
|
|
239
|
+
common_job_parameters: dict[str, Any],
|
|
240
|
+
) -> None:
|
|
241
|
+
"""
|
|
242
|
+
Remove stale CloudTrailSpaceliftEvent nodes and their relationships from Neo4j.
|
|
243
|
+
"""
|
|
244
|
+
logger.debug("Running CloudTrailSpaceliftEvent cleanup job")
|
|
245
|
+
|
|
246
|
+
GraphJob.from_node_schema(
|
|
247
|
+
CloudTrailSpaceliftEventSchema(), common_job_parameters
|
|
248
|
+
).run(neo4j_session)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
@timeit
|
|
252
|
+
def sync_ec2_ownership(
|
|
253
|
+
neo4j_session: neo4j.Session,
|
|
254
|
+
aws_session: boto3.Session,
|
|
255
|
+
bucket_name: str,
|
|
256
|
+
object_prefix: str,
|
|
257
|
+
update_tag: int,
|
|
258
|
+
account_id: str,
|
|
259
|
+
) -> None:
|
|
260
|
+
"""
|
|
261
|
+
Sync EC2 ownership data from CloudTrail into Neo4j as CloudTrailSpaceliftEvent nodes.
|
|
262
|
+
"""
|
|
263
|
+
logger.info("Starting EC2 ownership sync")
|
|
264
|
+
|
|
265
|
+
cloudtrail_data = get_ec2_ownership(aws_session, bucket_name, object_prefix)
|
|
266
|
+
|
|
267
|
+
events = transform_ec2_ownership(cloudtrail_data)
|
|
268
|
+
|
|
269
|
+
if events:
|
|
270
|
+
load_cloudtrail_events(neo4j_session, events, update_tag, account_id)
|
|
271
|
+
else:
|
|
272
|
+
logger.warning("No CloudTrail events found - no nodes created")
|
|
273
|
+
|
|
274
|
+
common_job_parameters = {
|
|
275
|
+
"UPDATE_TAG": update_tag,
|
|
276
|
+
"spacelift_account_id": account_id,
|
|
277
|
+
}
|
|
278
|
+
cleanup_cloudtrail_events(neo4j_session, common_job_parameters)
|
|
279
|
+
|
|
280
|
+
logger.info("EC2 ownership sync completed successfully")
|