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
cartography/intel/gcp/iam.py
CHANGED
|
@@ -31,33 +31,49 @@ def get_gcp_service_accounts(
|
|
|
31
31
|
:return: A list of dictionaries representing GCP service accounts.
|
|
32
32
|
"""
|
|
33
33
|
service_accounts: List[Dict[str, Any]] = []
|
|
34
|
-
|
|
34
|
+
request = (
|
|
35
|
+
iam_client.projects()
|
|
36
|
+
.serviceAccounts()
|
|
37
|
+
.list(
|
|
38
|
+
name=f"projects/{project_id}",
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
while request is not None:
|
|
42
|
+
response = request.execute()
|
|
43
|
+
if "accounts" in response:
|
|
44
|
+
service_accounts.extend(response["accounts"])
|
|
35
45
|
request = (
|
|
36
46
|
iam_client.projects()
|
|
37
47
|
.serviceAccounts()
|
|
38
|
-
.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
)
|
|
42
|
-
while request is not None:
|
|
43
|
-
response = request.execute()
|
|
44
|
-
if "accounts" in response:
|
|
45
|
-
service_accounts.extend(response["accounts"])
|
|
46
|
-
request = (
|
|
47
|
-
iam_client.projects()
|
|
48
|
-
.serviceAccounts()
|
|
49
|
-
.list_next(
|
|
50
|
-
previous_request=request,
|
|
51
|
-
previous_response=response,
|
|
52
|
-
)
|
|
48
|
+
.list_next(
|
|
49
|
+
previous_request=request,
|
|
50
|
+
previous_response=response,
|
|
53
51
|
)
|
|
54
|
-
except Exception as e:
|
|
55
|
-
logger.warning(
|
|
56
|
-
f"Error retrieving service accounts for project {project_id}: {e}"
|
|
57
52
|
)
|
|
58
53
|
return service_accounts
|
|
59
54
|
|
|
60
55
|
|
|
56
|
+
@timeit
|
|
57
|
+
def get_gcp_predefined_roles(iam_client: Resource) -> List[Dict]:
|
|
58
|
+
"""
|
|
59
|
+
Retrieve all predefined (Google-managed) IAM roles.
|
|
60
|
+
|
|
61
|
+
Predefined roles are global and not project-specific, so they can be fetched once
|
|
62
|
+
and reused across all target projects. This is useful for the CAI fallback where
|
|
63
|
+
the target project may not have the IAM API enabled.
|
|
64
|
+
|
|
65
|
+
:param iam_client: The IAM resource object created by googleapiclient.discovery.build().
|
|
66
|
+
:return: A list of dictionaries representing GCP predefined roles.
|
|
67
|
+
"""
|
|
68
|
+
roles: List[Dict] = []
|
|
69
|
+
predefined_req = iam_client.roles().list(view="FULL")
|
|
70
|
+
while predefined_req is not None:
|
|
71
|
+
resp = predefined_req.execute()
|
|
72
|
+
roles.extend(resp.get("roles", []))
|
|
73
|
+
predefined_req = iam_client.roles().list_next(predefined_req, resp)
|
|
74
|
+
return roles
|
|
75
|
+
|
|
76
|
+
|
|
61
77
|
@timeit
|
|
62
78
|
def get_gcp_roles(iam_client: Resource, project_id: str) -> List[Dict]:
|
|
63
79
|
"""
|
|
@@ -67,27 +83,42 @@ def get_gcp_roles(iam_client: Resource, project_id: str) -> List[Dict]:
|
|
|
67
83
|
:param project_id: The GCP Project ID to retrieve roles from.
|
|
68
84
|
:return: A list of dictionaries representing GCP roles.
|
|
69
85
|
"""
|
|
70
|
-
|
|
71
|
-
roles = []
|
|
86
|
+
roles = []
|
|
72
87
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
88
|
+
# Get custom roles
|
|
89
|
+
custom_req = iam_client.projects().roles().list(parent=f"projects/{project_id}")
|
|
90
|
+
while custom_req is not None:
|
|
91
|
+
resp = custom_req.execute()
|
|
92
|
+
roles.extend(resp.get("roles", []))
|
|
93
|
+
custom_req = iam_client.projects().roles().list_next(custom_req, resp)
|
|
79
94
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
while predefined_req is not None:
|
|
83
|
-
resp = predefined_req.execute()
|
|
84
|
-
roles.extend(resp.get("roles", []))
|
|
85
|
-
predefined_req = iam_client.roles().list_next(predefined_req, resp)
|
|
95
|
+
# Get predefined roles (global, not project-specific)
|
|
96
|
+
roles.extend(get_gcp_predefined_roles(iam_client))
|
|
86
97
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
98
|
+
return roles
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def transform_gcp_service_accounts(
|
|
102
|
+
raw_accounts: List[Dict[str, Any]],
|
|
103
|
+
project_id: str,
|
|
104
|
+
) -> List[Dict[str, Any]]:
|
|
105
|
+
"""
|
|
106
|
+
Transform raw GCP service accounts into loader-friendly dicts.
|
|
107
|
+
"""
|
|
108
|
+
result: List[Dict[str, Any]] = []
|
|
109
|
+
for sa in raw_accounts:
|
|
110
|
+
result.append(
|
|
111
|
+
{
|
|
112
|
+
"id": sa["uniqueId"],
|
|
113
|
+
"email": sa.get("email"),
|
|
114
|
+
"displayName": sa.get("displayName"),
|
|
115
|
+
"oauth2ClientId": sa.get("oauth2ClientId"),
|
|
116
|
+
"uniqueId": sa.get("uniqueId"),
|
|
117
|
+
"disabled": sa.get("disabled", False),
|
|
118
|
+
"projectId": project_id,
|
|
119
|
+
},
|
|
120
|
+
)
|
|
121
|
+
return result
|
|
91
122
|
|
|
92
123
|
|
|
93
124
|
@timeit
|
|
@@ -99,38 +130,55 @@ def load_gcp_service_accounts(
|
|
|
99
130
|
) -> None:
|
|
100
131
|
"""
|
|
101
132
|
Load GCP service account data into Neo4j.
|
|
102
|
-
|
|
103
|
-
:param neo4j_session: The Neo4j session.
|
|
104
|
-
:param service_accounts: A list of service account data to load.
|
|
105
|
-
:param project_id: The GCP Project ID associated with the service accounts.
|
|
106
|
-
:param gcp_update_tag: The timestamp of the current sync run.
|
|
107
133
|
"""
|
|
108
134
|
logger.debug(
|
|
109
135
|
f"Loading {len(service_accounts)} service accounts for project {project_id}"
|
|
110
136
|
)
|
|
111
|
-
transformed_service_accounts = []
|
|
112
|
-
for sa in service_accounts:
|
|
113
|
-
transformed_sa = {
|
|
114
|
-
"id": sa["uniqueId"],
|
|
115
|
-
"email": sa.get("email"),
|
|
116
|
-
"displayName": sa.get("displayName"),
|
|
117
|
-
"oauth2ClientId": sa.get("oauth2ClientId"),
|
|
118
|
-
"uniqueId": sa.get("uniqueId"),
|
|
119
|
-
"disabled": sa.get("disabled", False),
|
|
120
|
-
"projectId": project_id,
|
|
121
|
-
}
|
|
122
|
-
transformed_service_accounts.append(transformed_sa)
|
|
123
137
|
|
|
124
138
|
load(
|
|
125
139
|
neo4j_session,
|
|
126
140
|
GCPServiceAccountSchema(),
|
|
127
|
-
|
|
141
|
+
service_accounts,
|
|
128
142
|
lastupdated=gcp_update_tag,
|
|
129
143
|
projectId=project_id,
|
|
130
|
-
additional_labels=["GCPPrincipal"],
|
|
131
144
|
)
|
|
132
145
|
|
|
133
146
|
|
|
147
|
+
def transform_gcp_roles(
|
|
148
|
+
raw_roles: List[Dict[str, Any]],
|
|
149
|
+
project_id: str,
|
|
150
|
+
) -> List[Dict[str, Any]]:
|
|
151
|
+
"""
|
|
152
|
+
Transform raw GCP roles into loader-friendly dicts.
|
|
153
|
+
"""
|
|
154
|
+
result: List[Dict[str, Any]] = []
|
|
155
|
+
for role in raw_roles:
|
|
156
|
+
role_name = role["name"]
|
|
157
|
+
if role_name.startswith("roles/"):
|
|
158
|
+
role_type = (
|
|
159
|
+
"BASIC"
|
|
160
|
+
if role_name in ["roles/owner", "roles/editor", "roles/viewer"]
|
|
161
|
+
else "PREDEFINED"
|
|
162
|
+
)
|
|
163
|
+
else:
|
|
164
|
+
role_type = "CUSTOM"
|
|
165
|
+
|
|
166
|
+
result.append(
|
|
167
|
+
{
|
|
168
|
+
"id": role_name,
|
|
169
|
+
"name": role_name,
|
|
170
|
+
"title": role.get("title"),
|
|
171
|
+
"description": role.get("description"),
|
|
172
|
+
"deleted": role.get("deleted", False),
|
|
173
|
+
"etag": role.get("etag"),
|
|
174
|
+
"includedPermissions": role.get("includedPermissions", []),
|
|
175
|
+
"roleType": role_type,
|
|
176
|
+
"projectId": project_id,
|
|
177
|
+
},
|
|
178
|
+
)
|
|
179
|
+
return result
|
|
180
|
+
|
|
181
|
+
|
|
134
182
|
@timeit
|
|
135
183
|
def load_gcp_roles(
|
|
136
184
|
neo4j_session: neo4j.Session,
|
|
@@ -140,41 +188,13 @@ def load_gcp_roles(
|
|
|
140
188
|
) -> None:
|
|
141
189
|
"""
|
|
142
190
|
Load GCP role data into Neo4j.
|
|
143
|
-
|
|
144
|
-
:param neo4j_session: The Neo4j session.
|
|
145
|
-
:param roles: A list of role data to load.
|
|
146
|
-
:param project_id: The GCP Project ID associated with the roles.
|
|
147
|
-
:param gcp_update_tag: The timestamp of the current sync run.
|
|
148
191
|
"""
|
|
149
192
|
logger.debug(f"Loading {len(roles)} roles for project {project_id}")
|
|
150
|
-
transformed_roles = []
|
|
151
|
-
for role in roles:
|
|
152
|
-
role_name = role["name"]
|
|
153
|
-
if role_name.startswith("roles/"):
|
|
154
|
-
if role_name in ["roles/owner", "roles/editor", "roles/viewer"]:
|
|
155
|
-
role_type = "BASIC"
|
|
156
|
-
else:
|
|
157
|
-
role_type = "PREDEFINED"
|
|
158
|
-
else:
|
|
159
|
-
role_type = "CUSTOM"
|
|
160
|
-
|
|
161
|
-
transformed_role = {
|
|
162
|
-
"id": role_name,
|
|
163
|
-
"name": role_name,
|
|
164
|
-
"title": role.get("title"),
|
|
165
|
-
"description": role.get("description"),
|
|
166
|
-
"deleted": role.get("deleted", False),
|
|
167
|
-
"etag": role.get("etag"),
|
|
168
|
-
"includedPermissions": role.get("includedPermissions", []),
|
|
169
|
-
"roleType": role_type,
|
|
170
|
-
"projectId": project_id,
|
|
171
|
-
}
|
|
172
|
-
transformed_roles.append(transformed_role)
|
|
173
193
|
|
|
174
194
|
load(
|
|
175
195
|
neo4j_session,
|
|
176
196
|
GCPRoleSchema(),
|
|
177
|
-
|
|
197
|
+
roles,
|
|
178
198
|
lastupdated=gcp_update_tag,
|
|
179
199
|
projectId=project_id,
|
|
180
200
|
)
|
|
@@ -224,18 +244,18 @@ def sync(
|
|
|
224
244
|
"""
|
|
225
245
|
logger.info(f"Syncing GCP IAM for project {project_id}")
|
|
226
246
|
|
|
227
|
-
|
|
228
|
-
service_accounts = get_gcp_service_accounts(iam_client, project_id)
|
|
247
|
+
service_accounts_raw = get_gcp_service_accounts(iam_client, project_id)
|
|
229
248
|
logger.info(
|
|
230
|
-
f"Found {len(
|
|
249
|
+
f"Found {len(service_accounts_raw)} service accounts in project {project_id}"
|
|
231
250
|
)
|
|
251
|
+
service_accounts = transform_gcp_service_accounts(service_accounts_raw, project_id)
|
|
232
252
|
load_gcp_service_accounts(
|
|
233
253
|
neo4j_session, service_accounts, project_id, gcp_update_tag
|
|
234
254
|
)
|
|
235
255
|
|
|
236
|
-
|
|
237
|
-
roles
|
|
238
|
-
|
|
256
|
+
roles_raw = get_gcp_roles(iam_client, project_id)
|
|
257
|
+
logger.info(f"Found {len(roles_raw)} roles in project {project_id}")
|
|
258
|
+
roles = transform_gcp_roles(roles_raw, project_id)
|
|
239
259
|
load_gcp_roles(neo4j_session, roles, project_id, gcp_update_tag)
|
|
240
260
|
|
|
241
261
|
# Run cleanup
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
from string import Template
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import neo4j
|
|
8
|
+
import yaml
|
|
9
|
+
|
|
10
|
+
from cartography.client.core.tx import load_matchlinks
|
|
11
|
+
from cartography.client.core.tx import read_list_of_dicts_tx
|
|
12
|
+
from cartography.client.core.tx import read_list_of_values_tx
|
|
13
|
+
from cartography.graph.job import GraphJob
|
|
14
|
+
from cartography.models.gcp.permission_relationships import GCPPermissionMatchLink
|
|
15
|
+
from cartography.util import timeit
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def resolve_gcp_scope(scope: str, project_id: str) -> str:
|
|
21
|
+
"""
|
|
22
|
+
Resolve GCP scope to follow the standard hierarchy pattern.
|
|
23
|
+
|
|
24
|
+
Process breakdown:
|
|
25
|
+
- If scope starts with cloudresourcemanager.googleapis.com, return project/{project_id}/* (ORG,FOLDER,PROJECT)
|
|
26
|
+
- Otherwise, return project/{project_id}/resource/{resource_id} where resource_id is scope.split("/")[-1]
|
|
27
|
+
|
|
28
|
+
Typical Scope Examples:
|
|
29
|
+
- ORG: //cloudresourcemanager.googleapis.com/organizations/{id}
|
|
30
|
+
- FOLDER: //cloudresourcemanager.googleapis.com/folders/{id}
|
|
31
|
+
- PROJECT: //cloudresourcemanager.googleapis.com/projects/{id}
|
|
32
|
+
- BUCKET: //storage.googleapis.com/buckets/{bucket_name}
|
|
33
|
+
- INSTANCE: //compute.googleapis.com/projects/{project_id}/zones/{zone}/instances/{instance_id}
|
|
34
|
+
"""
|
|
35
|
+
if "cloudresourcemanager.googleapis.com" in scope:
|
|
36
|
+
return f"project/{project_id}/*"
|
|
37
|
+
|
|
38
|
+
return f"project/{project_id}/resource/{scope.split('/')[-1]}"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def compile_gcp_regex(item: str) -> re.Pattern:
|
|
42
|
+
# Escape special regex characters
|
|
43
|
+
item = item.replace(".", "\\.").replace("*", ".*")
|
|
44
|
+
try:
|
|
45
|
+
return re.compile(item, flags=re.IGNORECASE)
|
|
46
|
+
except re.error:
|
|
47
|
+
logger.warning(f"GCP regex did not compile for {item}")
|
|
48
|
+
# Return a regex that matches nothing -> no false positives
|
|
49
|
+
return re.compile("", flags=re.IGNORECASE)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def evaluate_clause(clause: re.Pattern, match: str) -> bool:
|
|
53
|
+
"""
|
|
54
|
+
Evaluates a clause in GCP IAM. Clauses can be permissions, denied permissions, or scopes.
|
|
55
|
+
|
|
56
|
+
Arguments:
|
|
57
|
+
clause {re.Pattern} -- The compiled regex pattern to evaluate against. Clauses can use
|
|
58
|
+
variable length wildcards (*)
|
|
59
|
+
match {str} -- The item to match against.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
[bool] -- True if the clause matched, False otherwise
|
|
63
|
+
"""
|
|
64
|
+
result = clause.fullmatch(match)
|
|
65
|
+
return result is not None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def evaluate_scope_for_resource(assignment: dict, resource_scope: str) -> bool:
|
|
69
|
+
scope = assignment["scope"]
|
|
70
|
+
# scope is now a compiled regex pattern
|
|
71
|
+
return evaluate_clause(scope, resource_scope)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def evaluate_denied_permission_for_permission(
|
|
75
|
+
permissions: dict, permission: str
|
|
76
|
+
) -> bool:
|
|
77
|
+
for clause in permissions["denied_permissions"]:
|
|
78
|
+
if evaluate_clause(clause, permission):
|
|
79
|
+
return True
|
|
80
|
+
return False
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def evaluate_permission_for_permission(permissions: dict, permission: str) -> bool:
|
|
84
|
+
for clause in permissions["permissions"]:
|
|
85
|
+
if evaluate_clause(clause, permission):
|
|
86
|
+
return True
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def evaluate_policy_binding_for_permissions(
|
|
91
|
+
assignment_data: dict[str, Any],
|
|
92
|
+
permissions: list[str],
|
|
93
|
+
resource_scope: str,
|
|
94
|
+
) -> bool:
|
|
95
|
+
permissions_dict = assignment_data["permissions"]
|
|
96
|
+
scope = assignment_data["scope"]
|
|
97
|
+
|
|
98
|
+
# Level 1: Check scope matching
|
|
99
|
+
if not evaluate_scope_for_resource({"scope": scope}, resource_scope):
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
for permission in permissions:
|
|
103
|
+
# Level 2: Check denied permissions
|
|
104
|
+
if not evaluate_denied_permission_for_permission(permissions_dict, permission):
|
|
105
|
+
# Level 3: Check allowed permissions
|
|
106
|
+
if evaluate_permission_for_permission(permissions_dict, permission):
|
|
107
|
+
return True
|
|
108
|
+
|
|
109
|
+
return False
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def principal_allowed_on_resource(
|
|
113
|
+
policy_bindings: dict[str, Any],
|
|
114
|
+
resource_scope: str,
|
|
115
|
+
permissions: list[str],
|
|
116
|
+
) -> bool:
|
|
117
|
+
for _, assignment_data in policy_bindings.items():
|
|
118
|
+
if evaluate_policy_binding_for_permissions(
|
|
119
|
+
assignment_data, permissions, resource_scope
|
|
120
|
+
):
|
|
121
|
+
return True
|
|
122
|
+
|
|
123
|
+
return False
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def calculate_permission_relationships(
|
|
127
|
+
principals: dict[str, Any],
|
|
128
|
+
resource_dict: dict[str, str],
|
|
129
|
+
permissions: list[str],
|
|
130
|
+
) -> list[dict[str, Any]]:
|
|
131
|
+
allowed_mappings: list[dict[str, Any]] = []
|
|
132
|
+
for resource_id, resource_scope in resource_dict.items():
|
|
133
|
+
for principal_email, policy_bindings in principals.items():
|
|
134
|
+
if principal_allowed_on_resource(
|
|
135
|
+
policy_bindings, resource_scope, permissions
|
|
136
|
+
):
|
|
137
|
+
allowed_mappings.append(
|
|
138
|
+
{
|
|
139
|
+
"principal_email": principal_email,
|
|
140
|
+
"resource_id": resource_id,
|
|
141
|
+
}
|
|
142
|
+
)
|
|
143
|
+
return allowed_mappings
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@timeit
|
|
147
|
+
def get_principals_for_project(
|
|
148
|
+
neo4j_session: neo4j.Session, project_id: str
|
|
149
|
+
) -> dict[str, Any]:
|
|
150
|
+
"""
|
|
151
|
+
Get all principals (users, service accounts, groups) with their policy bindings
|
|
152
|
+
for a given GCP project.
|
|
153
|
+
"""
|
|
154
|
+
get_principals_query = """
|
|
155
|
+
MATCH
|
|
156
|
+
(project:GCPProject{id: $ProjectId})-[:RESOURCE]->
|
|
157
|
+
(binding:GCPPolicyBinding)-[:GRANTS_ROLE]->
|
|
158
|
+
(role:GCPRole)
|
|
159
|
+
MATCH
|
|
160
|
+
(principal:GCPPrincipal)-[:HAS_ALLOW_POLICY]->(binding)
|
|
161
|
+
WHERE binding.has_condition = false
|
|
162
|
+
RETURN
|
|
163
|
+
DISTINCT principal.email as principal_email, binding.id as binding_id,
|
|
164
|
+
binding.resource as binding_resource, role.permissions as role_permissions
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
results = neo4j_session.execute_read(
|
|
168
|
+
read_list_of_dicts_tx,
|
|
169
|
+
get_principals_query,
|
|
170
|
+
ProjectId=project_id,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
principals: dict[str, Any] = {}
|
|
174
|
+
for r in results:
|
|
175
|
+
principal_email = r["principal_email"]
|
|
176
|
+
binding_id = r["binding_id"]
|
|
177
|
+
binding_resource = r["binding_resource"]
|
|
178
|
+
role_permissions = r["role_permissions"] or []
|
|
179
|
+
|
|
180
|
+
if principal_email not in principals:
|
|
181
|
+
principals[principal_email] = {}
|
|
182
|
+
|
|
183
|
+
# Compile permissions from role
|
|
184
|
+
compiled_permissions = compile_permissions_from_role(role_permissions)
|
|
185
|
+
compiled_scope = compile_gcp_regex(
|
|
186
|
+
resolve_gcp_scope(binding_resource, project_id)
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
principals[principal_email][binding_id] = {
|
|
190
|
+
"permissions": compiled_permissions,
|
|
191
|
+
"scope": compiled_scope,
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return principals
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def compile_permissions_from_role(role_permissions: list[str]) -> dict[str, Any]:
|
|
198
|
+
permissions: dict[str, list[str]] = {
|
|
199
|
+
"permissions": [],
|
|
200
|
+
"denied_permissions": [],
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
# For now, we only handle allow policies
|
|
204
|
+
# GCP can have denied permissions and denied principals (under deny policies),
|
|
205
|
+
# but we'd need to fetch that separately using the IAM API.
|
|
206
|
+
permissions["permissions"] = role_permissions
|
|
207
|
+
|
|
208
|
+
return compile_permissions(permissions)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def compile_permissions(permissions: dict[str, Any]) -> dict[str, Any]:
|
|
212
|
+
compiled_permissions = {}
|
|
213
|
+
compiled_permissions["permissions"] = [
|
|
214
|
+
compile_gcp_regex(item) for item in permissions["permissions"]
|
|
215
|
+
]
|
|
216
|
+
compiled_permissions["denied_permissions"] = [
|
|
217
|
+
compile_gcp_regex(item) for item in permissions["denied_permissions"]
|
|
218
|
+
]
|
|
219
|
+
|
|
220
|
+
return compiled_permissions
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@timeit
|
|
224
|
+
def get_resource_ids(
|
|
225
|
+
neo4j_session: neo4j.Session, project_id: str, target_label: str
|
|
226
|
+
) -> dict[str, str]:
|
|
227
|
+
"""
|
|
228
|
+
Get resource IDs for a given resource type in a project.
|
|
229
|
+
Returns a dictionary mapping actual resource IDs to their expected scopes.
|
|
230
|
+
"""
|
|
231
|
+
get_resource_query = Template(
|
|
232
|
+
"""
|
|
233
|
+
MATCH (project:GCPProject{id: $ProjectId})-[:RESOURCE]->(resource:$node_label)
|
|
234
|
+
RETURN resource.id as resource_id
|
|
235
|
+
""",
|
|
236
|
+
)
|
|
237
|
+
get_resource_query_template = get_resource_query.safe_substitute(
|
|
238
|
+
node_label=target_label,
|
|
239
|
+
)
|
|
240
|
+
resource_ids = neo4j_session.execute_read(
|
|
241
|
+
read_list_of_values_tx,
|
|
242
|
+
get_resource_query_template,
|
|
243
|
+
ProjectId=project_id,
|
|
244
|
+
)
|
|
245
|
+
resource_dict = {
|
|
246
|
+
resource_id: f'project/{project_id}/resource/{resource_id.split("/")[-1]}'
|
|
247
|
+
# Resource scope is project/{project_id}/resource/{last part of resource_id when separated by /}
|
|
248
|
+
# Resource_id as key for loading and resource scope as value for scope evaluation
|
|
249
|
+
for resource_id in resource_ids
|
|
250
|
+
}
|
|
251
|
+
return resource_dict
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def parse_permission_relationships_file(file_path: str) -> list[dict[str, Any]]:
|
|
255
|
+
try:
|
|
256
|
+
if os.path.isabs(file_path):
|
|
257
|
+
resolved_file_path = file_path
|
|
258
|
+
else:
|
|
259
|
+
resolved_file_path = os.path.join(os.getcwd(), file_path)
|
|
260
|
+
with open(resolved_file_path) as f:
|
|
261
|
+
relationship_mapping = yaml.load(f, Loader=yaml.FullLoader)
|
|
262
|
+
return relationship_mapping or []
|
|
263
|
+
except FileNotFoundError:
|
|
264
|
+
logger.warning(
|
|
265
|
+
f"GCP permission relationships file not found. Original filename passed to sync: '{file_path}', "
|
|
266
|
+
f"resolved full path: '{resolved_file_path}'. Skipping sync stage {__name__}. "
|
|
267
|
+
f"If you want to run this sync, please explicitly set a value for --gcp-permission-relationships-file in the "
|
|
268
|
+
f"command line interface."
|
|
269
|
+
)
|
|
270
|
+
return []
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def is_valid_gcp_rpr(rpr: dict[str, Any]) -> bool:
|
|
274
|
+
required_fields = ["permissions", "relationship_name", "target_label"]
|
|
275
|
+
for field in required_fields:
|
|
276
|
+
if field not in rpr:
|
|
277
|
+
return False
|
|
278
|
+
return True
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
@timeit
|
|
282
|
+
def load_principal_mappings(
|
|
283
|
+
neo4j_session: neo4j.Session,
|
|
284
|
+
principal_mappings: list[dict[str, Any]],
|
|
285
|
+
matchlink_schema: GCPPermissionMatchLink,
|
|
286
|
+
update_tag: int,
|
|
287
|
+
project_id: str,
|
|
288
|
+
) -> None:
|
|
289
|
+
"""
|
|
290
|
+
Load principal mappings into Neo4j using MatchLinks.
|
|
291
|
+
"""
|
|
292
|
+
if not principal_mappings:
|
|
293
|
+
return
|
|
294
|
+
|
|
295
|
+
logger.info(
|
|
296
|
+
f"Loading {len(principal_mappings)} {matchlink_schema.rel_label} relationships "
|
|
297
|
+
f"for {matchlink_schema.source_node_label} -> {matchlink_schema.target_node_label}"
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
load_matchlinks(
|
|
301
|
+
neo4j_session,
|
|
302
|
+
matchlink_schema,
|
|
303
|
+
principal_mappings,
|
|
304
|
+
lastupdated=update_tag,
|
|
305
|
+
_sub_resource_label="GCPProject",
|
|
306
|
+
_sub_resource_id=project_id,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
@timeit
|
|
311
|
+
def cleanup_rpr(
|
|
312
|
+
neo4j_session: neo4j.Session,
|
|
313
|
+
matchlink_schema: GCPPermissionMatchLink,
|
|
314
|
+
update_tag: int,
|
|
315
|
+
project_id: str,
|
|
316
|
+
) -> None:
|
|
317
|
+
logger.info(
|
|
318
|
+
"Cleaning up relationship '%s' for node label '%s'",
|
|
319
|
+
matchlink_schema.rel_label,
|
|
320
|
+
matchlink_schema.target_node_label,
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
GraphJob.from_matchlink(
|
|
324
|
+
matchlink_schema,
|
|
325
|
+
"GCPProject",
|
|
326
|
+
project_id,
|
|
327
|
+
update_tag,
|
|
328
|
+
).run(neo4j_session)
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
@timeit
|
|
332
|
+
def sync(
|
|
333
|
+
neo4j_session: neo4j.Session,
|
|
334
|
+
project_id: str,
|
|
335
|
+
update_tag: int,
|
|
336
|
+
common_job_parameters: dict[str, Any],
|
|
337
|
+
) -> None:
|
|
338
|
+
logger.info("Syncing GCP Permission Relationships for project '%s'.", project_id)
|
|
339
|
+
|
|
340
|
+
pr_file = common_job_parameters.get("gcp_permission_relationships_file")
|
|
341
|
+
if not pr_file:
|
|
342
|
+
logger.warning(
|
|
343
|
+
"GCP permission relationships file was not specified, skipping. If this is not expected, please check your "
|
|
344
|
+
"value of --gcp-permission-relationships-file"
|
|
345
|
+
)
|
|
346
|
+
return
|
|
347
|
+
|
|
348
|
+
# 1. GET - Fetch all GCP principals in suitable dict format
|
|
349
|
+
principals = get_principals_for_project(neo4j_session, project_id)
|
|
350
|
+
|
|
351
|
+
# 2. PARSE - Parse relationship file
|
|
352
|
+
relationship_mapping = parse_permission_relationships_file(pr_file)
|
|
353
|
+
|
|
354
|
+
# 3. EVALUATE - Evaluate each relationship and resource ID
|
|
355
|
+
for rpr in relationship_mapping:
|
|
356
|
+
if not is_valid_gcp_rpr(rpr):
|
|
357
|
+
logger.error(f"Invalid permission relationship configuration: {rpr}")
|
|
358
|
+
continue
|
|
359
|
+
|
|
360
|
+
target_label = rpr["target_label"]
|
|
361
|
+
relationship_name = rpr["relationship_name"]
|
|
362
|
+
permissions = rpr["permissions"]
|
|
363
|
+
|
|
364
|
+
resource_dict = get_resource_ids(neo4j_session, project_id, target_label)
|
|
365
|
+
|
|
366
|
+
logger.info(
|
|
367
|
+
f"Evaluating relationship '{relationship_name}' for resource type '{target_label}'"
|
|
368
|
+
)
|
|
369
|
+
matches = calculate_permission_relationships(
|
|
370
|
+
principals, resource_dict, permissions
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
# Create MatchLink schema with dynamic attributes
|
|
374
|
+
matchlink_schema = GCPPermissionMatchLink(
|
|
375
|
+
source_node_label="GCPPrincipal",
|
|
376
|
+
target_node_label=target_label,
|
|
377
|
+
rel_label=relationship_name,
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
load_principal_mappings(
|
|
381
|
+
neo4j_session,
|
|
382
|
+
matches,
|
|
383
|
+
matchlink_schema,
|
|
384
|
+
update_tag,
|
|
385
|
+
project_id,
|
|
386
|
+
)
|
|
387
|
+
cleanup_rpr(
|
|
388
|
+
neo4j_session,
|
|
389
|
+
matchlink_schema,
|
|
390
|
+
update_tag,
|
|
391
|
+
project_id,
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
logger.info(f"Completed GCP Permission Relationships sync for project {project_id}")
|