cartography 0.104.0rc2__py3-none-any.whl → 0.123.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cartography/_version.py +16 -3
- cartography/cli.py +466 -5
- cartography/client/aws/__init__.py +19 -0
- cartography/client/aws/ecr.py +51 -0
- cartography/client/core/tx.py +357 -8
- cartography/config.py +153 -0
- cartography/data/azure_permission_relationships.yaml +20 -0
- cartography/data/gcp_permission_relationships.yaml +21 -0
- cartography/data/indexes.cypher +0 -186
- cartography/data/jobs/analysis/aws_ec2_keypair_analysis.json +2 -2
- cartography/data/jobs/analysis/keycloak_inheritance.json +30 -0
- cartography/data/jobs/cleanup/gcp_compute_vpc_cleanup.json +0 -12
- cartography/data/jobs/cleanup/github_repos_cleanup.json +2 -0
- cartography/driftdetect/cli.py +3 -2
- cartography/graph/cleanupbuilder.py +198 -41
- cartography/graph/job.py +54 -6
- cartography/graph/querybuilder.py +528 -27
- cartography/graph/statement.py +5 -1
- cartography/intel/airbyte/__init__.py +105 -0
- cartography/intel/airbyte/connections.py +120 -0
- cartography/intel/airbyte/destinations.py +81 -0
- cartography/intel/airbyte/organizations.py +59 -0
- cartography/intel/airbyte/sources.py +78 -0
- cartography/intel/airbyte/tags.py +64 -0
- cartography/intel/airbyte/users.py +106 -0
- cartography/intel/airbyte/util.py +122 -0
- cartography/intel/airbyte/workspaces.py +63 -0
- cartography/intel/aws/__init__.py +24 -9
- cartography/intel/aws/acm.py +124 -0
- cartography/intel/aws/apigateway.py +253 -22
- cartography/intel/aws/apigatewayv2.py +116 -0
- cartography/intel/aws/cloudtrail.py +17 -39
- cartography/intel/aws/cloudtrail_management_events.py +962 -0
- cartography/intel/aws/cloudwatch.py +150 -4
- cartography/intel/aws/codebuild.py +132 -0
- cartography/intel/aws/cognito.py +201 -0
- cartography/intel/aws/config.py +7 -3
- cartography/intel/aws/ec2/elastic_ip_addresses.py +3 -1
- cartography/intel/aws/ec2/instances.py +25 -1
- cartography/intel/aws/ec2/internet_gateways.py +4 -2
- cartography/intel/aws/ec2/load_balancer_v2s.py +11 -5
- cartography/intel/aws/ec2/network_interfaces.py +5 -1
- cartography/intel/aws/ec2/reserved_instances.py +3 -1
- cartography/intel/aws/ec2/security_groups.py +140 -122
- cartography/intel/aws/ec2/snapshots.py +47 -84
- cartography/intel/aws/ec2/subnets.py +37 -63
- cartography/intel/aws/ec2/tgw.py +11 -5
- cartography/intel/aws/ec2/volumes.py +1 -1
- cartography/intel/aws/ec2/vpc.py +140 -124
- cartography/intel/aws/ec2/vpc_peerings.py +262 -125
- cartography/intel/aws/ecr.py +269 -98
- cartography/intel/aws/ecr_image_layers.py +923 -0
- cartography/intel/aws/ecs.py +251 -380
- cartography/intel/aws/efs.py +179 -11
- cartography/intel/aws/elasticache.py +102 -79
- cartography/intel/aws/elasticsearch.py +13 -4
- cartography/intel/aws/eventbridge.py +164 -0
- cartography/intel/aws/glue.py +181 -0
- cartography/intel/aws/guardduty.py +443 -0
- cartography/intel/aws/iam.py +750 -493
- cartography/intel/aws/identitycenter.py +605 -83
- cartography/intel/aws/inspector.py +221 -105
- cartography/intel/aws/kms.py +173 -201
- cartography/intel/aws/lambda_function.py +272 -189
- cartography/intel/aws/organizations.py +10 -9
- cartography/intel/aws/permission_relationships.py +10 -20
- cartography/intel/aws/rds.py +337 -446
- cartography/intel/aws/redshift.py +9 -4
- cartography/intel/aws/resourcegroupstaggingapi.py +78 -19
- cartography/intel/aws/resources.py +18 -0
- cartography/intel/aws/route53.py +386 -332
- cartography/intel/aws/s3.py +322 -14
- cartography/intel/aws/secretsmanager.py +81 -49
- cartography/intel/aws/securityhub.py +3 -1
- cartography/intel/aws/sns.py +62 -2
- cartography/intel/aws/sqs.py +36 -90
- cartography/intel/aws/ssm.py +3 -5
- cartography/intel/azure/__init__.py +202 -48
- cartography/intel/azure/aks.py +175 -0
- cartography/intel/azure/app_service.py +105 -0
- cartography/intel/azure/compute.py +59 -112
- cartography/intel/azure/container_instances.py +95 -0
- cartography/intel/azure/cosmosdb.py +222 -361
- cartography/intel/azure/data_factory.py +85 -0
- cartography/intel/azure/data_factory_dataset.py +128 -0
- cartography/intel/azure/data_factory_linked_service.py +119 -0
- cartography/intel/azure/data_factory_pipeline.py +142 -0
- cartography/intel/azure/data_lake.py +124 -0
- cartography/intel/azure/event_grid.py +94 -0
- cartography/intel/azure/functions.py +124 -0
- cartography/intel/azure/load_balancers.py +263 -0
- cartography/intel/azure/logic_apps.py +101 -0
- cartography/intel/azure/monitor.py +105 -0
- cartography/intel/azure/network.py +467 -0
- cartography/intel/azure/permission_relationships.py +466 -0
- cartography/intel/azure/rbac.py +309 -0
- cartography/intel/azure/resource_groups.py +82 -0
- cartography/intel/azure/security_center.py +106 -0
- cartography/intel/azure/sql.py +145 -292
- cartography/intel/azure/storage.py +185 -262
- cartography/intel/azure/subscription.py +21 -43
- cartography/intel/azure/tenant.py +39 -30
- cartography/intel/azure/util/common.py +13 -0
- cartography/intel/azure/util/credentials.py +49 -174
- cartography/intel/azure/util/tag.py +41 -0
- cartography/intel/create_indexes.py +2 -1
- cartography/intel/crowdstrike/spotlight.py +5 -2
- cartography/intel/dns.py +5 -2
- cartography/intel/entra/__init__.py +100 -1
- cartography/intel/entra/app_role_assignments.py +284 -0
- cartography/intel/entra/applications.py +182 -0
- cartography/intel/entra/federation/__init__.py +0 -0
- cartography/intel/entra/federation/aws_identity_center.py +77 -0
- cartography/intel/entra/groups.py +198 -0
- cartography/intel/entra/ou.py +48 -24
- cartography/intel/entra/service_principals.py +217 -0
- cartography/intel/entra/users.py +105 -57
- cartography/intel/gcp/__init__.py +334 -396
- cartography/intel/gcp/bigtable_app_profile.py +101 -0
- cartography/intel/gcp/bigtable_backup.py +91 -0
- cartography/intel/gcp/bigtable_cluster.py +93 -0
- cartography/intel/gcp/bigtable_instance.py +86 -0
- cartography/intel/gcp/bigtable_table.py +87 -0
- cartography/intel/gcp/cai.py +292 -0
- cartography/intel/gcp/clients.py +112 -0
- cartography/intel/gcp/compute.py +128 -119
- cartography/intel/gcp/crm/__init__.py +0 -0
- cartography/intel/gcp/crm/folders.py +114 -0
- cartography/intel/gcp/crm/orgs.py +70 -0
- cartography/intel/gcp/crm/projects.py +120 -0
- cartography/intel/gcp/dns.py +83 -169
- cartography/intel/gcp/gke.py +72 -113
- cartography/intel/gcp/iam.py +111 -91
- cartography/intel/gcp/permission_relationships.py +394 -0
- cartography/intel/gcp/policy_bindings.py +225 -0
- cartography/intel/gcp/storage.py +75 -159
- cartography/intel/github/__init__.py +62 -25
- cartography/intel/github/commits.py +423 -0
- cartography/intel/github/repos.py +463 -85
- cartography/intel/github/teams.py +3 -3
- cartography/intel/github/users.py +5 -0
- cartography/intel/github/util.py +12 -0
- cartography/intel/googleworkspace/__init__.py +193 -0
- cartography/intel/googleworkspace/devices.py +254 -0
- cartography/intel/googleworkspace/groups.py +568 -0
- cartography/intel/googleworkspace/oauth_apps.py +259 -0
- cartography/intel/googleworkspace/tenant.py +85 -0
- cartography/intel/googleworkspace/users.py +138 -0
- cartography/intel/gsuite/__init__.py +17 -9
- cartography/intel/gsuite/groups.py +291 -0
- cartography/intel/gsuite/users.py +142 -0
- cartography/intel/jamf/computers.py +7 -1
- cartography/intel/keycloak/__init__.py +153 -0
- cartography/intel/keycloak/authenticationexecutions.py +322 -0
- cartography/intel/keycloak/authenticationflows.py +77 -0
- cartography/intel/keycloak/clients.py +187 -0
- cartography/intel/keycloak/groups.py +126 -0
- cartography/intel/keycloak/identityproviders.py +94 -0
- cartography/intel/keycloak/organizations.py +163 -0
- cartography/intel/keycloak/realms.py +61 -0
- cartography/intel/keycloak/roles.py +202 -0
- cartography/intel/keycloak/scopes.py +73 -0
- cartography/intel/keycloak/users.py +70 -0
- cartography/intel/keycloak/util.py +47 -0
- cartography/intel/kubernetes/__init__.py +60 -14
- cartography/intel/kubernetes/clusters.py +86 -0
- cartography/intel/kubernetes/eks.py +402 -0
- cartography/intel/kubernetes/namespaces.py +59 -57
- cartography/intel/kubernetes/pods.py +168 -75
- cartography/intel/kubernetes/rbac.py +597 -0
- cartography/intel/kubernetes/secrets.py +95 -45
- cartography/intel/kubernetes/services.py +131 -67
- cartography/intel/kubernetes/util.py +142 -14
- cartography/intel/oci/iam.py +23 -9
- cartography/intel/oci/organizations.py +3 -1
- cartography/intel/oci/utils.py +28 -5
- cartography/intel/okta/applications.py +15 -5
- cartography/intel/okta/awssaml.py +14 -10
- cartography/intel/okta/factors.py +3 -1
- cartography/intel/okta/groups.py +5 -2
- cartography/intel/okta/organization.py +3 -1
- cartography/intel/okta/origins.py +3 -1
- cartography/intel/okta/roles.py +5 -2
- cartography/intel/okta/users.py +10 -2
- cartography/intel/ontology/__init__.py +44 -0
- cartography/intel/ontology/devices.py +54 -0
- cartography/intel/ontology/users.py +54 -0
- cartography/intel/ontology/utils.py +176 -0
- cartography/intel/pagerduty/escalation_policies.py +13 -6
- cartography/intel/pagerduty/schedules.py +9 -4
- cartography/intel/pagerduty/services.py +7 -3
- cartography/intel/pagerduty/teams.py +5 -2
- cartography/intel/pagerduty/users.py +3 -1
- cartography/intel/pagerduty/vendors.py +3 -1
- cartography/intel/scaleway/__init__.py +127 -0
- cartography/intel/scaleway/iam/__init__.py +0 -0
- cartography/intel/scaleway/iam/apikeys.py +71 -0
- cartography/intel/scaleway/iam/applications.py +71 -0
- cartography/intel/scaleway/iam/groups.py +71 -0
- cartography/intel/scaleway/iam/users.py +71 -0
- cartography/intel/scaleway/instances/__init__.py +0 -0
- cartography/intel/scaleway/instances/flexibleips.py +86 -0
- cartography/intel/scaleway/instances/instances.py +92 -0
- cartography/intel/scaleway/projects.py +79 -0
- cartography/intel/scaleway/storage/__init__.py +0 -0
- cartography/intel/scaleway/storage/snapshots.py +86 -0
- cartography/intel/scaleway/storage/volumes.py +84 -0
- cartography/intel/scaleway/utils.py +37 -0
- cartography/intel/sentinelone/__init__.py +75 -0
- cartography/intel/sentinelone/account.py +140 -0
- cartography/intel/sentinelone/agent.py +139 -0
- cartography/intel/sentinelone/api.py +124 -0
- cartography/intel/sentinelone/application.py +248 -0
- cartography/intel/sentinelone/cve.py +119 -0
- cartography/intel/sentinelone/utils.py +28 -0
- cartography/intel/slack/__init__.py +78 -0
- cartography/intel/slack/channels.py +80 -0
- cartography/intel/slack/groups.py +90 -0
- cartography/intel/slack/teams.py +65 -0
- cartography/intel/slack/users.py +57 -0
- cartography/intel/slack/utils.py +29 -0
- cartography/intel/spacelift/__init__.py +161 -0
- cartography/intel/spacelift/account.py +73 -0
- cartography/intel/spacelift/ec2_ownership.py +280 -0
- cartography/intel/spacelift/runs.py +463 -0
- cartography/intel/spacelift/spaces.py +112 -0
- cartography/intel/spacelift/stacks.py +119 -0
- cartography/intel/spacelift/util.py +122 -0
- cartography/intel/spacelift/workerpools.py +131 -0
- cartography/intel/spacelift/workers.py +128 -0
- cartography/intel/trivy/__init__.py +272 -0
- cartography/intel/trivy/scanner.py +386 -0
- cartography/models/airbyte/__init__.py +0 -0
- cartography/models/airbyte/connection.py +138 -0
- cartography/models/airbyte/destination.py +75 -0
- cartography/models/airbyte/organization.py +19 -0
- cartography/models/airbyte/source.py +75 -0
- cartography/models/airbyte/stream.py +74 -0
- cartography/models/airbyte/tag.py +69 -0
- cartography/models/airbyte/user.py +115 -0
- cartography/models/airbyte/workspace.py +46 -0
- cartography/models/anthropic/apikey.py +4 -0
- cartography/models/anthropic/user.py +4 -0
- cartography/models/aws/acm/__init__.py +0 -0
- cartography/models/aws/acm/certificate.py +75 -0
- cartography/models/aws/apigateway/__init__.py +0 -0
- cartography/models/aws/apigateway/apigatewaydeployment.py +74 -0
- cartography/models/aws/apigateway/apigatewayintegration.py +79 -0
- cartography/models/aws/apigateway/apigatewaymethod.py +74 -0
- cartography/models/aws/apigatewayv2/__init__.py +0 -0
- cartography/models/aws/apigatewayv2/apigatewayv2.py +53 -0
- cartography/models/aws/cloudtrail/management_events.py +153 -0
- cartography/models/aws/cloudtrail/trail.py +45 -0
- cartography/models/aws/cloudwatch/log_metric_filter.py +79 -0
- cartography/models/aws/cloudwatch/metric_alarm.py +53 -0
- cartography/models/aws/codebuild/__init__.py +0 -0
- cartography/models/aws/codebuild/project.py +49 -0
- cartography/models/aws/cognito/__init__.py +0 -0
- cartography/models/aws/cognito/identity_pool.py +70 -0
- cartography/models/aws/cognito/user_pool.py +47 -0
- cartography/models/aws/dynamodb/tables.py +2 -0
- cartography/models/aws/ec2/instances.py +25 -1
- cartography/models/aws/ec2/networkinterfaces.py +4 -0
- cartography/models/aws/ec2/security_group_rules.py +109 -0
- cartography/models/aws/ec2/security_groups.py +90 -0
- cartography/models/aws/ec2/snapshots.py +58 -0
- cartography/models/aws/ec2/subnet_instance.py +2 -0
- cartography/models/aws/ec2/subnet_networkinterface.py +2 -0
- cartography/models/aws/ec2/subnets.py +65 -0
- cartography/models/aws/ec2/volumes.py +20 -0
- cartography/models/aws/ec2/vpc.py +46 -0
- cartography/models/aws/ec2/vpc_cidr.py +102 -0
- cartography/models/aws/ec2/vpc_peering.py +157 -0
- cartography/models/aws/ecr/__init__.py +0 -0
- cartography/models/aws/ecr/image.py +146 -0
- cartography/models/aws/ecr/image_layer.py +107 -0
- cartography/models/aws/ecr/repository.py +72 -0
- cartography/models/aws/ecr/repository_image.py +95 -0
- cartography/models/aws/ecs/__init__.py +0 -0
- cartography/models/aws/ecs/clusters.py +64 -0
- cartography/models/aws/ecs/container_definitions.py +93 -0
- cartography/models/aws/ecs/container_instances.py +84 -0
- cartography/models/aws/ecs/containers.py +101 -0
- cartography/models/aws/ecs/services.py +134 -0
- cartography/models/aws/ecs/task_definitions.py +135 -0
- cartography/models/aws/ecs/tasks.py +134 -0
- cartography/models/aws/efs/access_point.py +77 -0
- cartography/models/aws/efs/file_system.py +60 -0
- cartography/models/aws/efs/mount_target.py +29 -2
- cartography/models/aws/elasticache/__init__.py +0 -0
- cartography/models/aws/elasticache/cluster.py +65 -0
- cartography/models/aws/elasticache/topic.py +67 -0
- cartography/models/aws/eventbridge/__init__.py +0 -0
- cartography/models/aws/eventbridge/rule.py +77 -0
- cartography/models/aws/eventbridge/target.py +71 -0
- cartography/models/aws/glue/__init__.py +0 -0
- cartography/models/aws/glue/connection.py +51 -0
- cartography/models/aws/glue/job.py +69 -0
- cartography/models/aws/guardduty/__init__.py +1 -0
- cartography/models/aws/guardduty/detectors.py +50 -0
- cartography/models/aws/guardduty/findings.py +121 -0
- cartography/models/aws/iam/access_key.py +103 -0
- cartography/models/aws/iam/account_role.py +24 -0
- cartography/models/aws/iam/federated_principal.py +60 -0
- cartography/models/aws/iam/group.py +60 -0
- cartography/models/aws/iam/group_membership.py +27 -0
- cartography/models/aws/iam/inline_policy.py +78 -0
- cartography/models/aws/iam/managed_policy.py +51 -0
- cartography/models/aws/iam/policy_statement.py +57 -0
- cartography/models/aws/iam/role.py +83 -0
- cartography/models/aws/iam/root_principal.py +52 -0
- cartography/models/aws/iam/service_principal.py +30 -0
- cartography/models/aws/iam/sts_assumerole_allow.py +38 -0
- cartography/models/aws/iam/user.py +59 -0
- cartography/models/aws/identitycenter/awsidentitycenter.py +1 -0
- cartography/models/aws/identitycenter/awspermissionset.py +70 -0
- cartography/models/aws/identitycenter/awssogroup.py +70 -0
- cartography/models/aws/identitycenter/awsssouser.py +49 -9
- cartography/models/aws/inspector/findings.py +37 -0
- cartography/models/aws/inspector/packages.py +1 -31
- cartography/models/aws/kms/__init__.py +0 -0
- cartography/models/aws/kms/aliases.py +86 -0
- cartography/models/aws/kms/grants.py +65 -0
- cartography/models/aws/kms/keys.py +88 -0
- cartography/models/aws/lambda_function/__init__.py +0 -0
- cartography/models/aws/lambda_function/alias.py +74 -0
- cartography/models/aws/lambda_function/event_source_mapping.py +88 -0
- cartography/models/aws/lambda_function/lambda_function.py +91 -0
- cartography/models/aws/lambda_function/layer.py +72 -0
- cartography/models/aws/rds/__init__.py +0 -0
- cartography/models/aws/rds/cluster.py +91 -0
- cartography/models/aws/rds/event_subscription.py +146 -0
- cartography/models/aws/rds/instance.py +156 -0
- cartography/models/aws/rds/snapshot.py +108 -0
- cartography/models/aws/rds/subnet_group.py +101 -0
- cartography/models/aws/route53/__init__.py +0 -0
- cartography/models/aws/route53/dnsrecord.py +235 -0
- cartography/models/aws/route53/nameserver.py +63 -0
- cartography/models/aws/route53/subzone.py +40 -0
- cartography/models/aws/route53/zone.py +47 -0
- cartography/models/aws/s3/notification.py +24 -0
- cartography/models/aws/secretsmanager/secret.py +106 -0
- cartography/models/aws/secretsmanager/secret_version.py +0 -2
- cartography/models/aws/sns/topic_subscription.py +74 -0
- cartography/models/aws/sqs/__init__.py +0 -0
- cartography/models/aws/sqs/queue.py +89 -0
- cartography/models/azure/__init__.py +0 -0
- cartography/models/azure/aks_cluster.py +54 -0
- cartography/models/azure/aks_nodepool.py +54 -0
- cartography/models/azure/app_service.py +59 -0
- cartography/models/azure/container_instance.py +57 -0
- cartography/models/azure/cosmosdb/__init__.py +0 -0
- cartography/models/azure/cosmosdb/account.py +77 -0
- cartography/models/azure/cosmosdb/accountfailoverpolicy.py +77 -0
- cartography/models/azure/cosmosdb/cassandrakeyspace.py +82 -0
- cartography/models/azure/cosmosdb/cassandratable.py +81 -0
- cartography/models/azure/cosmosdb/corspolicy.py +74 -0
- cartography/models/azure/cosmosdb/dblocation.py +120 -0
- cartography/models/azure/cosmosdb/mongodbcollection.py +82 -0
- cartography/models/azure/cosmosdb/mongodbdatabase.py +78 -0
- cartography/models/azure/cosmosdb/privateendpointconnection.py +81 -0
- cartography/models/azure/cosmosdb/sqlcontainer.py +88 -0
- cartography/models/azure/cosmosdb/sqldatabase.py +78 -0
- cartography/models/azure/cosmosdb/tableresource.py +76 -0
- cartography/models/azure/cosmosdb/virtualnetworkrule.py +78 -0
- cartography/models/azure/data_factory/__init__.py +0 -0
- cartography/models/azure/data_factory/data_factory.py +51 -0
- cartography/models/azure/data_factory/data_factory_dataset.py +94 -0
- cartography/models/azure/data_factory/data_factory_linked_service.py +78 -0
- cartography/models/azure/data_factory/data_factory_pipeline.py +93 -0
- cartography/models/azure/data_lake_filesystem.py +51 -0
- cartography/models/azure/event_grid_topic.py +57 -0
- cartography/models/azure/function_app.py +59 -0
- cartography/models/azure/load_balancer/__init__.py +0 -0
- cartography/models/azure/load_balancer/load_balancer.py +49 -0
- cartography/models/azure/load_balancer/load_balancer_backend_pool.py +73 -0
- cartography/models/azure/load_balancer/load_balancer_frontend_ip.py +75 -0
- cartography/models/azure/load_balancer/load_balancer_inbound_nat_rule.py +78 -0
- cartography/models/azure/load_balancer/load_balancer_rule.py +108 -0
- cartography/models/azure/logic_apps.py +56 -0
- cartography/models/azure/monitor.py +54 -0
- cartography/models/azure/network_interface.py +112 -0
- cartography/models/azure/network_security_group.py +50 -0
- cartography/models/azure/permission_relationships.py +60 -0
- cartography/models/azure/principal.py +41 -0
- cartography/models/azure/public_ip_address.py +50 -0
- cartography/models/azure/rbac.py +268 -0
- cartography/models/azure/resource_groups.py +52 -0
- cartography/models/azure/security_center.py +50 -0
- cartography/models/azure/sql/__init__.py +0 -0
- cartography/models/azure/sql/databasethreatdetectionpolicy.py +85 -0
- cartography/models/azure/sql/elasticpool.py +77 -0
- cartography/models/azure/sql/failovergroup.py +73 -0
- cartography/models/azure/sql/recoverabledatabase.py +75 -0
- cartography/models/azure/sql/replicationlink.py +81 -0
- cartography/models/azure/sql/restorabledroppeddatabase.py +82 -0
- cartography/models/azure/sql/restorepoint.py +74 -0
- cartography/models/azure/sql/serveradadministrator.py +74 -0
- cartography/models/azure/sql/serverdnsalias.py +71 -0
- cartography/models/azure/sql/sqldatabase.py +85 -0
- cartography/models/azure/sql/sqlserver.py +50 -0
- cartography/models/azure/sql/transparentdataencryption.py +76 -0
- cartography/models/azure/storage/__init__.py +0 -0
- cartography/models/azure/storage/account.py +59 -0
- cartography/models/azure/storage/blobcontainer.py +85 -0
- cartography/models/azure/storage/blobservice.py +71 -0
- cartography/models/azure/storage/fileservice.py +71 -0
- cartography/models/azure/storage/fileshare.py +82 -0
- cartography/models/azure/storage/queue.py +71 -0
- cartography/models/azure/storage/queueservice.py +73 -0
- cartography/models/azure/storage/table.py +72 -0
- cartography/models/azure/storage/tableservice.py +73 -0
- cartography/models/azure/subnet.py +101 -0
- cartography/models/azure/subscription.py +47 -0
- cartography/models/azure/tags/__init__.py +0 -0
- cartography/models/azure/tags/storage_tag.py +40 -0
- cartography/models/azure/tags/tag.py +37 -0
- cartography/models/azure/tenant.py +17 -0
- cartography/models/azure/virtual_network.py +49 -0
- cartography/models/azure/vm/__init__.py +0 -0
- cartography/models/azure/vm/datadisk.py +80 -0
- cartography/models/azure/vm/disk.py +55 -0
- cartography/models/azure/vm/snapshot.py +56 -0
- cartography/models/azure/vm/virtualmachine.py +59 -0
- cartography/models/bigfix/bigfix_computer.py +1 -1
- cartography/models/cloudflare/member.py +4 -0
- cartography/models/core/common.py +1 -0
- cartography/models/core/nodes.py +15 -2
- cartography/models/core/relationships.py +44 -0
- cartography/models/crowdstrike/hosts.py +1 -1
- cartography/models/digitalocean/droplet.py +2 -0
- cartography/models/duo/endpoint.py +1 -1
- cartography/models/duo/phone.py +2 -2
- cartography/models/duo/user.py +4 -0
- cartography/models/entra/app_role_assignment.py +115 -0
- cartography/models/entra/application.py +49 -0
- cartography/models/entra/entra_user_to_aws_sso.py +41 -0
- cartography/models/entra/group.py +117 -0
- cartography/models/entra/service_principal.py +104 -0
- cartography/models/entra/user.py +42 -51
- cartography/models/gcp/__init__.py +0 -0
- cartography/models/gcp/bigtable/__init__.py +0 -0
- cartography/models/gcp/bigtable/app_profile.py +94 -0
- cartography/models/gcp/bigtable/backup.py +91 -0
- cartography/models/gcp/bigtable/cluster.py +73 -0
- cartography/models/gcp/bigtable/instance.py +52 -0
- cartography/models/gcp/bigtable/table.py +69 -0
- cartography/models/gcp/compute/__init__.py +0 -0
- cartography/models/gcp/compute/subnet.py +74 -0
- cartography/models/gcp/compute/vpc.py +50 -0
- cartography/models/gcp/crm/__init__.py +0 -0
- cartography/models/gcp/crm/folders.py +98 -0
- cartography/models/gcp/crm/organizations.py +21 -0
- cartography/models/gcp/crm/projects.py +100 -0
- cartography/models/gcp/dns.py +109 -0
- cartography/models/gcp/gke.py +69 -0
- cartography/models/gcp/iam.py +3 -0
- cartography/models/gcp/permission_relationships.py +61 -0
- cartography/models/gcp/policy_bindings.py +93 -0
- cartography/models/gcp/storage/__init__.py +0 -0
- cartography/models/gcp/storage/bucket.py +119 -0
- cartography/models/github/commits.py +63 -0
- cartography/models/github/dependencies.py +73 -0
- cartography/models/github/manifests.py +49 -0
- cartography/models/github/users.py +10 -0
- cartography/models/googleworkspace/__init__.py +0 -0
- cartography/models/googleworkspace/device.py +132 -0
- cartography/models/googleworkspace/group.py +382 -0
- cartography/models/googleworkspace/oauth_app.py +124 -0
- cartography/models/googleworkspace/tenant.py +30 -0
- cartography/models/googleworkspace/user.py +113 -0
- cartography/models/gsuite/__init__.py +0 -0
- cartography/models/gsuite/group.py +218 -0
- cartography/models/gsuite/tenant.py +29 -0
- cartography/models/gsuite/user.py +107 -0
- cartography/models/kandji/device.py +1 -2
- cartography/models/keycloak/__init__.py +0 -0
- cartography/models/keycloak/authenticationexecution.py +160 -0
- cartography/models/keycloak/authenticationflow.py +54 -0
- cartography/models/keycloak/client.py +179 -0
- cartography/models/keycloak/group.py +101 -0
- cartography/models/keycloak/identityprovider.py +89 -0
- cartography/models/keycloak/organization.py +116 -0
- cartography/models/keycloak/organizationdomain.py +73 -0
- cartography/models/keycloak/realm.py +173 -0
- cartography/models/keycloak/role.py +126 -0
- cartography/models/keycloak/scope.py +73 -0
- cartography/models/keycloak/user.py +55 -0
- cartography/models/kubernetes/__init__.py +0 -0
- cartography/models/kubernetes/clusterrolebindings.py +138 -0
- cartography/models/kubernetes/clusterroles.py +52 -0
- cartography/models/kubernetes/clusters.py +26 -0
- cartography/models/kubernetes/containers.py +133 -0
- cartography/models/kubernetes/groups.py +107 -0
- cartography/models/kubernetes/namespaces.py +51 -0
- cartography/models/kubernetes/oidc.py +51 -0
- cartography/models/kubernetes/pods.py +80 -0
- cartography/models/kubernetes/rolebindings.py +159 -0
- cartography/models/kubernetes/roles.py +76 -0
- cartography/models/kubernetes/secrets.py +79 -0
- cartography/models/kubernetes/serviceaccounts.py +77 -0
- cartography/models/kubernetes/services.py +108 -0
- cartography/models/kubernetes/users.py +105 -0
- cartography/models/lastpass/user.py +4 -0
- cartography/models/ontology/__init__.py +0 -0
- cartography/models/ontology/device.py +137 -0
- cartography/models/ontology/mapping/__init__.py +76 -0
- cartography/models/ontology/mapping/data/__init__.py +0 -0
- cartography/models/ontology/mapping/data/apikeys.py +93 -0
- cartography/models/ontology/mapping/data/computeinstance.py +95 -0
- cartography/models/ontology/mapping/data/containers.py +88 -0
- cartography/models/ontology/mapping/data/databases.py +182 -0
- cartography/models/ontology/mapping/data/devices.py +194 -0
- cartography/models/ontology/mapping/data/thirdpartyapps.py +140 -0
- cartography/models/ontology/mapping/data/useraccounts.py +416 -0
- cartography/models/ontology/mapping/data/users.py +63 -0
- cartography/models/ontology/mapping/specs.py +85 -0
- cartography/models/ontology/user.py +51 -0
- cartography/models/openai/adminapikey.py +4 -0
- cartography/models/openai/apikey.py +4 -0
- cartography/models/openai/user.py +4 -0
- cartography/models/scaleway/__init__.py +0 -0
- cartography/models/scaleway/iam/__init__.py +0 -0
- cartography/models/scaleway/iam/apikey.py +100 -0
- cartography/models/scaleway/iam/application.py +52 -0
- cartography/models/scaleway/iam/group.py +95 -0
- cartography/models/scaleway/iam/user.py +64 -0
- cartography/models/scaleway/instance/__init__.py +0 -0
- cartography/models/scaleway/instance/flexibleip.py +52 -0
- cartography/models/scaleway/instance/instance.py +120 -0
- cartography/models/scaleway/organization.py +19 -0
- cartography/models/scaleway/project.py +48 -0
- cartography/models/scaleway/storage/__init__.py +0 -0
- cartography/models/scaleway/storage/snapshot.py +78 -0
- cartography/models/scaleway/storage/volume.py +51 -0
- cartography/models/sentinelone/__init__.py +1 -0
- cartography/models/sentinelone/account.py +40 -0
- cartography/models/sentinelone/agent.py +50 -0
- cartography/models/sentinelone/application.py +44 -0
- cartography/models/sentinelone/application_version.py +96 -0
- cartography/models/sentinelone/cve.py +73 -0
- cartography/models/slack/__init__.py +0 -0
- cartography/models/slack/channels.py +92 -0
- cartography/models/slack/group.py +129 -0
- cartography/models/slack/team.py +22 -0
- cartography/models/slack/user.py +62 -0
- cartography/models/snipeit/asset.py +2 -0
- cartography/models/snipeit/user.py +4 -0
- cartography/models/spacelift/__init__.py +0 -0
- cartography/models/spacelift/cloudtrailevent.py +120 -0
- cartography/models/spacelift/run.py +162 -0
- cartography/models/spacelift/space.py +131 -0
- cartography/models/spacelift/spaceliftaccount.py +31 -0
- cartography/models/spacelift/spaceliftgitcommit.py +157 -0
- cartography/models/spacelift/stack.py +96 -0
- cartography/models/spacelift/user.py +63 -0
- cartography/models/spacelift/worker.py +97 -0
- cartography/models/spacelift/workerpool.py +90 -0
- cartography/models/tailscale/device.py +2 -1
- cartography/models/tailscale/user.py +6 -1
- cartography/models/trivy/__init__.py +0 -0
- cartography/models/trivy/findings.py +66 -0
- cartography/models/trivy/fix.py +66 -0
- cartography/models/trivy/package.py +71 -0
- cartography/rules/README.md +1 -0
- cartography/rules/__init__.py +0 -0
- cartography/rules/cli.py +261 -0
- cartography/rules/data/__init__.py +0 -0
- cartography/rules/data/rules/__init__.py +46 -0
- cartography/rules/data/rules/cloud_security_product_deactivated.py +49 -0
- cartography/rules/data/rules/compute_instance_exposed.py +51 -0
- cartography/rules/data/rules/database_instance_exposed.py +53 -0
- cartography/rules/data/rules/delegation_boundary_modifiable.py +90 -0
- cartography/rules/data/rules/identity_administration_privileges.py +100 -0
- cartography/rules/data/rules/inactive_user_active_accounts.py +48 -0
- cartography/rules/data/rules/malicious_npm_dependencies_shai_hulud.py +2222 -0
- cartography/rules/data/rules/mfa_missing.py +46 -0
- cartography/rules/data/rules/object_storage_public.py +100 -0
- cartography/rules/data/rules/policy_administration_privileges.py +104 -0
- cartography/rules/data/rules/unmanaged_accounts.py +43 -0
- cartography/rules/data/rules/workload_identity_admin_capabilities.py +193 -0
- cartography/rules/formatters.py +108 -0
- cartography/rules/runners.py +216 -0
- cartography/rules/spec/__init__.py +0 -0
- cartography/rules/spec/model.py +267 -0
- cartography/rules/spec/result.py +38 -0
- cartography/sync.py +25 -5
- cartography/util.py +101 -31
- {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/METADATA +61 -22
- cartography-0.123.0.dist-info/RECORD +856 -0
- {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/entry_points.txt +1 -0
- cartography/data/jobs/cleanup/aws_dns_cleanup.json +0 -65
- cartography/data/jobs/cleanup/aws_import_account_access_key_cleanup.json +0 -17
- cartography/data/jobs/cleanup/aws_import_ec2_security_groupinfo_cleanup.json +0 -24
- cartography/data/jobs/cleanup/aws_import_groups_cleanup.json +0 -13
- cartography/data/jobs/cleanup/aws_import_identity_center_cleanup.json +0 -16
- cartography/data/jobs/cleanup/aws_import_lambda_cleanup.json +0 -50
- cartography/data/jobs/cleanup/aws_import_principals_cleanup.json +0 -30
- cartography/data/jobs/cleanup/aws_import_rds_clusters_cleanup.json +0 -23
- cartography/data/jobs/cleanup/aws_import_rds_instances_cleanup.json +0 -47
- cartography/data/jobs/cleanup/aws_import_rds_snapshots_cleanup.json +0 -23
- cartography/data/jobs/cleanup/aws_import_roles_cleanup.json +0 -13
- cartography/data/jobs/cleanup/aws_import_secrets_cleanup.json +0 -8
- cartography/data/jobs/cleanup/aws_import_snapshots_cleanup.json +0 -30
- cartography/data/jobs/cleanup/aws_import_users_cleanup.json +0 -8
- cartography/data/jobs/cleanup/aws_import_vpc_cleanup.json +0 -23
- cartography/data/jobs/cleanup/aws_import_vpc_peering_cleanup.json +0 -45
- cartography/data/jobs/cleanup/aws_kms_details.json +0 -10
- cartography/data/jobs/cleanup/azure_cosmosdb_cassandra_keyspace_cleanup.json +0 -25
- cartography/data/jobs/cleanup/azure_cosmosdb_cors_details.json +0 -15
- cartography/data/jobs/cleanup/azure_cosmosdb_mongodb_database_cleanup.json +0 -25
- cartography/data/jobs/cleanup/azure_cosmosdb_sql_database_cleanup.json +0 -25
- cartography/data/jobs/cleanup/azure_cosmosdb_table_resources_cleanup.json +0 -15
- cartography/data/jobs/cleanup/azure_database_account_cleanup.json +0 -85
- cartography/data/jobs/cleanup/azure_import_disks_cleanup.json +0 -15
- cartography/data/jobs/cleanup/azure_import_snapshots_cleanup.json +0 -15
- cartography/data/jobs/cleanup/azure_import_virtual_machines_cleanup.json +0 -25
- cartography/data/jobs/cleanup/azure_sql_server_cleanup.json +0 -125
- cartography/data/jobs/cleanup/azure_storage_account_cleanup.json +0 -95
- cartography/data/jobs/cleanup/azure_subscriptions_cleanup.json +0 -14
- cartography/data/jobs/cleanup/azure_tenant_cleanup.json +0 -9
- cartography/data/jobs/cleanup/gcp_compute_vpc_subnet_cleanup.json +0 -35
- cartography/data/jobs/cleanup/gcp_crm_folder_cleanup.json +0 -23
- cartography/data/jobs/cleanup/gcp_crm_organization_cleanup.json +0 -17
- cartography/data/jobs/cleanup/gcp_crm_project_cleanup.json +0 -23
- cartography/data/jobs/cleanup/gcp_dns_cleanup.json +0 -29
- cartography/data/jobs/cleanup/gcp_gke_cluster_cleanup.json +0 -17
- cartography/data/jobs/cleanup/gcp_storage_bucket_cleanup.json +0 -29
- cartography/data/jobs/cleanup/gsuite_ingest_groups_cleanup.json +0 -23
- cartography/data/jobs/cleanup/gsuite_ingest_users_cleanup.json +0 -11
- cartography/data/jobs/cleanup/kubernetes_import_cleanup.json +0 -70
- cartography/intel/gcp/crm.py +0 -355
- cartography/intel/gsuite/api.py +0 -342
- cartography-0.104.0rc2.dist-info/RECORD +0 -455
- /cartography/data/jobs/{analysis → scoped_analysis}/aws_s3acl_analysis.json +0 -0
- /cartography/models/aws/{apigateway.py → apigateway/apigateway.py} +0 -0
- /cartography/models/aws/{apigatewaycertificate.py → apigateway/apigatewaycertificate.py} +0 -0
- /cartography/models/aws/{apigatewayresource.py → apigateway/apigatewayresource.py} +0 -0
- /cartography/models/aws/{apigatewaystage.py → apigateway/apigatewaystage.py} +0 -0
- {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/WHEEL +0 -0
- {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import boto3
|
|
7
|
+
from neo4j import Session
|
|
8
|
+
|
|
9
|
+
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.graph.job import GraphJob
|
|
11
|
+
from cartography.models.trivy.findings import TrivyImageFindingSchema
|
|
12
|
+
from cartography.models.trivy.fix import TrivyFixSchema
|
|
13
|
+
from cartography.models.trivy.package import TrivyPackageSchema
|
|
14
|
+
from cartography.stats import get_stats_client
|
|
15
|
+
from cartography.util import timeit
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
stat_handler = get_stats_client(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _validate_packages(package_list: list[dict]) -> list[dict]:
|
|
22
|
+
"""
|
|
23
|
+
Validates that each package has the required fields.
|
|
24
|
+
Returns only packages that have both InstalledVersion and PkgName.
|
|
25
|
+
"""
|
|
26
|
+
validated_packages: list[dict] = []
|
|
27
|
+
for pkg in package_list:
|
|
28
|
+
if (
|
|
29
|
+
"InstalledVersion" in pkg
|
|
30
|
+
and pkg["InstalledVersion"]
|
|
31
|
+
and "PkgName" in pkg
|
|
32
|
+
and pkg["PkgName"]
|
|
33
|
+
):
|
|
34
|
+
validated_packages.append(pkg)
|
|
35
|
+
else:
|
|
36
|
+
logger.warning(
|
|
37
|
+
"Package object does not have required fields `InstalledVersion` or `PkgName` - skipping."
|
|
38
|
+
)
|
|
39
|
+
return validated_packages
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def transform_scan_results(
|
|
43
|
+
results: list[dict], image_digest: str
|
|
44
|
+
) -> tuple[list[dict], list[dict], list[dict]]:
|
|
45
|
+
"""
|
|
46
|
+
Transform raw Trivy scan results into a format suitable for loading into Neo4j.
|
|
47
|
+
Returns a tuple of (findings_list, packages_list, fixes_list).
|
|
48
|
+
"""
|
|
49
|
+
findings_list = []
|
|
50
|
+
packages_list = []
|
|
51
|
+
fixes_list = []
|
|
52
|
+
|
|
53
|
+
for scan_class in results:
|
|
54
|
+
# Sometimes a scan class will have no vulns and Trivy will leave the key undefined instead of showing [].
|
|
55
|
+
if "Vulnerabilities" in scan_class and scan_class["Vulnerabilities"]:
|
|
56
|
+
for result in scan_class["Vulnerabilities"]:
|
|
57
|
+
# Transform finding data
|
|
58
|
+
finding = {
|
|
59
|
+
"id": f'TIF|{result["VulnerabilityID"]}',
|
|
60
|
+
"VulnerabilityID": result["VulnerabilityID"],
|
|
61
|
+
"cve_id": result["VulnerabilityID"],
|
|
62
|
+
"Description": result.get("Description"),
|
|
63
|
+
"LastModifiedDate": result.get("LastModifiedDate"),
|
|
64
|
+
"PrimaryURL": result.get("PrimaryURL"),
|
|
65
|
+
"PublishedDate": result.get("PublishedDate"),
|
|
66
|
+
"Severity": result["Severity"],
|
|
67
|
+
"SeveritySource": result.get("SeveritySource"),
|
|
68
|
+
"Title": result.get("Title"),
|
|
69
|
+
"nvd_v2_score": None,
|
|
70
|
+
"nvd_v2_vector": None,
|
|
71
|
+
"nvd_v3_score": None,
|
|
72
|
+
"nvd_v3_vector": None,
|
|
73
|
+
"redhat_v3_score": None,
|
|
74
|
+
"redhat_v3_vector": None,
|
|
75
|
+
"ubuntu_v3_score": None,
|
|
76
|
+
"ubuntu_v3_vector": None,
|
|
77
|
+
"Class": scan_class["Class"],
|
|
78
|
+
"Type": scan_class["Type"],
|
|
79
|
+
"ImageDigest": image_digest, # For AFFECTS relationship
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# Add CVSS scores if available
|
|
83
|
+
if "CVSS" in result:
|
|
84
|
+
if "nvd" in result["CVSS"]:
|
|
85
|
+
nvd = result["CVSS"]["nvd"]
|
|
86
|
+
finding["nvd_v2_score"] = nvd.get("V2Score")
|
|
87
|
+
finding["nvd_v2_vector"] = nvd.get("V2Vector")
|
|
88
|
+
finding["nvd_v3_score"] = nvd.get("V3Score")
|
|
89
|
+
finding["nvd_v3_vector"] = nvd.get("V3Vector")
|
|
90
|
+
if "redhat" in result["CVSS"]:
|
|
91
|
+
redhat = result["CVSS"]["redhat"]
|
|
92
|
+
finding["redhat_v3_score"] = redhat.get("V3Score")
|
|
93
|
+
finding["redhat_v3_vector"] = redhat.get("V3Vector")
|
|
94
|
+
if "ubuntu" in result["CVSS"]:
|
|
95
|
+
ubuntu = result["CVSS"]["ubuntu"]
|
|
96
|
+
finding["ubuntu_v3_score"] = ubuntu.get("V3Score")
|
|
97
|
+
finding["ubuntu_v3_vector"] = ubuntu.get("V3Vector")
|
|
98
|
+
|
|
99
|
+
findings_list.append(finding)
|
|
100
|
+
|
|
101
|
+
# Transform package data
|
|
102
|
+
package_id = f"{result['InstalledVersion']}|{result['PkgName']}"
|
|
103
|
+
packages_list.append(
|
|
104
|
+
{
|
|
105
|
+
"id": package_id,
|
|
106
|
+
"InstalledVersion": result["InstalledVersion"],
|
|
107
|
+
"PkgName": result["PkgName"],
|
|
108
|
+
"Class": scan_class["Class"],
|
|
109
|
+
"Type": scan_class["Type"],
|
|
110
|
+
"ImageDigest": image_digest, # For DEPLOYED relationship
|
|
111
|
+
"FindingId": finding["id"], # For AFFECTS relationship
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# Transform fix data if available
|
|
116
|
+
if result.get("FixedVersion") is not None:
|
|
117
|
+
fixes_list.append(
|
|
118
|
+
{
|
|
119
|
+
"id": f"{result['FixedVersion']}|{result['PkgName']}",
|
|
120
|
+
"FixedVersion": result["FixedVersion"],
|
|
121
|
+
"PackageId": package_id,
|
|
122
|
+
"FindingId": finding["id"],
|
|
123
|
+
}
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Validate packages before returning
|
|
127
|
+
packages_list = _validate_packages(packages_list)
|
|
128
|
+
return findings_list, packages_list, fixes_list
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _parse_trivy_data(
|
|
132
|
+
trivy_data: dict, source: str
|
|
133
|
+
) -> tuple[str | None, list[dict], str]:
|
|
134
|
+
"""
|
|
135
|
+
Parse Trivy scan data and extract common fields.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
trivy_data: Raw JSON Trivy data
|
|
139
|
+
source: Source identifier for error messages (file path or S3 URI)
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Tuple of (artifact_name, results, image_digest)
|
|
143
|
+
"""
|
|
144
|
+
# Extract artifact name if present (only for file-based)
|
|
145
|
+
artifact_name = trivy_data.get("ArtifactName")
|
|
146
|
+
|
|
147
|
+
if "Results" not in trivy_data:
|
|
148
|
+
logger.error(
|
|
149
|
+
f"Scan data did not contain a `Results` key for {source}. This indicates a malformed scan result."
|
|
150
|
+
)
|
|
151
|
+
raise ValueError(f"Missing 'Results' key in scan data for {source}")
|
|
152
|
+
|
|
153
|
+
results = trivy_data["Results"]
|
|
154
|
+
if not results:
|
|
155
|
+
stat_handler.incr("image_scan_no_results_count")
|
|
156
|
+
logger.info(f"No vulnerabilities found for {source}")
|
|
157
|
+
|
|
158
|
+
if "Metadata" not in trivy_data or not trivy_data["Metadata"]:
|
|
159
|
+
raise ValueError(f"Missing 'Metadata' in scan data for {source}")
|
|
160
|
+
|
|
161
|
+
repo_digests = trivy_data["Metadata"].get("RepoDigests", [])
|
|
162
|
+
if not repo_digests:
|
|
163
|
+
raise ValueError(f"Missing 'RepoDigests' in scan metadata for {source}")
|
|
164
|
+
|
|
165
|
+
repo_digest = repo_digests[0]
|
|
166
|
+
if "@" not in repo_digest:
|
|
167
|
+
raise ValueError(f"Invalid repo digest format in {source}: {repo_digest}")
|
|
168
|
+
|
|
169
|
+
image_digest = repo_digest.split("@")[1]
|
|
170
|
+
if not image_digest:
|
|
171
|
+
raise ValueError(f"Empty image digest for {source}")
|
|
172
|
+
|
|
173
|
+
return artifact_name, results, image_digest
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@timeit
|
|
177
|
+
def sync_single_image(
|
|
178
|
+
neo4j_session: Session,
|
|
179
|
+
trivy_data: dict,
|
|
180
|
+
source: str,
|
|
181
|
+
update_tag: int,
|
|
182
|
+
) -> None:
|
|
183
|
+
"""
|
|
184
|
+
Sync a single image's Trivy scan results to Neo4j.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
neo4j_session: Neo4j session for database operations
|
|
188
|
+
trivy_data: Raw Trivy JSON data
|
|
189
|
+
source: Source identifier for logging (file path or image URI)
|
|
190
|
+
update_tag: Update tag for tracking
|
|
191
|
+
"""
|
|
192
|
+
try:
|
|
193
|
+
_, results, image_digest = _parse_trivy_data(trivy_data, source)
|
|
194
|
+
|
|
195
|
+
# Transform all data in one pass
|
|
196
|
+
findings_list, packages_list, fixes_list = transform_scan_results(
|
|
197
|
+
results,
|
|
198
|
+
image_digest,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
num_findings = len(findings_list)
|
|
202
|
+
stat_handler.incr("image_scan_cve_count", num_findings)
|
|
203
|
+
|
|
204
|
+
# Load the transformed data
|
|
205
|
+
load_scan_vulns(neo4j_session, findings_list, update_tag=update_tag)
|
|
206
|
+
load_scan_packages(neo4j_session, packages_list, update_tag=update_tag)
|
|
207
|
+
load_scan_fixes(neo4j_session, fixes_list, update_tag=update_tag)
|
|
208
|
+
stat_handler.incr("images_processed_count")
|
|
209
|
+
|
|
210
|
+
except Exception as e:
|
|
211
|
+
logger.error(f"Failed to process scan results for {source}: {e}")
|
|
212
|
+
raise
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@timeit
|
|
216
|
+
def get_json_files_in_s3(
|
|
217
|
+
s3_bucket: str, s3_prefix: str, boto3_session: boto3.Session
|
|
218
|
+
) -> set[str]:
|
|
219
|
+
"""
|
|
220
|
+
List S3 objects in the S3 prefix.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
s3_bucket: S3 bucket name containing scan results
|
|
224
|
+
s3_prefix: S3 prefix path containing scan results
|
|
225
|
+
boto3_session: boto3 session for dependency injection
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
Set of S3 object keys for JSON files in the S3 prefix
|
|
229
|
+
"""
|
|
230
|
+
s3_client = boto3_session.client("s3")
|
|
231
|
+
|
|
232
|
+
try:
|
|
233
|
+
# List objects in the S3 prefix
|
|
234
|
+
paginator = s3_client.get_paginator("list_objects_v2")
|
|
235
|
+
page_iterator = paginator.paginate(Bucket=s3_bucket, Prefix=s3_prefix)
|
|
236
|
+
results = set()
|
|
237
|
+
|
|
238
|
+
for page in page_iterator:
|
|
239
|
+
if "Contents" not in page:
|
|
240
|
+
continue
|
|
241
|
+
|
|
242
|
+
for obj in page["Contents"]:
|
|
243
|
+
object_key = obj["Key"]
|
|
244
|
+
|
|
245
|
+
# Skip non-JSON files
|
|
246
|
+
if not object_key.endswith(".json"):
|
|
247
|
+
continue
|
|
248
|
+
|
|
249
|
+
# Skip files that don't start with our prefix
|
|
250
|
+
if not object_key.startswith(s3_prefix):
|
|
251
|
+
continue
|
|
252
|
+
|
|
253
|
+
results.add(object_key)
|
|
254
|
+
|
|
255
|
+
except Exception as e:
|
|
256
|
+
logger.error(
|
|
257
|
+
f"Error listing S3 objects in bucket {s3_bucket} with prefix {s3_prefix}: {e}"
|
|
258
|
+
)
|
|
259
|
+
raise
|
|
260
|
+
|
|
261
|
+
logger.info(f"Found {len(results)} json files in s3://{s3_bucket}/{s3_prefix}")
|
|
262
|
+
return results
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
@timeit
|
|
266
|
+
def get_json_files_in_dir(results_dir: str) -> set[str]:
|
|
267
|
+
"""Return set of JSON file paths under a directory."""
|
|
268
|
+
results = set()
|
|
269
|
+
for root, _dirs, files in os.walk(results_dir):
|
|
270
|
+
for filename in files:
|
|
271
|
+
if filename.endswith(".json"):
|
|
272
|
+
results.add(os.path.join(root, filename))
|
|
273
|
+
logger.info(f"Found {len(results)} json files in {results_dir}")
|
|
274
|
+
return results
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
@timeit
|
|
278
|
+
def cleanup(neo4j_session: Session, common_job_parameters: dict[str, Any]) -> None:
|
|
279
|
+
"""
|
|
280
|
+
Run cleanup jobs for Trivy nodes.
|
|
281
|
+
"""
|
|
282
|
+
logger.info("Running Trivy cleanup")
|
|
283
|
+
GraphJob.from_node_schema(TrivyImageFindingSchema(), common_job_parameters).run(
|
|
284
|
+
neo4j_session
|
|
285
|
+
)
|
|
286
|
+
GraphJob.from_node_schema(TrivyPackageSchema(), common_job_parameters).run(
|
|
287
|
+
neo4j_session
|
|
288
|
+
)
|
|
289
|
+
GraphJob.from_node_schema(TrivyFixSchema(), common_job_parameters).run(
|
|
290
|
+
neo4j_session
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
@timeit
|
|
295
|
+
def load_scan_vulns(
|
|
296
|
+
neo4j_session: Session,
|
|
297
|
+
findings_list: list[dict[str, Any]],
|
|
298
|
+
update_tag: int,
|
|
299
|
+
) -> None:
|
|
300
|
+
"""
|
|
301
|
+
Load TrivyImageFinding nodes into Neo4j.
|
|
302
|
+
"""
|
|
303
|
+
load(
|
|
304
|
+
neo4j_session,
|
|
305
|
+
TrivyImageFindingSchema(),
|
|
306
|
+
findings_list,
|
|
307
|
+
lastupdated=update_tag,
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
@timeit
|
|
312
|
+
def load_scan_packages(
|
|
313
|
+
neo4j_session: Session,
|
|
314
|
+
packages_list: list[dict[str, Any]],
|
|
315
|
+
update_tag: int,
|
|
316
|
+
) -> None:
|
|
317
|
+
"""
|
|
318
|
+
Load TrivyPackage nodes into Neo4j.
|
|
319
|
+
"""
|
|
320
|
+
load(
|
|
321
|
+
neo4j_session,
|
|
322
|
+
TrivyPackageSchema(),
|
|
323
|
+
packages_list,
|
|
324
|
+
lastupdated=update_tag,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
@timeit
|
|
329
|
+
def load_scan_fixes(
|
|
330
|
+
neo4j_session: Session,
|
|
331
|
+
fixes_list: list[dict[str, Any]],
|
|
332
|
+
update_tag: int,
|
|
333
|
+
) -> None:
|
|
334
|
+
"""
|
|
335
|
+
Load TrivyFix nodes into Neo4j.
|
|
336
|
+
"""
|
|
337
|
+
load(
|
|
338
|
+
neo4j_session,
|
|
339
|
+
TrivyFixSchema(),
|
|
340
|
+
fixes_list,
|
|
341
|
+
lastupdated=update_tag,
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
@timeit
|
|
346
|
+
def sync_single_image_from_s3(
|
|
347
|
+
neo4j_session: Session,
|
|
348
|
+
image_uri: str,
|
|
349
|
+
update_tag: int,
|
|
350
|
+
s3_bucket: str,
|
|
351
|
+
s3_object_key: str,
|
|
352
|
+
boto3_session: boto3.Session,
|
|
353
|
+
) -> None:
|
|
354
|
+
"""
|
|
355
|
+
Read Trivy scan results from S3 and sync to Neo4j.
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
neo4j_session: Neo4j session for database operations
|
|
359
|
+
image_uri: ECR image URI
|
|
360
|
+
update_tag: Update tag for tracking
|
|
361
|
+
s3_bucket: S3 bucket containing scan results
|
|
362
|
+
s3_object_key: S3 object key for this image's scan results
|
|
363
|
+
boto3_session: boto3 session for S3 operations
|
|
364
|
+
"""
|
|
365
|
+
s3_client = boto3_session.client("s3")
|
|
366
|
+
|
|
367
|
+
logger.debug(f"Reading scan results from S3: s3://{s3_bucket}/{s3_object_key}")
|
|
368
|
+
response = s3_client.get_object(Bucket=s3_bucket, Key=s3_object_key)
|
|
369
|
+
scan_data_json = response["Body"].read().decode("utf-8")
|
|
370
|
+
|
|
371
|
+
trivy_data = json.loads(scan_data_json)
|
|
372
|
+
sync_single_image(neo4j_session, trivy_data, image_uri, update_tag)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
@timeit
|
|
376
|
+
def sync_single_image_from_file(
|
|
377
|
+
neo4j_session: Session,
|
|
378
|
+
file_path: str,
|
|
379
|
+
update_tag: int,
|
|
380
|
+
) -> None:
|
|
381
|
+
"""Read a Trivy JSON file from disk and sync to Neo4j."""
|
|
382
|
+
logger.debug(f"Reading scan results from file: {file_path}")
|
|
383
|
+
with open(file_path, encoding="utf-8") as f:
|
|
384
|
+
trivy_data = json.load(f)
|
|
385
|
+
|
|
386
|
+
sync_single_image(neo4j_session, trivy_data, file_path, update_tag)
|
|
File without changes
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from cartography.models.core.common import PropertyRef
|
|
4
|
+
from cartography.models.core.nodes import CartographyNodeProperties
|
|
5
|
+
from cartography.models.core.nodes import CartographyNodeSchema
|
|
6
|
+
from cartography.models.core.relationships import CartographyRelProperties
|
|
7
|
+
from cartography.models.core.relationships import CartographyRelSchema
|
|
8
|
+
from cartography.models.core.relationships import LinkDirection
|
|
9
|
+
from cartography.models.core.relationships import make_target_node_matcher
|
|
10
|
+
from cartography.models.core.relationships import OtherRelationships
|
|
11
|
+
from cartography.models.core.relationships import TargetNodeMatcher
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class AirbyteConnectionNodeProperties(CartographyNodeProperties):
|
|
16
|
+
id: PropertyRef = PropertyRef("connectionId")
|
|
17
|
+
name: PropertyRef = PropertyRef("name")
|
|
18
|
+
namespace_format: PropertyRef = PropertyRef("namespaceFormat")
|
|
19
|
+
prefix: PropertyRef = PropertyRef("prefix")
|
|
20
|
+
status: PropertyRef = PropertyRef("status")
|
|
21
|
+
data_residency: PropertyRef = PropertyRef("dataResidency")
|
|
22
|
+
non_breaking_schema_updates_behavior: PropertyRef = PropertyRef(
|
|
23
|
+
"nonBreakingSchemaUpdatesBehavior"
|
|
24
|
+
)
|
|
25
|
+
namespace_definition: PropertyRef = PropertyRef("namespaceDefinition")
|
|
26
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True)
|
|
30
|
+
class AirbyteConnectionToOrganizationRelProperties(CartographyRelProperties):
|
|
31
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
# (:AirbyteOrganization)-[:RESOURCE]->(:AirbyteConnection)
|
|
36
|
+
class AirbyteConnectionToOrganizationRel(CartographyRelSchema):
|
|
37
|
+
target_node_label: str = "AirbyteOrganization"
|
|
38
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
39
|
+
{"id": PropertyRef("ORG_ID", set_in_kwargs=True)},
|
|
40
|
+
)
|
|
41
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
42
|
+
rel_label: str = "RESOURCE"
|
|
43
|
+
properties: AirbyteConnectionToOrganizationRelProperties = (
|
|
44
|
+
AirbyteConnectionToOrganizationRelProperties()
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True)
|
|
49
|
+
class AirbyteConnectionToWorkspaceRelProperties(CartographyRelProperties):
|
|
50
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass(frozen=True)
|
|
54
|
+
# (:AirbyteWorkspace)-[:CONTAINS]->(:AirbyteConnection)
|
|
55
|
+
class AirbyteConnectionToWorkspaceRel(CartographyRelSchema):
|
|
56
|
+
target_node_label: str = "AirbyteWorkspace"
|
|
57
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
58
|
+
{"id": PropertyRef("workspaceId")},
|
|
59
|
+
)
|
|
60
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
61
|
+
rel_label: str = "CONTAINS"
|
|
62
|
+
properties: AirbyteConnectionToWorkspaceRelProperties = (
|
|
63
|
+
AirbyteConnectionToWorkspaceRelProperties()
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass(frozen=True)
|
|
68
|
+
class AirbyteConnectionToSourceRelProperties(CartographyRelProperties):
|
|
69
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass(frozen=True)
|
|
73
|
+
# (:AirbyteSource)<-[:SYNC_FROM]-(:AirbyteConnection)
|
|
74
|
+
class AirbyteConnectionToSourceRel(CartographyRelSchema):
|
|
75
|
+
target_node_label: str = "AirbyteSource"
|
|
76
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
77
|
+
{"id": PropertyRef("sourceId")},
|
|
78
|
+
)
|
|
79
|
+
direction: LinkDirection = LinkDirection.OUTWARD
|
|
80
|
+
rel_label: str = "SYNC_FROM"
|
|
81
|
+
properties: AirbyteConnectionToSourceRelProperties = (
|
|
82
|
+
AirbyteConnectionToSourceRelProperties()
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass(frozen=True)
|
|
87
|
+
class AirbyteConnectionToDestinationRelProperties(CartographyRelProperties):
|
|
88
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass(frozen=True)
|
|
92
|
+
# (:AirbyteDestination)<-[:SYNC_TO]-(:AirbyteConnection)
|
|
93
|
+
class AirbyteConnectionToDestinationRel(CartographyRelSchema):
|
|
94
|
+
target_node_label: str = "AirbyteDestination"
|
|
95
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
96
|
+
{"id": PropertyRef("destinationId")},
|
|
97
|
+
)
|
|
98
|
+
direction: LinkDirection = LinkDirection.OUTWARD
|
|
99
|
+
rel_label: str = "SYNC_TO"
|
|
100
|
+
properties: AirbyteConnectionToDestinationRelProperties = (
|
|
101
|
+
AirbyteConnectionToDestinationRelProperties()
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass(frozen=True)
|
|
106
|
+
class AirbyteConnectionToTagRelProperties(CartographyRelProperties):
|
|
107
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@dataclass(frozen=True)
|
|
111
|
+
# (:AirbyteTag)<-[:TAGGED]-(:AirbyteConnection)
|
|
112
|
+
class AirbyteConnectionToTagRel(CartographyRelSchema):
|
|
113
|
+
target_node_label: str = "AirbyteTag"
|
|
114
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
115
|
+
{"id": PropertyRef("tags_ids", one_to_many=True)},
|
|
116
|
+
)
|
|
117
|
+
direction: LinkDirection = LinkDirection.OUTWARD
|
|
118
|
+
rel_label: str = "TAGGED"
|
|
119
|
+
properties: AirbyteConnectionToTagRelProperties = (
|
|
120
|
+
AirbyteConnectionToTagRelProperties()
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@dataclass(frozen=True)
|
|
125
|
+
class AirbyteConnectionSchema(CartographyNodeSchema):
|
|
126
|
+
label: str = "AirbyteConnection"
|
|
127
|
+
properties: AirbyteConnectionNodeProperties = AirbyteConnectionNodeProperties()
|
|
128
|
+
sub_resource_relationship: AirbyteConnectionToOrganizationRel = (
|
|
129
|
+
AirbyteConnectionToOrganizationRel()
|
|
130
|
+
)
|
|
131
|
+
other_relationships: OtherRelationships = OtherRelationships(
|
|
132
|
+
[
|
|
133
|
+
AirbyteConnectionToWorkspaceRel(),
|
|
134
|
+
AirbyteConnectionToSourceRel(),
|
|
135
|
+
AirbyteConnectionToDestinationRel(),
|
|
136
|
+
AirbyteConnectionToTagRel(),
|
|
137
|
+
]
|
|
138
|
+
)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from cartography.models.core.common import PropertyRef
|
|
4
|
+
from cartography.models.core.nodes import CartographyNodeProperties
|
|
5
|
+
from cartography.models.core.nodes import CartographyNodeSchema
|
|
6
|
+
from cartography.models.core.relationships import CartographyRelProperties
|
|
7
|
+
from cartography.models.core.relationships import CartographyRelSchema
|
|
8
|
+
from cartography.models.core.relationships import LinkDirection
|
|
9
|
+
from cartography.models.core.relationships import make_target_node_matcher
|
|
10
|
+
from cartography.models.core.relationships import OtherRelationships
|
|
11
|
+
from cartography.models.core.relationships import TargetNodeMatcher
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class AirbyteDestinationNodeProperties(CartographyNodeProperties):
|
|
16
|
+
id: PropertyRef = PropertyRef("destinationId")
|
|
17
|
+
name: PropertyRef = PropertyRef("name")
|
|
18
|
+
type: PropertyRef = PropertyRef("destinationType")
|
|
19
|
+
config_host: PropertyRef = PropertyRef("configuration.host")
|
|
20
|
+
config_port: PropertyRef = PropertyRef("configuration.port")
|
|
21
|
+
config_name: PropertyRef = PropertyRef("configuration.name")
|
|
22
|
+
config_region: PropertyRef = PropertyRef("configuration.region")
|
|
23
|
+
config_endpoint: PropertyRef = PropertyRef("configuration.endpoint")
|
|
24
|
+
config_account: PropertyRef = PropertyRef("configuration.account")
|
|
25
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class AirbyteDestinationToOrganizationRelProperties(CartographyRelProperties):
|
|
30
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
# (:AirbyteOrganization)-[:RESOURCE]->(:AirbyteDestination)
|
|
35
|
+
class AirbyteDestinationToOrganizationRel(CartographyRelSchema):
|
|
36
|
+
target_node_label: str = "AirbyteOrganization"
|
|
37
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
38
|
+
{"id": PropertyRef("ORG_ID", set_in_kwargs=True)},
|
|
39
|
+
)
|
|
40
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
41
|
+
rel_label: str = "RESOURCE"
|
|
42
|
+
properties: AirbyteDestinationToOrganizationRelProperties = (
|
|
43
|
+
AirbyteDestinationToOrganizationRelProperties()
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class AirbyteDestinationToWorkspaceRelProperties(CartographyRelProperties):
|
|
49
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass(frozen=True)
|
|
53
|
+
# (:AirbyteWorkspace)-[:CONTAINS]->(:AirbyteDestination)
|
|
54
|
+
class AirbyteDestinationToWorkspaceRel(CartographyRelSchema):
|
|
55
|
+
target_node_label: str = "AirbyteWorkspace"
|
|
56
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
57
|
+
{"id": PropertyRef("workspaceId")},
|
|
58
|
+
)
|
|
59
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
60
|
+
rel_label: str = "CONTAINS"
|
|
61
|
+
properties: AirbyteDestinationToWorkspaceRelProperties = (
|
|
62
|
+
AirbyteDestinationToWorkspaceRelProperties()
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass(frozen=True)
|
|
67
|
+
class AirbyteDestinationSchema(CartographyNodeSchema):
|
|
68
|
+
label: str = "AirbyteDestination"
|
|
69
|
+
properties: AirbyteDestinationNodeProperties = AirbyteDestinationNodeProperties()
|
|
70
|
+
sub_resource_relationship: AirbyteDestinationToOrganizationRel = (
|
|
71
|
+
AirbyteDestinationToOrganizationRel()
|
|
72
|
+
)
|
|
73
|
+
other_relationships: OtherRelationships = OtherRelationships(
|
|
74
|
+
[AirbyteDestinationToWorkspaceRel()]
|
|
75
|
+
)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from cartography.models.core.common import PropertyRef
|
|
4
|
+
from cartography.models.core.nodes import CartographyNodeProperties
|
|
5
|
+
from cartography.models.core.nodes import CartographyNodeSchema
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class AirbyteOrganizationNodeProperties(CartographyNodeProperties):
|
|
10
|
+
id: PropertyRef = PropertyRef("organizationId")
|
|
11
|
+
name: PropertyRef = PropertyRef("organizationName")
|
|
12
|
+
email: PropertyRef = PropertyRef("email")
|
|
13
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class AirbyteOrganizationSchema(CartographyNodeSchema):
|
|
18
|
+
label: str = "AirbyteOrganization"
|
|
19
|
+
properties: AirbyteOrganizationNodeProperties = AirbyteOrganizationNodeProperties()
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from cartography.models.core.common import PropertyRef
|
|
4
|
+
from cartography.models.core.nodes import CartographyNodeProperties
|
|
5
|
+
from cartography.models.core.nodes import CartographyNodeSchema
|
|
6
|
+
from cartography.models.core.relationships import CartographyRelProperties
|
|
7
|
+
from cartography.models.core.relationships import CartographyRelSchema
|
|
8
|
+
from cartography.models.core.relationships import LinkDirection
|
|
9
|
+
from cartography.models.core.relationships import make_target_node_matcher
|
|
10
|
+
from cartography.models.core.relationships import OtherRelationships
|
|
11
|
+
from cartography.models.core.relationships import TargetNodeMatcher
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class AirbyteSourceNodeProperties(CartographyNodeProperties):
|
|
16
|
+
id: PropertyRef = PropertyRef("sourceId")
|
|
17
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
18
|
+
name: PropertyRef = PropertyRef("name")
|
|
19
|
+
type: PropertyRef = PropertyRef("sourceType")
|
|
20
|
+
config_host: PropertyRef = PropertyRef("configuration.host")
|
|
21
|
+
config_port: PropertyRef = PropertyRef("configuration.port")
|
|
22
|
+
config_name: PropertyRef = PropertyRef("configuration.name")
|
|
23
|
+
config_region: PropertyRef = PropertyRef("configuration.region")
|
|
24
|
+
config_endpoint: PropertyRef = PropertyRef("configuration.endpoint")
|
|
25
|
+
config_account: PropertyRef = PropertyRef("configuration.account")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class AirbyteSourceToOrganizationRelProperties(CartographyRelProperties):
|
|
30
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
# (:AirbyteOrganization)-[:RESOURCE]->(:AirbyteSource)
|
|
35
|
+
class AirbyteSourceToOrganizationRel(CartographyRelSchema):
|
|
36
|
+
target_node_label: str = "AirbyteOrganization"
|
|
37
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
38
|
+
{"id": PropertyRef("ORG_ID", set_in_kwargs=True)},
|
|
39
|
+
)
|
|
40
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
41
|
+
rel_label: str = "RESOURCE"
|
|
42
|
+
properties: AirbyteSourceToOrganizationRelProperties = (
|
|
43
|
+
AirbyteSourceToOrganizationRelProperties()
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class AirbyteSourceToWorkspaceRelProperties(CartographyRelProperties):
|
|
49
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass(frozen=True)
|
|
53
|
+
# (:AirbyteWorkspace)-[:CONTAINS]->(:AirbyteSource)
|
|
54
|
+
class AirbyteSourceToWorkspaceRel(CartographyRelSchema):
|
|
55
|
+
target_node_label: str = "AirbyteWorkspace"
|
|
56
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
57
|
+
{"id": PropertyRef("workspaceId")},
|
|
58
|
+
)
|
|
59
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
60
|
+
rel_label: str = "CONTAINS"
|
|
61
|
+
properties: AirbyteSourceToWorkspaceRelProperties = (
|
|
62
|
+
AirbyteSourceToWorkspaceRelProperties()
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass(frozen=True)
|
|
67
|
+
class AirbyteSourceSchema(CartographyNodeSchema):
|
|
68
|
+
label: str = "AirbyteSource"
|
|
69
|
+
properties: AirbyteSourceNodeProperties = AirbyteSourceNodeProperties()
|
|
70
|
+
sub_resource_relationship: AirbyteSourceToOrganizationRel = (
|
|
71
|
+
AirbyteSourceToOrganizationRel()
|
|
72
|
+
)
|
|
73
|
+
other_relationships: OtherRelationships = OtherRelationships(
|
|
74
|
+
[AirbyteSourceToWorkspaceRel()]
|
|
75
|
+
)
|