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,46 @@
|
|
|
1
|
+
from cartography.rules.spec.model import Fact
|
|
2
|
+
from cartography.rules.spec.model import Finding
|
|
3
|
+
from cartography.rules.spec.model import Maturity
|
|
4
|
+
from cartography.rules.spec.model import Module
|
|
5
|
+
from cartography.rules.spec.model import Rule
|
|
6
|
+
|
|
7
|
+
# Facts
|
|
8
|
+
_missing_mfa_cloudflare = Fact(
|
|
9
|
+
id="missing-mfa-cloudflare",
|
|
10
|
+
name="Cloudflare members with disabled MFA",
|
|
11
|
+
description="Finds Cloudflare member accounts that have Multi-Factor Authentication disabled.",
|
|
12
|
+
module=Module.CLOUDFLARE,
|
|
13
|
+
cypher_query="""
|
|
14
|
+
MATCH (m:CloudflareMember)
|
|
15
|
+
WHERE m.two_factor_authentication_enabled = false
|
|
16
|
+
RETURN m.id AS id, m.email AS email, m.firstname AS firstname, m.lastname AS lastname, m.status AS status
|
|
17
|
+
""",
|
|
18
|
+
cypher_visual_query="""
|
|
19
|
+
MATCH (m:CloudflareMember)
|
|
20
|
+
WHERE m.two_factor_authentication_enabled = false
|
|
21
|
+
RETURN m
|
|
22
|
+
""",
|
|
23
|
+
maturity=Maturity.EXPERIMENTAL,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Rule
|
|
28
|
+
class MFARuleOutput(Finding):
|
|
29
|
+
email: str | None = None
|
|
30
|
+
id: str | None = None
|
|
31
|
+
firstname: str | None = None
|
|
32
|
+
lastname: str | None = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
missing_mfa_rule = Rule(
|
|
36
|
+
id="mfa-missing",
|
|
37
|
+
name="User accounts missing MFA",
|
|
38
|
+
description="Detects user accounts that do not have Multi-Factor Authentication enabled.",
|
|
39
|
+
output_model=MFARuleOutput,
|
|
40
|
+
tags=("identity",),
|
|
41
|
+
facts=(
|
|
42
|
+
# TODO: _missing_mfa_slack,
|
|
43
|
+
_missing_mfa_cloudflare,
|
|
44
|
+
),
|
|
45
|
+
version="0.1.0",
|
|
46
|
+
)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from cartography.rules.spec.model import Fact
|
|
2
|
+
from cartography.rules.spec.model import Finding
|
|
3
|
+
from cartography.rules.spec.model import Maturity
|
|
4
|
+
from cartography.rules.spec.model import Module
|
|
5
|
+
from cartography.rules.spec.model import Rule
|
|
6
|
+
|
|
7
|
+
# AWS Facts
|
|
8
|
+
_aws_s3_public = Fact(
|
|
9
|
+
id="aws_s3_public",
|
|
10
|
+
name="Internet-Accessible S3 Storage Attack Surface",
|
|
11
|
+
description=("AWS S3 buckets accessible from the internet"),
|
|
12
|
+
cypher_query="""
|
|
13
|
+
MATCH (b:S3Bucket)
|
|
14
|
+
WHERE b.anonymous_access = true
|
|
15
|
+
OR (b.anonymous_actions IS NOT NULL AND size(b.anonymous_actions) > 0)
|
|
16
|
+
OR EXISTS {
|
|
17
|
+
MATCH (b)-[:POLICY_STATEMENT]->(stmt:S3PolicyStatement)
|
|
18
|
+
WHERE stmt.effect = 'Allow'
|
|
19
|
+
AND (stmt.principal = '*' OR stmt.principal CONTAINS 'AllUsers')
|
|
20
|
+
}
|
|
21
|
+
RETURN
|
|
22
|
+
b.id as id,
|
|
23
|
+
b.name AS name,
|
|
24
|
+
b.region AS region,
|
|
25
|
+
b.anonymous_access AS public_access,
|
|
26
|
+
b.anonymous_actions AS public_actions
|
|
27
|
+
""",
|
|
28
|
+
cypher_visual_query="""
|
|
29
|
+
MATCH (b:S3Bucket)
|
|
30
|
+
WHERE b.anonymous_access = true
|
|
31
|
+
OR (b.anonymous_actions IS NOT NULL AND size(b.anonymous_actions) > 0)
|
|
32
|
+
OR EXISTS {
|
|
33
|
+
MATCH (b)-[:POLICY_STATEMENT]->(stmt:S3PolicyStatement)
|
|
34
|
+
WHERE stmt.effect = 'Allow'
|
|
35
|
+
AND (stmt.principal = '*' OR stmt.principal CONTAINS 'AllUsers')
|
|
36
|
+
}
|
|
37
|
+
WITH b
|
|
38
|
+
OPTIONAL MATCH p=(b)-[:POLICY_STATEMENT]->(:S3PolicyStatement)
|
|
39
|
+
RETURN *
|
|
40
|
+
""",
|
|
41
|
+
module=Module.AWS,
|
|
42
|
+
maturity=Maturity.EXPERIMENTAL,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Azure Facts
|
|
46
|
+
_azure_storage_public_blob_access = Fact(
|
|
47
|
+
id="azure_storage_public_blob_access",
|
|
48
|
+
name="Azure Storage Accounts with Public Blob Containers",
|
|
49
|
+
description=(
|
|
50
|
+
"Azure Storage Accounts that have blob containers with public access. "
|
|
51
|
+
"If a storage blob container has public_access set to 'Container' or 'Blob', "
|
|
52
|
+
"it means that the container is publicly accessible."
|
|
53
|
+
),
|
|
54
|
+
cypher_query="""
|
|
55
|
+
MATCH (sa:AzureStorageAccount)-[:USES]->(bs:AzureStorageBlobService)-[:CONTAINS]->(bc:AzureStorageBlobContainer)
|
|
56
|
+
WHERE bc.publicaccess IN ['Container', 'Blob']
|
|
57
|
+
RETURN
|
|
58
|
+
sa.id AS account_id,
|
|
59
|
+
sa.name AS account,
|
|
60
|
+
sa.resourcegroup AS resource_group,
|
|
61
|
+
sa.location AS region,
|
|
62
|
+
bc.id as id,
|
|
63
|
+
bc.name AS name,
|
|
64
|
+
bc.publicaccess AS public_access_element,
|
|
65
|
+
bc.publicaccess IN ['Container', 'Blob'] AS public_access
|
|
66
|
+
""",
|
|
67
|
+
cypher_visual_query="""
|
|
68
|
+
MATCH p=(sa:AzureStorageAccount)-[:USES]->(bs:AzureStorageBlobService)-[:CONTAINS]->(bc:AzureStorageBlobContainer)
|
|
69
|
+
WHERE bc.publicaccess IN ['Container', 'Blob']
|
|
70
|
+
RETURN *
|
|
71
|
+
""",
|
|
72
|
+
module=Module.AZURE,
|
|
73
|
+
maturity=Maturity.EXPERIMENTAL,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# Rule
|
|
78
|
+
class ObjectStoragePublic(Finding):
|
|
79
|
+
name: str | None = None
|
|
80
|
+
id: str | None = None
|
|
81
|
+
account: str | None = None
|
|
82
|
+
account_id: str | None = None
|
|
83
|
+
region: str | None = None
|
|
84
|
+
public_access: bool | None = None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
object_storage_public = Rule(
|
|
88
|
+
id="object_storage_public",
|
|
89
|
+
name="Public Object Storage Attack Surface",
|
|
90
|
+
description=(
|
|
91
|
+
"Publicly accessible object storage services such as AWS S3 buckets and Azure Storage Blob Containers"
|
|
92
|
+
),
|
|
93
|
+
output_model=ObjectStoragePublic,
|
|
94
|
+
facts=(
|
|
95
|
+
_aws_s3_public,
|
|
96
|
+
_azure_storage_public_blob_access,
|
|
97
|
+
),
|
|
98
|
+
tags=("infrastructure", "attack_surface"),
|
|
99
|
+
version="0.1.0",
|
|
100
|
+
)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from cartography.rules.spec.model import Fact
|
|
2
|
+
from cartography.rules.spec.model import Finding
|
|
3
|
+
from cartography.rules.spec.model import Maturity
|
|
4
|
+
from cartography.rules.spec.model import Module
|
|
5
|
+
from cartography.rules.spec.model import Rule
|
|
6
|
+
|
|
7
|
+
# AWS
|
|
8
|
+
_aws_policy_manipulation_capabilities = Fact(
|
|
9
|
+
id="aws_policy_manipulation_capabilities",
|
|
10
|
+
name="Principals with IAM Policy Creation and Modification Capabilities",
|
|
11
|
+
description=(
|
|
12
|
+
"AWS IAM principals that can create, modify, or attach IAM policies to other principals. "
|
|
13
|
+
),
|
|
14
|
+
cypher_query="""
|
|
15
|
+
MATCH (a:AWSAccount)-[:RESOURCE]->(principal:AWSPrincipal)
|
|
16
|
+
MATCH (principal)-[:POLICY]->(policy:AWSPolicy)-[:STATEMENT]->(allow_stmt:AWSPolicyStatement {effect:"Allow"})
|
|
17
|
+
WHERE NOT principal.name STARTS WITH 'AWSServiceRole'
|
|
18
|
+
AND NOT principal.name CONTAINS 'QuickSetup'
|
|
19
|
+
AND principal.name <> 'OrganizationAccountAccessRole'
|
|
20
|
+
WITH a, principal, policy, allow_stmt,
|
|
21
|
+
[label IN labels(principal) WHERE label <> 'AWSPrincipal'][0] AS principal_type,
|
|
22
|
+
[
|
|
23
|
+
'iam:CreatePolicy','iam:CreatePolicyVersion',
|
|
24
|
+
'iam:AttachUserPolicy','iam:AttachRolePolicy','iam:AttachGroupPolicy',
|
|
25
|
+
'iam:DetachUserPolicy','iam:DetachRolePolicy','iam:DetachGroupPolicy',
|
|
26
|
+
'iam:PutUserPolicy','iam:PutRolePolicy','iam:PutGroupPolicy'
|
|
27
|
+
] AS patterns
|
|
28
|
+
// Step 1 - Collect (action, resource) pairs for allowed statements
|
|
29
|
+
UNWIND allow_stmt.action AS allow_action
|
|
30
|
+
WITH a, principal, principal_type, policy, allow_stmt, allow_action, patterns
|
|
31
|
+
WHERE ANY(p IN patterns WHERE allow_action = p)
|
|
32
|
+
OR allow_action = 'iam:*'
|
|
33
|
+
OR allow_action = '*'
|
|
34
|
+
WITH a, principal, principal_type, policy, allow_stmt, allow_action, allow_stmt.resource AS allow_resources
|
|
35
|
+
// Step 2 - Gather all Deny statements for the same principal
|
|
36
|
+
OPTIONAL MATCH (principal)-[:POLICY]->(:AWSPolicy)-[:STATEMENT]->(deny_stmt:AWSPolicyStatement {effect:"Deny"})
|
|
37
|
+
WITH a, principal, principal_type, policy, allow_action, allow_resources,
|
|
38
|
+
REDUCE(acc = [], ds IN collect(deny_stmt.action) | acc + ds) AS all_deny_actions
|
|
39
|
+
// Step 3 - Filter out denied actions (handles *, iam:*, exact, and prefix wildcards)
|
|
40
|
+
WHERE NOT (
|
|
41
|
+
'*' IN all_deny_actions OR
|
|
42
|
+
'iam:*' IN all_deny_actions OR
|
|
43
|
+
allow_action IN all_deny_actions OR
|
|
44
|
+
ANY(d IN all_deny_actions WHERE d ENDS WITH('*') AND allow_action STARTS WITH split(d,'*')[0])
|
|
45
|
+
)
|
|
46
|
+
// Step 4 - Preserve (action, resource) mapping
|
|
47
|
+
UNWIND allow_resources AS resource
|
|
48
|
+
RETURN DISTINCT
|
|
49
|
+
a.name AS account,
|
|
50
|
+
a.id AS account_id,
|
|
51
|
+
principal.name AS principal_name,
|
|
52
|
+
principal.arn AS principal_identifier,
|
|
53
|
+
principal_type,
|
|
54
|
+
policy.name AS policy_name,
|
|
55
|
+
allow_action AS action,
|
|
56
|
+
resource
|
|
57
|
+
ORDER BY account, principal_name, action, resource
|
|
58
|
+
""",
|
|
59
|
+
cypher_visual_query="""
|
|
60
|
+
MATCH p1=(a:AWSAccount)-[:RESOURCE]->(principal:AWSPrincipal)
|
|
61
|
+
MATCH p2=(principal)-[:POLICY]->(policy:AWSPolicy)-[:STATEMENT]->(stmt:AWSPolicyStatement)
|
|
62
|
+
WHERE NOT principal.name STARTS WITH 'AWSServiceRole'
|
|
63
|
+
AND NOT principal.name CONTAINS 'QuickSetup'
|
|
64
|
+
AND principal.name <> 'OrganizationAccountAccessRole'
|
|
65
|
+
AND stmt.effect = 'Allow'
|
|
66
|
+
AND ANY(action IN stmt.action WHERE
|
|
67
|
+
action CONTAINS 'iam:CreatePolicy' OR action CONTAINS 'iam:CreatePolicyVersion'
|
|
68
|
+
OR action CONTAINS 'iam:AttachUserPolicy' OR action CONTAINS 'iam:AttachRolePolicy'
|
|
69
|
+
OR action CONTAINS 'iam:AttachGroupPolicy' OR action CONTAINS 'iam:DetachUserPolicy'
|
|
70
|
+
OR action CONTAINS 'iam:DetachRolePolicy' OR action CONTAINS 'iam:DetachGroupPolicy'
|
|
71
|
+
OR action CONTAINS 'iam:PutUserPolicy' OR action CONTAINS 'iam:PutRolePolicy'
|
|
72
|
+
OR action CONTAINS 'iam:PutGroupPolicy' OR action = 'iam:*' OR action = '*'
|
|
73
|
+
)
|
|
74
|
+
RETURN *
|
|
75
|
+
""",
|
|
76
|
+
module=Module.AWS,
|
|
77
|
+
maturity=Maturity.EXPERIMENTAL,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# Findings
|
|
82
|
+
class PolicyAdministrationPrivileges(Finding):
|
|
83
|
+
principal_name: str | None = None
|
|
84
|
+
principal_identifier: str | None = None
|
|
85
|
+
account: str | None = None
|
|
86
|
+
account_id: str | None = None
|
|
87
|
+
principal_type: str | None = None
|
|
88
|
+
policy_name: str | None = None
|
|
89
|
+
action: str | None = None
|
|
90
|
+
resource: str | None = None
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
policy_administration_privileges = Rule(
|
|
94
|
+
id="policy_administration_privileges",
|
|
95
|
+
name="Policy Administration Privileges",
|
|
96
|
+
description=(
|
|
97
|
+
"Principals can create, attach/detach, or write IAM policies—often enabling "
|
|
98
|
+
"indirect privilege escalation."
|
|
99
|
+
),
|
|
100
|
+
output_model=PolicyAdministrationPrivileges,
|
|
101
|
+
facts=(_aws_policy_manipulation_capabilities,),
|
|
102
|
+
tags=("iam", "privilege_escalation"),
|
|
103
|
+
version="0.1.0",
|
|
104
|
+
)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from cartography.rules.spec.model import Fact
|
|
2
|
+
from cartography.rules.spec.model import Finding
|
|
3
|
+
from cartography.rules.spec.model import Maturity
|
|
4
|
+
from cartography.rules.spec.model import Module
|
|
5
|
+
from cartography.rules.spec.model import Rule
|
|
6
|
+
|
|
7
|
+
# Facts
|
|
8
|
+
_unmanaged_accounts_ontology = Fact(
|
|
9
|
+
id="unmanaged-accounts-ontology",
|
|
10
|
+
name="User accounts not linked to a user identity",
|
|
11
|
+
description="Finds user accounts that are not linked to an ontology user node.",
|
|
12
|
+
cypher_query="""
|
|
13
|
+
MATCH (a:UserAccount)
|
|
14
|
+
WHERE NOT (a)<-[:HAS_ACCOUNT]-(:User)
|
|
15
|
+
AND (a.active = true OR a.active IS NULL)
|
|
16
|
+
return a.id as id, a._ont_email AS email, a._ont_source AS source
|
|
17
|
+
""",
|
|
18
|
+
cypher_visual_query="""
|
|
19
|
+
MATCH (a:UserAccount)
|
|
20
|
+
WHERE NOT (a)<-[:HAS_ACCOUNT]-(:User)
|
|
21
|
+
AND (a.active = true OR a.active IS NULL)
|
|
22
|
+
return a
|
|
23
|
+
""",
|
|
24
|
+
module=Module.CROSS_CLOUD,
|
|
25
|
+
maturity=Maturity.EXPERIMENTAL,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Rule
|
|
30
|
+
class UnmanagedAccountRuleOutput(Finding):
|
|
31
|
+
id: str | None = None
|
|
32
|
+
email: str | None = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
unmanaged_accounts = Rule(
|
|
36
|
+
id="unmanaged-account",
|
|
37
|
+
name="User accounts not linked to a user identity",
|
|
38
|
+
description="Detects accounts that are not linked to a known user identity (inactive accounts are excluded).",
|
|
39
|
+
output_model=UnmanagedAccountRuleOutput,
|
|
40
|
+
tags=("identity", "iam", "compliance"),
|
|
41
|
+
facts=(_unmanaged_accounts_ontology,),
|
|
42
|
+
version="0.1.1",
|
|
43
|
+
)
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
from cartography.rules.spec.model import Fact
|
|
2
|
+
from cartography.rules.spec.model import Finding
|
|
3
|
+
from cartography.rules.spec.model import Maturity
|
|
4
|
+
from cartography.rules.spec.model import Module
|
|
5
|
+
from cartography.rules.spec.model import Rule
|
|
6
|
+
|
|
7
|
+
# AWS
|
|
8
|
+
_aws_service_account_manipulation_via_ec2 = Fact(
|
|
9
|
+
id="aws_service_account_manipulation_via_ec2",
|
|
10
|
+
name="Service Resources with Account Manipulation Through Instance Profiles",
|
|
11
|
+
description=(
|
|
12
|
+
"AWS EC2 instances with attached IAM roles that can manipulate other AWS accounts. "
|
|
13
|
+
"Also indicates whether the instance is internet-exposed."
|
|
14
|
+
),
|
|
15
|
+
cypher_query="""
|
|
16
|
+
MATCH (a:AWSAccount)-[:RESOURCE]->(ec2:EC2Instance)
|
|
17
|
+
MATCH (ec2)-[:INSTANCE_PROFILE]->(profile:AWSInstanceProfile)
|
|
18
|
+
MATCH (profile)-[:ASSOCIATED_WITH]->(role:AWSRole)
|
|
19
|
+
MATCH (role)-[:POLICY]->(:AWSPolicy)-[:STATEMENT]->(allow_stmt:AWSPolicyStatement {effect:"Allow"})
|
|
20
|
+
WITH a, ec2, role, allow_stmt,
|
|
21
|
+
['iam:Create','iam:Attach','iam:Put','iam:Update','iam:Add'] AS patterns
|
|
22
|
+
// Step 1: Collect allowed actions that match IAM modification patterns
|
|
23
|
+
WITH a, ec2, role, patterns,
|
|
24
|
+
[action IN allow_stmt.action
|
|
25
|
+
WHERE ANY(p IN patterns WHERE action STARTS WITH p)
|
|
26
|
+
OR action = 'iam:*'
|
|
27
|
+
OR action = '*'
|
|
28
|
+
] AS matched_allow_actions
|
|
29
|
+
WHERE size(matched_allow_actions) > 0
|
|
30
|
+
// Step 2: Collect deny statements for the same role
|
|
31
|
+
OPTIONAL MATCH (role)-[:POLICY]->(:AWSPolicy)-[:STATEMENT]->(deny_stmt:AWSPolicyStatement {effect:"Deny"})
|
|
32
|
+
WITH a, ec2, role, patterns, matched_allow_actions,
|
|
33
|
+
// Flatten the deny action lists manually
|
|
34
|
+
REDUCE(acc = [], ds IN collect(deny_stmt.action) | acc + ds) AS all_deny_actions
|
|
35
|
+
// Step 3: Compute effective = allows minus denies
|
|
36
|
+
WITH a, ec2, role, matched_allow_actions, all_deny_actions,
|
|
37
|
+
[action IN matched_allow_actions
|
|
38
|
+
WHERE NOT (
|
|
39
|
+
// Full wildcard Deny *
|
|
40
|
+
'*' IN all_deny_actions OR
|
|
41
|
+
// IAM category wildcard Deny iam:*
|
|
42
|
+
'iam:*' IN all_deny_actions OR
|
|
43
|
+
// Exact match deny
|
|
44
|
+
action IN all_deny_actions OR
|
|
45
|
+
// Prefix wildcards like Deny iam:Update*
|
|
46
|
+
ANY(d IN all_deny_actions WHERE d ENDS WITH('*') AND action STARTS WITH split(d,'*')[0])
|
|
47
|
+
)
|
|
48
|
+
] AS effective_actions
|
|
49
|
+
WHERE size(effective_actions) > 0
|
|
50
|
+
// Step 4: Optional internet exposure context
|
|
51
|
+
OPTIONAL MATCH (ec2 {exposed_internet: True})
|
|
52
|
+
-[:MEMBER_OF_EC2_SECURITY_GROUP]->(sg:EC2SecurityGroup)
|
|
53
|
+
<-[:MEMBER_OF_EC2_SECURITY_GROUP]-(ip:IpPermissionInbound)
|
|
54
|
+
UNWIND effective_actions AS action
|
|
55
|
+
WITH a, ec2, role, sg, ip, COLLECT(DISTINCT action) AS actions
|
|
56
|
+
RETURN DISTINCT
|
|
57
|
+
ec2.id AS workload_id,
|
|
58
|
+
a.name AS account,
|
|
59
|
+
a.id AS account_id,
|
|
60
|
+
role.name AS role_name,
|
|
61
|
+
actions,
|
|
62
|
+
ec2.exposed_internet AS internet_accessible,
|
|
63
|
+
ec2.publicipaddress AS public_ip_address,
|
|
64
|
+
ip.fromport AS from_port,
|
|
65
|
+
ip.toport AS to_port
|
|
66
|
+
ORDER BY account, workload_id, internet_accessible, from_port
|
|
67
|
+
""",
|
|
68
|
+
cypher_visual_query="""
|
|
69
|
+
MATCH p = (a:AWSAccount)-[:RESOURCE]->(ec2:EC2Instance)
|
|
70
|
+
MATCH p1 = (ec2)-[:INSTANCE_PROFILE]->(profile:AWSInstanceProfile)
|
|
71
|
+
MATCH p2 = (profile)-[:ASSOCIATED_WITH]->(role:AWSRole)
|
|
72
|
+
MATCH p3 = (role)-[:POLICY]->(:AWSPolicy)-[:STATEMENT]->(stmt:AWSPolicyStatement)
|
|
73
|
+
WHERE stmt.effect = 'Allow'
|
|
74
|
+
AND ANY(action IN stmt.action WHERE
|
|
75
|
+
action STARTS WITH 'iam:Create'
|
|
76
|
+
OR action STARTS WITH 'iam:Attach'
|
|
77
|
+
OR action STARTS WITH 'iam:Put'
|
|
78
|
+
OR action STARTS WITH 'iam:Update'
|
|
79
|
+
OR action STARTS WITH 'iam:Add'
|
|
80
|
+
OR action = 'iam:*'
|
|
81
|
+
OR action = '*'
|
|
82
|
+
)
|
|
83
|
+
WITH p, p1, p2, p3, ec2
|
|
84
|
+
// Include the SG and rules for the instances that are internet open
|
|
85
|
+
MATCH p4=(ec2{exposed_internet: true})-[:MEMBER_OF_EC2_SECURITY_GROUP]->(sg:EC2SecurityGroup)<-[:MEMBER_OF_EC2_SECURITY_GROUP]-(ip:IpPermissionInbound)
|
|
86
|
+
RETURN *
|
|
87
|
+
""",
|
|
88
|
+
module=Module.AWS,
|
|
89
|
+
maturity=Maturity.EXPERIMENTAL,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
_aws_service_account_manipulation_via_lambda = Fact(
|
|
93
|
+
id="aws_service_account_manipulation",
|
|
94
|
+
name="Service Resources with Account Manipulation Through Lambda Roles",
|
|
95
|
+
description=(
|
|
96
|
+
"AWS Lambda functions with IAM roles that can manipulate other AWS accounts."
|
|
97
|
+
),
|
|
98
|
+
cypher_query="""
|
|
99
|
+
// Find Lambda functions with IAM modification or account manipulation capabilities
|
|
100
|
+
MATCH (a:AWSAccount)-[:RESOURCE]->(lambda:AWSLambda)
|
|
101
|
+
MATCH (lambda)-[:STS_ASSUMEROLE_ALLOW]->(role:AWSRole)
|
|
102
|
+
MATCH (role)-[:POLICY]->(:AWSPolicy)-[:STATEMENT]->(allow_stmt:AWSPolicyStatement {effect:"Allow"})
|
|
103
|
+
WITH a, lambda, role, allow_stmt,
|
|
104
|
+
['iam:Create','iam:Attach','iam:Put','iam:Update','iam:Add'] AS patterns
|
|
105
|
+
// Step 1: Gather allowed actions that match IAM modification patterns
|
|
106
|
+
WITH a, lambda, role, patterns,
|
|
107
|
+
[action IN allow_stmt.action
|
|
108
|
+
WHERE ANY(p IN patterns WHERE action STARTS WITH p)
|
|
109
|
+
OR action = 'iam:*'
|
|
110
|
+
OR action = '*'
|
|
111
|
+
] AS matched_allow_actions
|
|
112
|
+
WHERE size(matched_allow_actions) > 0
|
|
113
|
+
// Step 2: Gather all deny actions from the same role
|
|
114
|
+
OPTIONAL MATCH (role)-[:POLICY]->(:AWSPolicy)-[:STATEMENT]->(deny_stmt:AWSPolicyStatement {effect:"Deny"})
|
|
115
|
+
WITH a, lambda, role, patterns, matched_allow_actions,
|
|
116
|
+
REDUCE(acc = [], ds IN collect(deny_stmt.action) | acc + ds) AS all_deny_actions
|
|
117
|
+
// Step 3: Subtract Deny actions from Allow actions
|
|
118
|
+
WITH a, lambda, role, matched_allow_actions, all_deny_actions,
|
|
119
|
+
[action IN matched_allow_actions
|
|
120
|
+
WHERE NOT (
|
|
121
|
+
// Global wildcard deny
|
|
122
|
+
'*' IN all_deny_actions OR
|
|
123
|
+
// IAM wildcard deny
|
|
124
|
+
'iam:*' IN all_deny_actions OR
|
|
125
|
+
// Exact match deny
|
|
126
|
+
action IN all_deny_actions OR
|
|
127
|
+
// Prefix wildcards like Deny iam:Update*
|
|
128
|
+
ANY(d IN all_deny_actions WHERE d ENDS WITH('*') AND action STARTS WITH split(d,'*')[0])
|
|
129
|
+
)
|
|
130
|
+
] AS effective_actions
|
|
131
|
+
WHERE size(effective_actions) > 0
|
|
132
|
+
// Step 4: Return only Lambdas with effective IAM modification capabilities
|
|
133
|
+
UNWIND effective_actions AS action
|
|
134
|
+
WITH a, lambda, role, COLLECT(DISTINCT action) AS actions
|
|
135
|
+
RETURN DISTINCT
|
|
136
|
+
lambda.arn AS workload_id,
|
|
137
|
+
lambda.name AS workload_name,
|
|
138
|
+
a.name AS account,
|
|
139
|
+
a.id AS account_id,
|
|
140
|
+
role.name AS role_name,
|
|
141
|
+
actions,
|
|
142
|
+
lambda.anonymous_access AS internet_accessible,
|
|
143
|
+
lambda.description AS description
|
|
144
|
+
ORDER BY account, workload_id, internet_accessible
|
|
145
|
+
""",
|
|
146
|
+
cypher_visual_query="""
|
|
147
|
+
MATCH p = (a:AWSAccount)-[:RESOURCE]->(lambda:AWSLambda)
|
|
148
|
+
MATCH p1 = (lambda)-[:STS_ASSUMEROLE_ALLOW]->(role:AWSRole)
|
|
149
|
+
MATCH p2 = (role)-[:POLICY]->(policy:AWSPolicy)-[:STATEMENT]->(stmt:AWSPolicyStatement)
|
|
150
|
+
WHERE stmt.effect = 'Allow'
|
|
151
|
+
AND ANY(action IN stmt.action WHERE
|
|
152
|
+
action STARTS WITH 'iam:Create'
|
|
153
|
+
OR action STARTS WITH 'iam:Attach'
|
|
154
|
+
OR action STARTS WITH 'iam:Put'
|
|
155
|
+
OR action STARTS WITH 'iam:Update'
|
|
156
|
+
OR action STARTS WITH 'iam:Add'
|
|
157
|
+
OR action = 'iam:*'
|
|
158
|
+
OR action = '*'
|
|
159
|
+
)
|
|
160
|
+
RETURN *
|
|
161
|
+
""",
|
|
162
|
+
module=Module.AWS,
|
|
163
|
+
maturity=Maturity.EXPERIMENTAL,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# Rule
|
|
168
|
+
class WorkloadIdentityAdminCapabilities(Finding):
|
|
169
|
+
workload_name: str | None = None
|
|
170
|
+
workload_id: str | None = None
|
|
171
|
+
account: str | None = None
|
|
172
|
+
account_id: str | None = None
|
|
173
|
+
role_name: str | None = None
|
|
174
|
+
actions: list[str] | None = None
|
|
175
|
+
internet_accessible: bool | None = None
|
|
176
|
+
public_ip_address: str | None = None
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
workload_identity_admin_capabilities = Rule(
|
|
180
|
+
id="workload_identity_admin_capabilities",
|
|
181
|
+
name="Workload Identity-Admin Capabilities",
|
|
182
|
+
description=(
|
|
183
|
+
"A compute workload (VM or function) holds permissions to administer identities/policies. "
|
|
184
|
+
"If internet-exposed, the blast radius is higher."
|
|
185
|
+
),
|
|
186
|
+
output_model=WorkloadIdentityAdminCapabilities,
|
|
187
|
+
facts=(
|
|
188
|
+
_aws_service_account_manipulation_via_ec2,
|
|
189
|
+
_aws_service_account_manipulation_via_lambda,
|
|
190
|
+
),
|
|
191
|
+
tags=("iam", "privilege_escalation"),
|
|
192
|
+
version="0.1.0",
|
|
193
|
+
)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Output formatting utilities for Cartography rules.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
from dataclasses import asdict
|
|
8
|
+
from dataclasses import is_dataclass
|
|
9
|
+
from urllib.parse import quote
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel
|
|
12
|
+
|
|
13
|
+
from cartography.rules.data.rules import RULES
|
|
14
|
+
from cartography.rules.spec.result import RuleResult
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _generate_neo4j_browser_url(neo4j_uri: str, cypher_query: str) -> str:
|
|
18
|
+
"""Generate a clickable Neo4j Browser URL with pre-populated query."""
|
|
19
|
+
# Handle different Neo4j URI protocols
|
|
20
|
+
if neo4j_uri.startswith("bolt://"):
|
|
21
|
+
browser_uri = neo4j_uri.replace("bolt://", "http://", 1)
|
|
22
|
+
elif neo4j_uri.startswith("bolt+s://"):
|
|
23
|
+
browser_uri = neo4j_uri.replace("bolt+s://", "https://", 1)
|
|
24
|
+
elif neo4j_uri.startswith("bolt+ssc://"):
|
|
25
|
+
browser_uri = neo4j_uri.replace("bolt+ssc://", "https://", 1)
|
|
26
|
+
elif neo4j_uri.startswith("neo4j://"):
|
|
27
|
+
browser_uri = neo4j_uri.replace("neo4j://", "http://", 1)
|
|
28
|
+
elif neo4j_uri.startswith("neo4j+s://"):
|
|
29
|
+
browser_uri = neo4j_uri.replace("neo4j+s://", "https://", 1)
|
|
30
|
+
elif neo4j_uri.startswith("neo4j+ssc://"):
|
|
31
|
+
browser_uri = neo4j_uri.replace("neo4j+ssc://", "https://", 1)
|
|
32
|
+
else:
|
|
33
|
+
browser_uri = neo4j_uri
|
|
34
|
+
|
|
35
|
+
# Handle port mapping for local instances
|
|
36
|
+
if ":7687" in browser_uri and (
|
|
37
|
+
"localhost" in browser_uri or "127.0.0.1" in browser_uri
|
|
38
|
+
):
|
|
39
|
+
browser_uri = browser_uri.replace(":7687", ":7474")
|
|
40
|
+
|
|
41
|
+
# For Neo4j Aura (cloud), remove the port as it uses standard HTTPS port
|
|
42
|
+
if ".databases.neo4j.io" in browser_uri:
|
|
43
|
+
# Remove any port number for Aura URLs
|
|
44
|
+
browser_uri = re.sub(r":\d+", "", browser_uri)
|
|
45
|
+
|
|
46
|
+
# Ensure the URL ends properly
|
|
47
|
+
if not browser_uri.endswith("/"):
|
|
48
|
+
browser_uri += "/"
|
|
49
|
+
|
|
50
|
+
# URL encode the cypher query
|
|
51
|
+
encoded_query = quote(cypher_query.strip())
|
|
52
|
+
|
|
53
|
+
# Construct the Neo4j Browser URL with pre-populated query
|
|
54
|
+
return f"{browser_uri}browser/?cmd=edit&arg={encoded_query}"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def to_serializable(obj):
|
|
58
|
+
# Pydantic model (v2)
|
|
59
|
+
if isinstance(obj, BaseModel):
|
|
60
|
+
return to_serializable(obj.model_dump())
|
|
61
|
+
|
|
62
|
+
# Dataclass
|
|
63
|
+
if is_dataclass(obj):
|
|
64
|
+
return to_serializable(asdict(obj))
|
|
65
|
+
|
|
66
|
+
# Dict
|
|
67
|
+
if isinstance(obj, dict):
|
|
68
|
+
return {k: to_serializable(v) for k, v in obj.items()}
|
|
69
|
+
|
|
70
|
+
# List / Tuple / Set
|
|
71
|
+
if isinstance(obj, (list, tuple, set)):
|
|
72
|
+
return [to_serializable(v) for v in obj]
|
|
73
|
+
|
|
74
|
+
# Primitive
|
|
75
|
+
return obj
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _format_and_output_results(
|
|
79
|
+
all_results: list[RuleResult],
|
|
80
|
+
rule_names: list[str],
|
|
81
|
+
output_format: str,
|
|
82
|
+
total_facts: int,
|
|
83
|
+
total_findings: int,
|
|
84
|
+
):
|
|
85
|
+
"""Format and output the results of framework execution."""
|
|
86
|
+
if output_format == "json":
|
|
87
|
+
combined_output = [asdict(result) for result in all_results]
|
|
88
|
+
print(json.dumps(to_serializable(combined_output), indent=2))
|
|
89
|
+
else:
|
|
90
|
+
# Text summary
|
|
91
|
+
print("\n" + "=" * 60)
|
|
92
|
+
if len(rule_names) == 1:
|
|
93
|
+
print(f"EXECUTION SUMMARY - {RULES[rule_names[0]].name}")
|
|
94
|
+
else:
|
|
95
|
+
print("OVERALL SUMMARY")
|
|
96
|
+
print("=" * 60)
|
|
97
|
+
|
|
98
|
+
if len(rule_names) > 1:
|
|
99
|
+
print(f"Rules executed: {len(rule_names)}")
|
|
100
|
+
print(f"Total facts: {total_facts}")
|
|
101
|
+
print(f"Total findings: {total_findings}")
|
|
102
|
+
|
|
103
|
+
if total_findings > 0:
|
|
104
|
+
print(
|
|
105
|
+
f"\n\033[36mRule execution completed with {total_findings} total findings\033[0m"
|
|
106
|
+
)
|
|
107
|
+
else:
|
|
108
|
+
print("\n\033[90mRule execution completed with no findings\033[0m")
|