cartography 0.93.0rc1__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/__main__.py +1 -2
- cartography/_version.py +34 -0
- cartography/cli.py +903 -225
- cartography/client/aws/__init__.py +19 -0
- cartography/client/aws/ecr.py +51 -0
- cartography/client/core/tx.py +400 -27
- cartography/config.py +215 -10
- cartography/data/azure_permission_relationships.yaml +20 -0
- cartography/data/gcp_permission_relationships.yaml +21 -0
- cartography/data/indexes.cypher +1 -200
- cartography/data/jobs/analysis/aws_ec2_asset_exposure.json +17 -2
- cartography/data/jobs/analysis/aws_ec2_keypair_analysis.json +2 -2
- cartography/data/jobs/analysis/gcp_compute_asset_inet_exposure.json +1 -1
- cartography/data/jobs/analysis/keycloak_inheritance.json +30 -0
- cartography/data/jobs/cleanup/crowdstrike_import_cleanup.json +0 -5
- cartography/data/jobs/cleanup/gcp_compute_vpc_cleanup.json +0 -12
- cartography/data/jobs/cleanup/github_repos_cleanup.json +27 -0
- cartography/data/jobs/scoped_analysis/aws_ec2_iaminstanceprofile.json +15 -0
- cartography/data/jobs/scoped_analysis/semgrep_sca_risk_analysis.json +13 -13
- cartography/driftdetect/__main__.py +1 -2
- cartography/driftdetect/add_shortcut.py +10 -2
- cartography/driftdetect/cli.py +72 -75
- cartography/driftdetect/detect_deviations.py +7 -3
- cartography/driftdetect/get_states.py +20 -8
- cartography/driftdetect/model.py +5 -5
- cartography/driftdetect/serializers.py +8 -6
- cartography/driftdetect/storage.py +2 -2
- cartography/graph/cleanupbuilder.py +255 -35
- cartography/graph/job.py +104 -20
- cartography/graph/querybuilder.py +689 -91
- cartography/graph/statement.py +49 -36
- 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/analysis.py +4 -1
- cartography/intel/anthropic/__init__.py +62 -0
- cartography/intel/anthropic/apikeys.py +72 -0
- cartography/intel/anthropic/users.py +75 -0
- cartography/intel/anthropic/util.py +51 -0
- cartography/intel/anthropic/workspaces.py +95 -0
- cartography/intel/aws/__init__.py +137 -59
- cartography/intel/aws/acm.py +124 -0
- cartography/intel/aws/apigateway.py +482 -217
- cartography/intel/aws/apigatewayv2.py +116 -0
- cartography/intel/aws/cloudtrail.py +105 -0
- cartography/intel/aws/cloudtrail_management_events.py +962 -0
- cartography/intel/aws/cloudwatch.py +239 -0
- cartography/intel/aws/codebuild.py +132 -0
- cartography/intel/aws/cognito.py +201 -0
- cartography/intel/aws/config.py +63 -23
- cartography/intel/aws/dynamodb.py +108 -40
- cartography/intel/aws/ec2/__init__.py +2 -2
- cartography/intel/aws/ec2/auto_scaling_groups.py +254 -189
- cartography/intel/aws/ec2/elastic_ip_addresses.py +44 -14
- cartography/intel/aws/ec2/images.py +74 -39
- cartography/intel/aws/ec2/instances.py +262 -137
- cartography/intel/aws/ec2/internet_gateways.py +44 -13
- cartography/intel/aws/ec2/key_pairs.py +72 -39
- cartography/intel/aws/ec2/launch_templates.py +143 -66
- cartography/intel/aws/ec2/load_balancer_v2s.py +119 -45
- cartography/intel/aws/ec2/load_balancers.py +165 -147
- cartography/intel/aws/ec2/network_acls.py +233 -0
- cartography/intel/aws/ec2/network_interfaces.py +150 -87
- cartography/intel/aws/ec2/reserved_instances.py +48 -17
- cartography/intel/aws/ec2/route_tables.py +327 -0
- cartography/intel/aws/ec2/security_groups.py +189 -121
- cartography/intel/aws/ec2/snapshots.py +93 -91
- cartography/intel/aws/ec2/subnets.py +70 -58
- cartography/intel/aws/ec2/tgw.py +111 -39
- cartography/intel/aws/ec2/util.py +1 -1
- cartography/intel/aws/ec2/volumes.py +69 -41
- cartography/intel/aws/ec2/vpc.py +157 -116
- cartography/intel/aws/ec2/vpc_peerings.py +317 -121
- cartography/intel/aws/ecr.py +336 -93
- cartography/intel/aws/ecr_image_layers.py +923 -0
- cartography/intel/aws/ecs.py +310 -403
- cartography/intel/aws/efs.py +261 -0
- cartography/intel/aws/eks.py +55 -29
- cartography/intel/aws/elasticache.py +130 -83
- cartography/intel/aws/elasticsearch.py +70 -24
- cartography/intel/aws/emr.py +61 -23
- 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 +978 -464
- cartography/intel/aws/iam_instance_profiles.py +73 -0
- cartography/intel/aws/identitycenter.py +847 -0
- cartography/intel/aws/inspector.py +330 -133
- cartography/intel/aws/kms.py +235 -209
- cartography/intel/aws/lambda_function.py +328 -176
- cartography/intel/aws/organizations.py +40 -19
- cartography/intel/aws/permission_relationships.py +144 -68
- cartography/intel/aws/rds.py +467 -412
- cartography/intel/aws/redshift.py +116 -50
- cartography/intel/aws/resourcegroupstaggingapi.py +198 -82
- cartography/intel/aws/resources.py +80 -42
- cartography/intel/aws/route53.py +419 -318
- cartography/intel/aws/s3.py +489 -96
- cartography/intel/aws/s3accountpublicaccessblock.py +157 -0
- cartography/intel/aws/secretsmanager.py +217 -40
- cartography/intel/aws/securityhub.py +23 -10
- cartography/intel/aws/sns.py +226 -0
- cartography/intel/aws/sqs.py +74 -96
- cartography/intel/aws/ssm.py +142 -33
- cartography/intel/aws/util/arns.py +7 -7
- cartography/intel/aws/util/common.py +31 -4
- cartography/intel/azure/__init__.py +259 -46
- cartography/intel/azure/aks.py +175 -0
- cartography/intel/azure/app_service.py +105 -0
- cartography/intel/azure/compute.py +141 -120
- cartography/intel/azure/container_instances.py +95 -0
- cartography/intel/azure/cosmosdb.py +706 -519
- 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 +436 -392
- cartography/intel/azure/storage.py +467 -335
- cartography/intel/azure/subscription.py +49 -55
- cartography/intel/azure/tenant.py +46 -28
- cartography/intel/azure/util/common.py +13 -0
- cartography/intel/azure/util/credentials.py +58 -143
- cartography/intel/azure/util/tag.py +41 -0
- cartography/intel/bigfix/__init__.py +2 -2
- cartography/intel/bigfix/computers.py +93 -65
- cartography/intel/cloudflare/__init__.py +74 -0
- cartography/intel/cloudflare/accounts.py +57 -0
- cartography/intel/cloudflare/dnsrecords.py +64 -0
- cartography/intel/cloudflare/members.py +75 -0
- cartography/intel/cloudflare/roles.py +65 -0
- cartography/intel/cloudflare/zones.py +64 -0
- cartography/intel/create_indexes.py +5 -3
- cartography/intel/crowdstrike/__init__.py +26 -12
- cartography/intel/crowdstrike/endpoints.py +17 -45
- cartography/intel/crowdstrike/spotlight.py +13 -5
- cartography/intel/cve/__init__.py +91 -26
- cartography/intel/cve/feed.py +77 -56
- cartography/intel/digitalocean/__init__.py +22 -13
- cartography/intel/digitalocean/compute.py +75 -108
- cartography/intel/digitalocean/management.py +44 -80
- cartography/intel/digitalocean/platform.py +48 -43
- cartography/intel/dns.py +41 -12
- cartography/intel/duo/__init__.py +21 -16
- cartography/intel/duo/api_host.py +14 -9
- cartography/intel/duo/endpoints.py +50 -45
- cartography/intel/duo/groups.py +18 -14
- cartography/intel/duo/phones.py +37 -34
- cartography/intel/duo/tokens.py +26 -23
- cartography/intel/duo/users.py +54 -50
- cartography/intel/duo/web_authn_credentials.py +30 -25
- cartography/intel/entra/__init__.py +160 -0
- 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 +136 -0
- cartography/intel/entra/service_principals.py +217 -0
- cartography/intel/entra/users.py +259 -0
- cartography/intel/gcp/__init__.py +381 -175
- 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 +521 -325
- 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 +134 -179
- cartography/intel/gcp/gke.py +100 -107
- cartography/intel/gcp/iam.py +262 -0
- cartography/intel/gcp/permission_relationships.py +394 -0
- cartography/intel/gcp/policy_bindings.py +225 -0
- cartography/intel/gcp/storage.py +103 -158
- cartography/intel/github/__init__.py +66 -27
- cartography/intel/github/commits.py +423 -0
- cartography/intel/github/repos.py +871 -160
- cartography/intel/github/teams.py +386 -53
- cartography/intel/github/users.py +214 -49
- cartography/intel/github/util.py +50 -35
- 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 +101 -42
- cartography/intel/gsuite/groups.py +291 -0
- cartography/intel/gsuite/users.py +142 -0
- cartography/intel/jamf/__init__.py +19 -1
- cartography/intel/jamf/computers.py +37 -8
- cartography/intel/jamf/util.py +7 -2
- cartography/intel/kandji/__init__.py +6 -3
- cartography/intel/kandji/devices.py +40 -10
- 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 +60 -55
- cartography/intel/kubernetes/pods.py +171 -75
- cartography/intel/kubernetes/rbac.py +597 -0
- cartography/intel/kubernetes/secrets.py +95 -45
- cartography/intel/kubernetes/services.py +131 -63
- cartography/intel/kubernetes/util.py +142 -14
- cartography/intel/lastpass/__init__.py +2 -2
- cartography/intel/lastpass/users.py +23 -12
- cartography/intel/oci/__init__.py +44 -11
- cartography/intel/oci/iam.py +157 -47
- cartography/intel/oci/organizations.py +16 -7
- cartography/intel/oci/utils.py +71 -25
- cartography/intel/okta/__init__.py +66 -15
- cartography/intel/okta/applications.py +57 -25
- cartography/intel/okta/awssaml.py +105 -41
- cartography/intel/okta/factors.py +19 -5
- cartography/intel/okta/groups.py +61 -31
- cartography/intel/okta/organization.py +8 -2
- cartography/intel/okta/origins.py +9 -3
- cartography/intel/okta/roles.py +20 -7
- cartography/intel/okta/users.py +31 -10
- cartography/intel/okta/utils.py +6 -4
- 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/openai/__init__.py +86 -0
- cartography/intel/openai/adminapikeys.py +89 -0
- cartography/intel/openai/apikeys.py +96 -0
- cartography/intel/openai/projects.py +97 -0
- cartography/intel/openai/serviceaccounts.py +82 -0
- cartography/intel/openai/users.py +75 -0
- cartography/intel/openai/util.py +45 -0
- cartography/intel/pagerduty/__init__.py +8 -7
- cartography/intel/pagerduty/escalation_policies.py +31 -12
- cartography/intel/pagerduty/schedules.py +21 -8
- cartography/intel/pagerduty/services.py +18 -7
- cartography/intel/pagerduty/teams.py +13 -5
- cartography/intel/pagerduty/users.py +6 -2
- cartography/intel/pagerduty/vendors.py +6 -2
- 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/semgrep/__init__.py +30 -5
- cartography/intel/semgrep/dependencies.py +255 -0
- cartography/intel/semgrep/deployment.py +69 -0
- cartography/intel/semgrep/findings.py +157 -117
- 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/snipeit/__init__.py +44 -0
- cartography/intel/snipeit/asset.py +80 -0
- cartography/intel/snipeit/user.py +78 -0
- cartography/intel/snipeit/util.py +40 -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/tailscale/__init__.py +77 -0
- cartography/intel/tailscale/acls.py +146 -0
- cartography/intel/tailscale/devices.py +127 -0
- cartography/intel/tailscale/postureintegrations.py +81 -0
- cartography/intel/tailscale/tailnets.py +76 -0
- cartography/intel/tailscale/users.py +80 -0
- cartography/intel/tailscale/utils.py +132 -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/__init__.py +0 -0
- cartography/models/anthropic/apikey.py +94 -0
- cartography/models/anthropic/organization.py +19 -0
- cartography/models/anthropic/user.py +52 -0
- cartography/models/anthropic/workspace.py +90 -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/apigateway.py +51 -0
- cartography/models/aws/apigateway/apigatewaycertificate.py +72 -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/apigateway/apigatewayresource.py +70 -0
- cartography/models/aws/apigateway/apigatewaystage.py +75 -0
- cartography/models/aws/apigatewayv2/__init__.py +0 -0
- cartography/models/aws/apigatewayv2/apigatewayv2.py +53 -0
- cartography/models/aws/cloudtrail/__init__.py +0 -0
- cartography/models/aws/cloudtrail/management_events.py +153 -0
- cartography/models/aws/cloudtrail/trail.py +106 -0
- cartography/models/aws/cloudwatch/__init__.py +0 -0
- cartography/models/aws/cloudwatch/log_metric_filter.py +79 -0
- cartography/models/aws/cloudwatch/loggroup.py +52 -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/gsi.py +30 -22
- cartography/models/aws/dynamodb/tables.py +27 -17
- cartography/models/aws/ec2/auto_scaling_groups.py +224 -0
- cartography/models/aws/ec2/images.py +36 -34
- cartography/models/aws/ec2/instances.py +85 -38
- cartography/models/aws/ec2/keypair.py +59 -0
- cartography/models/aws/ec2/keypair_instance.py +76 -0
- cartography/models/aws/ec2/launch_configurations.py +59 -0
- cartography/models/aws/ec2/launch_template_versions.py +48 -38
- cartography/models/aws/ec2/launch_templates.py +21 -17
- cartography/models/aws/ec2/load_balancer_listeners.py +72 -0
- cartography/models/aws/ec2/load_balancers.py +112 -0
- cartography/models/aws/ec2/network_acl_rules.py +106 -0
- cartography/models/aws/ec2/network_acls.py +95 -0
- cartography/models/aws/ec2/networkinterface_instance.py +52 -39
- cartography/models/aws/ec2/networkinterfaces.py +57 -37
- cartography/models/aws/ec2/privateip_networkinterface.py +32 -22
- cartography/models/aws/ec2/reservations.py +18 -14
- cartography/models/aws/ec2/route_table_associations.py +97 -0
- cartography/models/aws/ec2/route_tables.py +128 -0
- cartography/models/aws/ec2/routes.py +85 -0
- cartography/models/aws/ec2/security_group_rules.py +109 -0
- cartography/models/aws/ec2/security_groups.py +90 -0
- cartography/models/aws/ec2/securitygroup_instance.py +29 -20
- cartography/models/aws/ec2/securitygroup_networkinterface.py +24 -15
- cartography/models/aws/ec2/snapshots.py +58 -0
- cartography/models/aws/ec2/subnet_instance.py +26 -19
- cartography/models/aws/ec2/subnet_networkinterface.py +42 -31
- cartography/models/aws/ec2/subnets.py +65 -0
- cartography/models/aws/ec2/volumes.py +67 -40
- 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/__init__.py +0 -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 +79 -0
- cartography/models/aws/eks/clusters.py +23 -21
- 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/emr.py +32 -30
- 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/__init__.py +0 -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/instanceprofile.py +76 -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/__init__.py +0 -0
- cartography/models/aws/identitycenter/awsidentitycenter.py +49 -0
- cartography/models/aws/identitycenter/awspermissionset.py +162 -0
- cartography/models/aws/identitycenter/awssogroup.py +70 -0
- cartography/models/aws/identitycenter/awsssouser.py +110 -0
- cartography/models/aws/inspector/findings.py +124 -58
- cartography/models/aws/inspector/packages.py +18 -42
- 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/__init__.py +0 -0
- cartography/models/aws/s3/account_public_access_block.py +51 -0
- cartography/models/aws/s3/notification.py +24 -0
- cartography/models/aws/secretsmanager/__init__.py +0 -0
- cartography/models/aws/secretsmanager/secret.py +106 -0
- cartography/models/aws/secretsmanager/secret_version.py +114 -0
- cartography/models/aws/sns/__init__.py +0 -0
- cartography/models/aws/sns/topic.py +50 -0
- 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/aws/ssm/instance_information.py +51 -39
- cartography/models/aws/ssm/instance_patch.py +32 -26
- cartography/models/aws/ssm/parameters.py +84 -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 +42 -38
- cartography/models/bigfix/bigfix_root.py +3 -3
- cartography/models/cloudflare/__init__.py +0 -0
- cartography/models/cloudflare/account.py +25 -0
- cartography/models/cloudflare/dnsrecord.py +55 -0
- cartography/models/cloudflare/member.py +86 -0
- cartography/models/cloudflare/role.py +44 -0
- cartography/models/cloudflare/zone.py +59 -0
- cartography/models/core/common.py +53 -2
- cartography/models/core/nodes.py +20 -4
- cartography/models/core/relationships.py +58 -6
- cartography/models/crowdstrike/__init__.py +0 -0
- cartography/models/crowdstrike/hosts.py +51 -0
- cartography/models/cve/cve.py +34 -32
- cartography/models/cve/cve_feed.py +6 -6
- cartography/models/digitalocean/__init__.py +0 -0
- cartography/models/digitalocean/account.py +21 -0
- cartography/models/digitalocean/droplet.py +58 -0
- cartography/models/digitalocean/project.py +48 -0
- cartography/models/duo/api_host.py +3 -3
- cartography/models/duo/endpoint.py +43 -41
- cartography/models/duo/group.py +14 -14
- cartography/models/duo/phone.py +27 -27
- cartography/models/duo/token.py +16 -16
- cartography/models/duo/user.py +50 -44
- cartography/models/duo/web_authn_credential.py +27 -19
- cartography/models/entra/__init__.py +0 -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/ou.py +48 -0
- cartography/models/entra/service_principal.py +104 -0
- cartography/models/entra/tenant.py +39 -0
- cartography/models/entra/user.py +90 -0
- 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 +73 -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/orgs.py +27 -0
- cartography/models/github/teams.py +74 -22
- cartography/models/github/users.py +149 -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 +22 -17
- cartography/models/kandji/tenant.py +6 -4
- 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/tenant.py +3 -3
- cartography/models/lastpass/user.py +36 -28
- 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/__init__.py +0 -0
- cartography/models/openai/adminapikey.py +94 -0
- cartography/models/openai/apikey.py +88 -0
- cartography/models/openai/organization.py +17 -0
- cartography/models/openai/project.py +89 -0
- cartography/models/openai/serviceaccount.py +50 -0
- cartography/models/openai/user.py +53 -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/semgrep/dependencies.py +102 -0
- cartography/models/semgrep/deployment.py +5 -5
- cartography/models/semgrep/findings.py +58 -40
- cartography/models/semgrep/locations.py +27 -21
- 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/__init__.py +0 -0
- cartography/models/snipeit/asset.py +92 -0
- cartography/models/snipeit/tenant.py +19 -0
- cartography/models/snipeit/user.py +60 -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/__init__.py +0 -0
- cartography/models/tailscale/device.py +96 -0
- cartography/models/tailscale/group.py +86 -0
- cartography/models/tailscale/postureintegration.py +58 -0
- cartography/models/tailscale/tag.py +102 -0
- cartography/models/tailscale/tailnet.py +29 -0
- cartography/models/tailscale/user.py +57 -0
- 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/stats.py +4 -4
- cartography/sync.py +137 -31
- cartography/util.py +187 -77
- cartography-0.123.0.dist-info/METADATA +230 -0
- cartography-0.123.0.dist-info/RECORD +856 -0
- {cartography-0.93.0rc1.dist-info → cartography-0.123.0.dist-info}/WHEEL +1 -1
- {cartography-0.93.0rc1.dist-info → cartography-0.123.0.dist-info}/entry_points.txt +1 -0
- {cartography-0.93.0rc1.dist-info → cartography-0.123.0.dist-info/licenses}/LICENSE +1 -1
- cartography/data/jobs/analysis/aws_ec2_iaminstance.json +0 -10
- cartography/data/jobs/analysis/aws_ec2_iaminstanceprofile.json +0 -10
- cartography/data/jobs/cleanup/aws_apigateway_details.json +0 -10
- 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_apigateway_cleanup.json +0 -45
- 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_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/crxcavator_import_cleanup.json +0 -18
- 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/github_users_cleanup.json +0 -23
- 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/crxcavator/__init__.py +0 -44
- cartography/intel/crxcavator/crxcavator.py +0 -329
- cartography/intel/gcp/crm.py +0 -302
- cartography/intel/gsuite/api.py +0 -284
- cartography/models/aws/ec2/keypairs.py +0 -64
- cartography-0.93.0rc1.dist-info/METADATA +0 -55
- cartography-0.93.0rc1.dist-info/NOTICE +0 -4
- cartography-0.93.0rc1.dist-info/RECORD +0 -341
- /cartography/data/jobs/{analysis → scoped_analysis}/aws_s3acl_analysis.json +0 -0
- {cartography-0.93.0rc1.dist-info → cartography-0.123.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
import neo4j
|
|
4
|
+
|
|
5
|
+
from cartography.client.core.tx import read_list_of_values_tx
|
|
6
|
+
from cartography.util import timeit
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@timeit
|
|
10
|
+
def list_accounts(neo4j_session: neo4j.Session) -> List[str]:
|
|
11
|
+
"""
|
|
12
|
+
:param neo4j_session: The neo4j session object.
|
|
13
|
+
:return: A list of all AWS account IDs in the graph
|
|
14
|
+
"""
|
|
15
|
+
# See https://community.neo4j.com/t/extract-list-of-nodes-and-labels-from-path/13665/4
|
|
16
|
+
query = """
|
|
17
|
+
MATCH (a:AWSAccount) RETURN a.id
|
|
18
|
+
"""
|
|
19
|
+
return neo4j_session.read_transaction(read_list_of_values_tx, query)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from typing import Set
|
|
2
|
+
from typing import Tuple
|
|
3
|
+
|
|
4
|
+
import neo4j
|
|
5
|
+
|
|
6
|
+
from cartography.client.core.tx import read_list_of_tuples_tx
|
|
7
|
+
from cartography.util import timeit
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@timeit
|
|
11
|
+
def get_ecr_images(
|
|
12
|
+
neo4j_session: neo4j.Session, aws_account_id: str
|
|
13
|
+
) -> Set[Tuple[str, str, str, str, str]]:
|
|
14
|
+
"""
|
|
15
|
+
Queries the graph for all ECR images and their parent images.
|
|
16
|
+
Returns 5-tuples of ECR repository regions, tags, URIs, names, and binary digests. This is used to identify which
|
|
17
|
+
images to scan.
|
|
18
|
+
:param neo4j_session: The neo4j session object.
|
|
19
|
+
:param aws_account_id: The AWS account ID to get ECR repo data for.
|
|
20
|
+
:return: 5-tuples of repo region, image tag, image URI, repo_name, and image_digest.
|
|
21
|
+
"""
|
|
22
|
+
# See https://community.neo4j.com/t/extract-list-of-nodes-and-labels-from-path/13665/4
|
|
23
|
+
query = """
|
|
24
|
+
MATCH (e1:ECRRepositoryImage)<-[:REPO_IMAGE]-(repo:ECRRepository)
|
|
25
|
+
MATCH (repo)<-[:RESOURCE]-(:AWSAccount {id: $AWS_ID})
|
|
26
|
+
|
|
27
|
+
// OPTIONAL traversal of parent hierarchy
|
|
28
|
+
OPTIONAL MATCH path = (e1)-[:PARENT*1..]->(ancestor:ECRRepositoryImage)
|
|
29
|
+
WITH e1,
|
|
30
|
+
CASE
|
|
31
|
+
WHEN path IS NULL THEN [e1]
|
|
32
|
+
ELSE [n IN nodes(path) | n] + [e1]
|
|
33
|
+
END AS repo_img_collection_unflattened
|
|
34
|
+
|
|
35
|
+
// Flatten and dedupe
|
|
36
|
+
UNWIND repo_img_collection_unflattened AS repo_img
|
|
37
|
+
WITH DISTINCT repo_img
|
|
38
|
+
|
|
39
|
+
// Match image metadata
|
|
40
|
+
MATCH (er:ECRRepository)-[:REPO_IMAGE]->(repo_img)-[:IMAGE]->(img:ECRImage)
|
|
41
|
+
|
|
42
|
+
RETURN DISTINCT
|
|
43
|
+
er.region AS region,
|
|
44
|
+
repo_img.tag AS tag,
|
|
45
|
+
repo_img.id AS uri,
|
|
46
|
+
er.name AS repo_name,
|
|
47
|
+
img.digest AS digest
|
|
48
|
+
"""
|
|
49
|
+
return neo4j_session.read_transaction(
|
|
50
|
+
read_list_of_tuples_tx, query, AWS_ID=aws_account_id
|
|
51
|
+
)
|
cartography/client/core/tx.py
CHANGED
|
@@ -1,19 +1,287 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import time
|
|
3
|
+
from functools import partial
|
|
1
4
|
from typing import Any
|
|
5
|
+
from typing import Callable
|
|
2
6
|
from typing import Dict
|
|
3
7
|
from typing import List
|
|
4
8
|
from typing import Optional
|
|
5
9
|
from typing import Tuple
|
|
10
|
+
from typing import TypeVar
|
|
6
11
|
from typing import Union
|
|
7
12
|
|
|
13
|
+
import backoff
|
|
8
14
|
import neo4j
|
|
15
|
+
import neo4j.exceptions
|
|
9
16
|
|
|
10
17
|
from cartography.graph.querybuilder import build_create_index_queries
|
|
18
|
+
from cartography.graph.querybuilder import build_create_index_queries_for_matchlink
|
|
11
19
|
from cartography.graph.querybuilder import build_ingestion_query
|
|
20
|
+
from cartography.graph.querybuilder import build_matchlink_query
|
|
12
21
|
from cartography.models.core.nodes import CartographyNodeSchema
|
|
22
|
+
from cartography.models.core.relationships import CartographyRelSchema
|
|
23
|
+
from cartography.util import backoff_handler
|
|
13
24
|
from cartography.util import batch
|
|
14
25
|
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
15
27
|
|
|
16
|
-
|
|
28
|
+
T = TypeVar("T")
|
|
29
|
+
|
|
30
|
+
_MAX_NETWORK_RETRIES = 5
|
|
31
|
+
_MAX_ENTITY_NOT_FOUND_RETRIES = 5
|
|
32
|
+
_NETWORK_EXCEPTIONS: tuple[type[BaseException], ...] = (
|
|
33
|
+
ConnectionResetError,
|
|
34
|
+
neo4j.exceptions.ServiceUnavailable,
|
|
35
|
+
neo4j.exceptions.SessionExpired,
|
|
36
|
+
neo4j.exceptions.TransientError,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _is_retryable_client_error(exc: Exception) -> bool:
|
|
41
|
+
"""
|
|
42
|
+
Determine if a ClientError should be retried.
|
|
43
|
+
|
|
44
|
+
EntityNotFound during concurrent write operations is a known transient error in Neo4j
|
|
45
|
+
that occurs due to the database's query execution pipeline design. When multiple threads
|
|
46
|
+
run concurrent MERGE/DELETE operations, one thread can delete an entity that another
|
|
47
|
+
thread has already referenced but hasn't locked yet.
|
|
48
|
+
|
|
49
|
+
Neo4j maintainers explicitly recommend retrying EntityNotFound errors during
|
|
50
|
+
multi-threaded operations, even though the driver classifies it as a non-retryable
|
|
51
|
+
ClientError. See: https://github.com/neo4j/neo4j/issues/6823
|
|
52
|
+
|
|
53
|
+
This is particularly common in Cartography when:
|
|
54
|
+
- Multiple providers sync concurrently (e.g., AWS + GCP + Okta)
|
|
55
|
+
- Large batch sizes (10,000 nodes per transaction) are used
|
|
56
|
+
- Page cache evictions occur under memory pressure (especially in Aura)
|
|
57
|
+
- Concurrent MERGE and DETACH DELETE operations overlap
|
|
58
|
+
|
|
59
|
+
:param exc: The exception to check
|
|
60
|
+
:return: True if this is a retryable ClientError (EntityNotFound), False otherwise
|
|
61
|
+
"""
|
|
62
|
+
if not isinstance(exc, neo4j.exceptions.ClientError):
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
# Only retry EntityNotFound errors - all other ClientErrors are permanent failures
|
|
66
|
+
# Note: exc.code can be None for locally-created errors (per neo4j driver docs)
|
|
67
|
+
code = exc.code
|
|
68
|
+
if code is None:
|
|
69
|
+
return False
|
|
70
|
+
return code == "Neo.ClientError.Statement.EntityNotFound"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _entity_not_found_backoff_handler(details: Dict) -> None:
|
|
74
|
+
"""
|
|
75
|
+
Custom backoff handler that provides enhanced logging for EntityNotFound retries.
|
|
76
|
+
|
|
77
|
+
This handler logs additional context when retrying EntityNotFound errors to help
|
|
78
|
+
diagnose concurrent write issues and page cache pressure in Neo4j.
|
|
79
|
+
|
|
80
|
+
:param details: Backoff details dict containing 'exception', 'wait', 'tries', 'target'
|
|
81
|
+
"""
|
|
82
|
+
exc = details.get("exception")
|
|
83
|
+
if isinstance(exc, Exception) and _is_retryable_client_error(exc):
|
|
84
|
+
wait = details.get("wait")
|
|
85
|
+
wait_str = f"{wait:0.1f}" if wait is not None else "unknown"
|
|
86
|
+
tries = details.get("tries", 0)
|
|
87
|
+
|
|
88
|
+
if tries == 1:
|
|
89
|
+
log_msg = (
|
|
90
|
+
f"Encountered EntityNotFound error (attempt 1/{_MAX_ENTITY_NOT_FOUND_RETRIES}). "
|
|
91
|
+
f"This is expected during concurrent write operations. "
|
|
92
|
+
f"Retrying after {wait_str} seconds backoff. "
|
|
93
|
+
f"Function: {details.get('target')}. Error: {details.get('exception')}"
|
|
94
|
+
)
|
|
95
|
+
else:
|
|
96
|
+
log_msg = (
|
|
97
|
+
f"EntityNotFound retry {tries}/{_MAX_ENTITY_NOT_FOUND_RETRIES}. "
|
|
98
|
+
f"Backing off {wait_str} seconds before next attempt. "
|
|
99
|
+
f"Function: {details.get('target')}. Error: {details.get('exception')}"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
logger.warning(log_msg)
|
|
103
|
+
else:
|
|
104
|
+
# Fall back to standard backoff handler for other errors
|
|
105
|
+
backoff_handler(details)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _run_with_retry(operation: Callable[[], T], target: str) -> T:
|
|
109
|
+
"""
|
|
110
|
+
Execute the supplied callable with retry logic for transient network errors and
|
|
111
|
+
EntityNotFound ClientErrors.
|
|
112
|
+
"""
|
|
113
|
+
network_attempts = 0
|
|
114
|
+
entity_attempts = 0
|
|
115
|
+
network_wait = backoff.expo()
|
|
116
|
+
entity_wait = backoff.expo()
|
|
117
|
+
|
|
118
|
+
while True:
|
|
119
|
+
try:
|
|
120
|
+
result = operation()
|
|
121
|
+
# Log success if we recovered from errors
|
|
122
|
+
if network_attempts > 0:
|
|
123
|
+
logger.info(
|
|
124
|
+
f"Successfully recovered from network error after {network_attempts} "
|
|
125
|
+
f"{'retry' if network_attempts == 1 else 'retries'}. Function: {target}"
|
|
126
|
+
)
|
|
127
|
+
if entity_attempts > 0:
|
|
128
|
+
logger.info(
|
|
129
|
+
f"Successfully recovered from EntityNotFound error after {entity_attempts} "
|
|
130
|
+
f"{'retry' if entity_attempts == 1 else 'retries'}. Function: {target}"
|
|
131
|
+
)
|
|
132
|
+
return result
|
|
133
|
+
except _NETWORK_EXCEPTIONS as exc:
|
|
134
|
+
if network_attempts >= _MAX_NETWORK_RETRIES - 1:
|
|
135
|
+
raise
|
|
136
|
+
network_attempts += 1
|
|
137
|
+
wait = next(network_wait)
|
|
138
|
+
if wait is None:
|
|
139
|
+
logger.error(
|
|
140
|
+
f"Unexpected: backoff generator returned None for wait time. "
|
|
141
|
+
f"target={target}, attempts={network_attempts}, exc={exc}"
|
|
142
|
+
)
|
|
143
|
+
wait = 1.0 # Fallback to 1 second wait
|
|
144
|
+
backoff_handler(
|
|
145
|
+
{
|
|
146
|
+
"exception": exc,
|
|
147
|
+
"target": target,
|
|
148
|
+
"tries": network_attempts,
|
|
149
|
+
"wait": wait,
|
|
150
|
+
}
|
|
151
|
+
)
|
|
152
|
+
time.sleep(wait)
|
|
153
|
+
continue
|
|
154
|
+
except neo4j.exceptions.ClientError as exc:
|
|
155
|
+
if not _is_retryable_client_error(exc):
|
|
156
|
+
raise
|
|
157
|
+
if entity_attempts >= _MAX_ENTITY_NOT_FOUND_RETRIES - 1:
|
|
158
|
+
raise
|
|
159
|
+
entity_attempts += 1
|
|
160
|
+
wait = next(entity_wait)
|
|
161
|
+
if wait is None:
|
|
162
|
+
logger.error(
|
|
163
|
+
f"Unexpected: backoff generator returned None for wait time. "
|
|
164
|
+
f"target={target}, attempts={entity_attempts}, exc={exc}"
|
|
165
|
+
)
|
|
166
|
+
wait = 1.0 # Fallback to 1 second wait
|
|
167
|
+
_entity_not_found_backoff_handler(
|
|
168
|
+
{
|
|
169
|
+
"exception": exc,
|
|
170
|
+
"target": target,
|
|
171
|
+
"tries": entity_attempts,
|
|
172
|
+
"wait": wait,
|
|
173
|
+
}
|
|
174
|
+
)
|
|
175
|
+
time.sleep(wait)
|
|
176
|
+
continue
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@backoff.on_exception( # type: ignore
|
|
180
|
+
backoff.expo,
|
|
181
|
+
(
|
|
182
|
+
ConnectionResetError,
|
|
183
|
+
neo4j.exceptions.ServiceUnavailable,
|
|
184
|
+
neo4j.exceptions.SessionExpired,
|
|
185
|
+
neo4j.exceptions.TransientError,
|
|
186
|
+
),
|
|
187
|
+
max_tries=5,
|
|
188
|
+
on_backoff=backoff_handler,
|
|
189
|
+
)
|
|
190
|
+
def _run_index_query_with_retry(neo4j_session: neo4j.Session, query: str) -> None:
|
|
191
|
+
"""
|
|
192
|
+
Execute an index creation query with retry logic.
|
|
193
|
+
Index creation requires autocommit transactions and can experience transient errors.
|
|
194
|
+
|
|
195
|
+
Handles the EquivalentSchemaRuleAlreadyExists error that can occur when multiple
|
|
196
|
+
parallel sync operations attempt to create the same index simultaneously. Even though
|
|
197
|
+
we use CREATE INDEX IF NOT EXISTS, Neo4j has a race condition where concurrent
|
|
198
|
+
index creation can fail if another session creates the index between the existence
|
|
199
|
+
check and the actual creation.
|
|
200
|
+
"""
|
|
201
|
+
try:
|
|
202
|
+
neo4j_session.run(query)
|
|
203
|
+
except neo4j.exceptions.ClientError as e:
|
|
204
|
+
# EquivalentSchemaRuleAlreadyExists means another parallel sync already created
|
|
205
|
+
# this index, which is the desired end state. Safe to ignore.
|
|
206
|
+
if e.code == "Neo.ClientError.Schema.EquivalentSchemaRuleAlreadyExists":
|
|
207
|
+
logger.debug(
|
|
208
|
+
f"Index already exists (likely created by parallel sync): {query}"
|
|
209
|
+
)
|
|
210
|
+
return
|
|
211
|
+
raise
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def execute_write_with_retry(
|
|
215
|
+
neo4j_session: neo4j.Session,
|
|
216
|
+
tx_func: Any,
|
|
217
|
+
*args: Any,
|
|
218
|
+
**kwargs: Any,
|
|
219
|
+
) -> Any:
|
|
220
|
+
"""
|
|
221
|
+
Execute a custom transaction function with retry logic for transient errors.
|
|
222
|
+
|
|
223
|
+
This is a generic wrapper for any custom transaction function that needs retry logic
|
|
224
|
+
for EntityNotFound and other transient errors. Use this when you have complex
|
|
225
|
+
transaction logic that doesn't fit the standard load_graph_data pattern.
|
|
226
|
+
|
|
227
|
+
Example usage:
|
|
228
|
+
def my_custom_tx(tx, data_list, update_tag):
|
|
229
|
+
for item in data_list:
|
|
230
|
+
tx.run(query, **item).consume()
|
|
231
|
+
|
|
232
|
+
execute_write_with_retry(
|
|
233
|
+
neo4j_session,
|
|
234
|
+
my_custom_tx,
|
|
235
|
+
data_list,
|
|
236
|
+
update_tag
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
:param neo4j_session: The Neo4j session
|
|
240
|
+
:param tx_func: The transaction function to execute (takes neo4j.Transaction as first arg)
|
|
241
|
+
:param args: Positional arguments to pass to tx_func
|
|
242
|
+
:param kwargs: Keyword arguments to pass to tx_func
|
|
243
|
+
:return: The return value of tx_func
|
|
244
|
+
"""
|
|
245
|
+
|
|
246
|
+
target = getattr(tx_func, "__qualname__", repr(tx_func))
|
|
247
|
+
operation = partial(neo4j_session.execute_write, tx_func, *args, **kwargs)
|
|
248
|
+
return _run_with_retry(operation, target)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def run_write_query(
|
|
252
|
+
neo4j_session: neo4j.Session, query: str, **parameters: Any
|
|
253
|
+
) -> None:
|
|
254
|
+
"""
|
|
255
|
+
Execute a write query inside a managed transaction with retry logic.
|
|
256
|
+
|
|
257
|
+
This function now includes retry logic for:
|
|
258
|
+
- Network errors (ConnectionResetError)
|
|
259
|
+
- Service unavailability (ServiceUnavailable, SessionExpired)
|
|
260
|
+
- Transient database errors (TransientError)
|
|
261
|
+
- EntityNotFound errors during concurrent operations (specific ClientError)
|
|
262
|
+
|
|
263
|
+
Used by intel modules that run manual transactions (e.g., GCP firewalls, AWS resources).
|
|
264
|
+
|
|
265
|
+
:param neo4j_session: The Neo4j session
|
|
266
|
+
:param query: The Cypher query to execute
|
|
267
|
+
:param parameters: Parameters to pass to the query
|
|
268
|
+
:return: None
|
|
269
|
+
"""
|
|
270
|
+
|
|
271
|
+
def _run_query_tx(tx: neo4j.Transaction) -> None:
|
|
272
|
+
tx.run(query, **parameters).consume()
|
|
273
|
+
|
|
274
|
+
def _operation() -> None:
|
|
275
|
+
neo4j_session.execute_write(_run_query_tx)
|
|
276
|
+
|
|
277
|
+
_run_with_retry(_operation, _run_query_tx.__qualname__)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def read_list_of_values_tx(
|
|
281
|
+
tx: neo4j.Transaction,
|
|
282
|
+
query: str,
|
|
283
|
+
**kwargs,
|
|
284
|
+
) -> List[Union[str, int]]:
|
|
17
285
|
"""
|
|
18
286
|
Runs the given Neo4j query in the given transaction object and returns a list of either str or int. This is intended
|
|
19
287
|
to be run only with queries that return a list of a single field.
|
|
@@ -21,7 +289,7 @@ def read_list_of_values_tx(tx: neo4j.Transaction, query: str, **kwargs) -> List[
|
|
|
21
289
|
Example usage:
|
|
22
290
|
query = "MATCH (a:TestNode) RETURN a.name ORDER BY a.name"
|
|
23
291
|
|
|
24
|
-
values = neo4j_session.
|
|
292
|
+
values = neo4j_session.execute_read(read_list_of_values_tx, query)
|
|
25
293
|
|
|
26
294
|
:param tx: A neo4j read transaction object
|
|
27
295
|
:param query: A neo4j query string that returns a list of single values. For example,
|
|
@@ -39,7 +307,11 @@ def read_list_of_values_tx(tx: neo4j.Transaction, query: str, **kwargs) -> List[
|
|
|
39
307
|
return values
|
|
40
308
|
|
|
41
309
|
|
|
42
|
-
def read_single_value_tx(
|
|
310
|
+
def read_single_value_tx(
|
|
311
|
+
tx: neo4j.Transaction,
|
|
312
|
+
query: str,
|
|
313
|
+
**kwargs,
|
|
314
|
+
) -> Optional[Union[str, int]]:
|
|
43
315
|
"""
|
|
44
316
|
Runs the given Neo4j query in the given transaction object and returns a str, int, or None. This is intended to be
|
|
45
317
|
run only with queries that return a single str, int, or None value.
|
|
@@ -70,7 +342,11 @@ def read_single_value_tx(tx: neo4j.Transaction, query: str, **kwargs) -> Optiona
|
|
|
70
342
|
return value
|
|
71
343
|
|
|
72
344
|
|
|
73
|
-
def read_list_of_dicts_tx(
|
|
345
|
+
def read_list_of_dicts_tx(
|
|
346
|
+
tx: neo4j.Transaction,
|
|
347
|
+
query: str,
|
|
348
|
+
**kwargs,
|
|
349
|
+
) -> List[Dict[str, Any]]:
|
|
74
350
|
"""
|
|
75
351
|
Runs the given Neo4j query in the given transaction object and returns the results as a list of dicts.
|
|
76
352
|
|
|
@@ -92,7 +368,11 @@ def read_list_of_dicts_tx(tx: neo4j.Transaction, query: str, **kwargs) -> List[D
|
|
|
92
368
|
return values
|
|
93
369
|
|
|
94
370
|
|
|
95
|
-
def read_list_of_tuples_tx(
|
|
371
|
+
def read_list_of_tuples_tx(
|
|
372
|
+
tx: neo4j.Transaction,
|
|
373
|
+
query: str,
|
|
374
|
+
**kwargs,
|
|
375
|
+
) -> List[Tuple[Any, ...]]:
|
|
96
376
|
"""
|
|
97
377
|
Runs the given Neo4j query in the given transaction object and returns the results as a list of tuples.
|
|
98
378
|
|
|
@@ -122,7 +402,7 @@ def read_list_of_tuples_tx(tx: neo4j.Transaction, query: str, **kwargs) -> List[
|
|
|
122
402
|
return [tuple(val) for val in values]
|
|
123
403
|
|
|
124
404
|
|
|
125
|
-
def read_single_dict_tx(tx: neo4j.Transaction, query: str, **kwargs) ->
|
|
405
|
+
def read_single_dict_tx(tx: neo4j.Transaction, query: str, **kwargs) -> Any:
|
|
126
406
|
"""
|
|
127
407
|
Runs the given Neo4j query in the given transaction object and returns the single dict result. This is intended to
|
|
128
408
|
be run only with queries that return a single dict.
|
|
@@ -154,9 +434,9 @@ def read_single_dict_tx(tx: neo4j.Transaction, query: str, **kwargs) -> Dict[str
|
|
|
154
434
|
|
|
155
435
|
|
|
156
436
|
def write_list_of_dicts_tx(
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
437
|
+
tx: neo4j.Transaction,
|
|
438
|
+
query: str,
|
|
439
|
+
**kwargs,
|
|
160
440
|
) -> None:
|
|
161
441
|
"""
|
|
162
442
|
Writes a list of dicts to Neo4j.
|
|
@@ -168,7 +448,7 @@ def write_list_of_dicts_tx(
|
|
|
168
448
|
neo4j_driver = neo4j.driver(... args ...)
|
|
169
449
|
neo4j_session = neo4j_driver.Session(... args ...)
|
|
170
450
|
|
|
171
|
-
neo4j_session.
|
|
451
|
+
neo4j_session.execute_write(
|
|
172
452
|
write_list_of_dicts_tx,
|
|
173
453
|
'''
|
|
174
454
|
UNWIND $DictList as data
|
|
@@ -188,26 +468,43 @@ def write_list_of_dicts_tx(
|
|
|
188
468
|
:param kwargs: Keyword args to be supplied to the Neo4j query.
|
|
189
469
|
:return: None
|
|
190
470
|
"""
|
|
191
|
-
tx.run(query, kwargs)
|
|
471
|
+
tx.run(query, kwargs).consume()
|
|
192
472
|
|
|
193
473
|
|
|
194
474
|
def load_graph_data(
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
475
|
+
neo4j_session: neo4j.Session,
|
|
476
|
+
query: str,
|
|
477
|
+
dict_list: List[Dict[str, Any]],
|
|
478
|
+
batch_size: int = 10000,
|
|
479
|
+
**kwargs,
|
|
199
480
|
) -> None:
|
|
200
481
|
"""
|
|
201
|
-
Writes data to the graph.
|
|
482
|
+
Writes data to the graph with retry logic for transient errors.
|
|
483
|
+
|
|
484
|
+
This function handles retries for:
|
|
485
|
+
- Network errors (ConnectionResetError)
|
|
486
|
+
- Service unavailability (ServiceUnavailable, SessionExpired)
|
|
487
|
+
- Transient database errors (TransientError)
|
|
488
|
+
- EntityNotFound errors during concurrent operations (ClientError with specific code)
|
|
489
|
+
|
|
490
|
+
EntityNotFound errors are retried because they commonly occur during concurrent
|
|
491
|
+
write operations when multiple threads access the same node space. This is expected
|
|
492
|
+
behavior in Neo4j's query execution pipeline, not a permanent failure.
|
|
493
|
+
|
|
202
494
|
:param neo4j_session: The Neo4j session
|
|
203
495
|
:param query: The Neo4j write query to run. This query is not meant to be handwritten, rather it should be generated
|
|
204
496
|
with cartography.graph.querybuilder.build_ingestion_query().
|
|
205
497
|
:param dict_list: The data to load to the graph represented as a list of dicts.
|
|
498
|
+
:param batch_size: The number of items to process per transaction. Defaults to 10000.
|
|
206
499
|
:param kwargs: Allows additional keyword args to be supplied to the Neo4j query.
|
|
207
500
|
:return: None
|
|
208
501
|
"""
|
|
209
|
-
|
|
210
|
-
|
|
502
|
+
if batch_size <= 0:
|
|
503
|
+
raise ValueError(f"batch_size must be greater than 0, got {batch_size}")
|
|
504
|
+
|
|
505
|
+
for data_batch in batch(dict_list, size=batch_size):
|
|
506
|
+
execute_write_with_retry(
|
|
507
|
+
neo4j_session,
|
|
211
508
|
write_list_of_dicts_tx,
|
|
212
509
|
query,
|
|
213
510
|
DictList=data_batch,
|
|
@@ -215,7 +512,10 @@ def load_graph_data(
|
|
|
215
512
|
)
|
|
216
513
|
|
|
217
514
|
|
|
218
|
-
def ensure_indexes(
|
|
515
|
+
def ensure_indexes(
|
|
516
|
+
neo4j_session: neo4j.Session,
|
|
517
|
+
node_schema: CartographyNodeSchema,
|
|
518
|
+
) -> None:
|
|
219
519
|
"""
|
|
220
520
|
Creates indexes if they don't exist for the given CartographyNodeSchema object, as well as for all of the
|
|
221
521
|
relationships defined on its `other_relationships` and `sub_resource_relationship` fields. This operation is
|
|
@@ -229,16 +529,38 @@ def ensure_indexes(neo4j_session: neo4j.Session, node_schema: CartographyNodeSch
|
|
|
229
529
|
queries = build_create_index_queries(node_schema)
|
|
230
530
|
|
|
231
531
|
for query in queries:
|
|
232
|
-
if not query.startswith(
|
|
233
|
-
raise ValueError(
|
|
234
|
-
|
|
532
|
+
if not query.startswith("CREATE INDEX IF NOT EXISTS"):
|
|
533
|
+
raise ValueError(
|
|
534
|
+
'Query provided to `ensure_indexes()` does not start with "CREATE INDEX IF NOT EXISTS".',
|
|
535
|
+
)
|
|
536
|
+
_run_index_query_with_retry(neo4j_session, query)
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
def ensure_indexes_for_matchlinks(
|
|
540
|
+
neo4j_session: neo4j.Session,
|
|
541
|
+
rel_schema: CartographyRelSchema,
|
|
542
|
+
) -> None:
|
|
543
|
+
"""
|
|
544
|
+
Creates indexes for node fields if they don't exist for the given CartographyRelSchema object.
|
|
545
|
+
This is only used for load_rels() where we match on and connect existing nodes.
|
|
546
|
+
This is not used for CartographyNodeSchema objects.
|
|
547
|
+
"""
|
|
548
|
+
queries = build_create_index_queries_for_matchlink(rel_schema)
|
|
549
|
+
logger.debug(f"CREATE INDEX queries for {rel_schema.rel_label}: {queries}")
|
|
550
|
+
for query in queries:
|
|
551
|
+
if not query.startswith("CREATE INDEX IF NOT EXISTS"):
|
|
552
|
+
raise ValueError(
|
|
553
|
+
'Query provided to `ensure_indexes_for_matchlinks()` does not start with "CREATE INDEX IF NOT EXISTS".',
|
|
554
|
+
)
|
|
555
|
+
_run_index_query_with_retry(neo4j_session, query)
|
|
235
556
|
|
|
236
557
|
|
|
237
558
|
def load(
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
559
|
+
neo4j_session: neo4j.Session,
|
|
560
|
+
node_schema: CartographyNodeSchema,
|
|
561
|
+
dict_list: List[Dict[str, Any]],
|
|
562
|
+
batch_size: int = 10000,
|
|
563
|
+
**kwargs,
|
|
242
564
|
) -> None:
|
|
243
565
|
"""
|
|
244
566
|
Main entrypoint for intel modules to write data to the graph. Ensures that indexes exist for the datatypes loaded
|
|
@@ -246,9 +568,60 @@ def load(
|
|
|
246
568
|
:param neo4j_session: The Neo4j session
|
|
247
569
|
:param node_schema: The CartographyNodeSchema object to create indexes for and generate a query.
|
|
248
570
|
:param dict_list: The data to load to the graph represented as a list of dicts.
|
|
571
|
+
:param batch_size: The number of items to process per transaction. Defaults to 10000.
|
|
249
572
|
:param kwargs: Allows additional keyword args to be supplied to the Neo4j query.
|
|
250
573
|
:return: None
|
|
251
574
|
"""
|
|
575
|
+
if batch_size <= 0:
|
|
576
|
+
raise ValueError(f"batch_size must be greater than 0, got {batch_size}")
|
|
577
|
+
if len(dict_list) == 0:
|
|
578
|
+
# If there is no data to load, save some time.
|
|
579
|
+
return
|
|
252
580
|
ensure_indexes(neo4j_session, node_schema)
|
|
253
581
|
ingestion_query = build_ingestion_query(node_schema)
|
|
254
|
-
load_graph_data(
|
|
582
|
+
load_graph_data(
|
|
583
|
+
neo4j_session, ingestion_query, dict_list, batch_size=batch_size, **kwargs
|
|
584
|
+
)
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
def load_matchlinks(
|
|
588
|
+
neo4j_session: neo4j.Session,
|
|
589
|
+
rel_schema: CartographyRelSchema,
|
|
590
|
+
dict_list: list[dict[str, Any]],
|
|
591
|
+
batch_size: int = 10000,
|
|
592
|
+
**kwargs,
|
|
593
|
+
) -> None:
|
|
594
|
+
"""
|
|
595
|
+
Main entrypoint for intel modules to write relationships to the graph between two existing nodes.
|
|
596
|
+
:param neo4j_session: The Neo4j session
|
|
597
|
+
:param rel_schema: The CartographyRelSchema object to generate a query.
|
|
598
|
+
:param dict_list: The data to load to the graph represented as a list of dicts. The dicts must contain the source and
|
|
599
|
+
target node ids.
|
|
600
|
+
:param batch_size: The number of items to process per transaction. Defaults to 10000.
|
|
601
|
+
:param kwargs: Allows additional keyword args to be supplied to the Neo4j query.
|
|
602
|
+
:return: None
|
|
603
|
+
"""
|
|
604
|
+
if batch_size <= 0:
|
|
605
|
+
raise ValueError(f"batch_size must be greater than 0, got {batch_size}")
|
|
606
|
+
if len(dict_list) == 0:
|
|
607
|
+
# If there is no data to load, save some time.
|
|
608
|
+
return
|
|
609
|
+
|
|
610
|
+
# Validate that required kwargs are provided for cleanup queries
|
|
611
|
+
if "_sub_resource_label" not in kwargs:
|
|
612
|
+
raise ValueError(
|
|
613
|
+
f"Required kwarg '_sub_resource_label' not provided for {rel_schema.rel_label}. "
|
|
614
|
+
"This is needed for cleanup queries."
|
|
615
|
+
)
|
|
616
|
+
if "_sub_resource_id" not in kwargs:
|
|
617
|
+
raise ValueError(
|
|
618
|
+
f"Required kwarg '_sub_resource_id' not provided for {rel_schema.rel_label}. "
|
|
619
|
+
"This is needed for cleanup queries."
|
|
620
|
+
)
|
|
621
|
+
|
|
622
|
+
ensure_indexes_for_matchlinks(neo4j_session, rel_schema)
|
|
623
|
+
matchlink_query = build_matchlink_query(rel_schema)
|
|
624
|
+
logger.debug(f"Matchlink query: {matchlink_query}")
|
|
625
|
+
load_graph_data(
|
|
626
|
+
neo4j_session, matchlink_query, dict_list, batch_size=batch_size, **kwargs
|
|
627
|
+
)
|