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.
Files changed (642) hide show
  1. cartography/_version.py +16 -3
  2. cartography/cli.py +466 -5
  3. cartography/client/aws/__init__.py +19 -0
  4. cartography/client/aws/ecr.py +51 -0
  5. cartography/client/core/tx.py +357 -8
  6. cartography/config.py +153 -0
  7. cartography/data/azure_permission_relationships.yaml +20 -0
  8. cartography/data/gcp_permission_relationships.yaml +21 -0
  9. cartography/data/indexes.cypher +0 -186
  10. cartography/data/jobs/analysis/aws_ec2_keypair_analysis.json +2 -2
  11. cartography/data/jobs/analysis/keycloak_inheritance.json +30 -0
  12. cartography/data/jobs/cleanup/gcp_compute_vpc_cleanup.json +0 -12
  13. cartography/data/jobs/cleanup/github_repos_cleanup.json +2 -0
  14. cartography/driftdetect/cli.py +3 -2
  15. cartography/graph/cleanupbuilder.py +198 -41
  16. cartography/graph/job.py +54 -6
  17. cartography/graph/querybuilder.py +528 -27
  18. cartography/graph/statement.py +5 -1
  19. cartography/intel/airbyte/__init__.py +105 -0
  20. cartography/intel/airbyte/connections.py +120 -0
  21. cartography/intel/airbyte/destinations.py +81 -0
  22. cartography/intel/airbyte/organizations.py +59 -0
  23. cartography/intel/airbyte/sources.py +78 -0
  24. cartography/intel/airbyte/tags.py +64 -0
  25. cartography/intel/airbyte/users.py +106 -0
  26. cartography/intel/airbyte/util.py +122 -0
  27. cartography/intel/airbyte/workspaces.py +63 -0
  28. cartography/intel/aws/__init__.py +24 -9
  29. cartography/intel/aws/acm.py +124 -0
  30. cartography/intel/aws/apigateway.py +253 -22
  31. cartography/intel/aws/apigatewayv2.py +116 -0
  32. cartography/intel/aws/cloudtrail.py +17 -39
  33. cartography/intel/aws/cloudtrail_management_events.py +962 -0
  34. cartography/intel/aws/cloudwatch.py +150 -4
  35. cartography/intel/aws/codebuild.py +132 -0
  36. cartography/intel/aws/cognito.py +201 -0
  37. cartography/intel/aws/config.py +7 -3
  38. cartography/intel/aws/ec2/elastic_ip_addresses.py +3 -1
  39. cartography/intel/aws/ec2/instances.py +25 -1
  40. cartography/intel/aws/ec2/internet_gateways.py +4 -2
  41. cartography/intel/aws/ec2/load_balancer_v2s.py +11 -5
  42. cartography/intel/aws/ec2/network_interfaces.py +5 -1
  43. cartography/intel/aws/ec2/reserved_instances.py +3 -1
  44. cartography/intel/aws/ec2/security_groups.py +140 -122
  45. cartography/intel/aws/ec2/snapshots.py +47 -84
  46. cartography/intel/aws/ec2/subnets.py +37 -63
  47. cartography/intel/aws/ec2/tgw.py +11 -5
  48. cartography/intel/aws/ec2/volumes.py +1 -1
  49. cartography/intel/aws/ec2/vpc.py +140 -124
  50. cartography/intel/aws/ec2/vpc_peerings.py +262 -125
  51. cartography/intel/aws/ecr.py +269 -98
  52. cartography/intel/aws/ecr_image_layers.py +923 -0
  53. cartography/intel/aws/ecs.py +251 -380
  54. cartography/intel/aws/efs.py +179 -11
  55. cartography/intel/aws/elasticache.py +102 -79
  56. cartography/intel/aws/elasticsearch.py +13 -4
  57. cartography/intel/aws/eventbridge.py +164 -0
  58. cartography/intel/aws/glue.py +181 -0
  59. cartography/intel/aws/guardduty.py +443 -0
  60. cartography/intel/aws/iam.py +750 -493
  61. cartography/intel/aws/identitycenter.py +605 -83
  62. cartography/intel/aws/inspector.py +221 -105
  63. cartography/intel/aws/kms.py +173 -201
  64. cartography/intel/aws/lambda_function.py +272 -189
  65. cartography/intel/aws/organizations.py +10 -9
  66. cartography/intel/aws/permission_relationships.py +10 -20
  67. cartography/intel/aws/rds.py +337 -446
  68. cartography/intel/aws/redshift.py +9 -4
  69. cartography/intel/aws/resourcegroupstaggingapi.py +78 -19
  70. cartography/intel/aws/resources.py +18 -0
  71. cartography/intel/aws/route53.py +386 -332
  72. cartography/intel/aws/s3.py +322 -14
  73. cartography/intel/aws/secretsmanager.py +81 -49
  74. cartography/intel/aws/securityhub.py +3 -1
  75. cartography/intel/aws/sns.py +62 -2
  76. cartography/intel/aws/sqs.py +36 -90
  77. cartography/intel/aws/ssm.py +3 -5
  78. cartography/intel/azure/__init__.py +202 -48
  79. cartography/intel/azure/aks.py +175 -0
  80. cartography/intel/azure/app_service.py +105 -0
  81. cartography/intel/azure/compute.py +59 -112
  82. cartography/intel/azure/container_instances.py +95 -0
  83. cartography/intel/azure/cosmosdb.py +222 -361
  84. cartography/intel/azure/data_factory.py +85 -0
  85. cartography/intel/azure/data_factory_dataset.py +128 -0
  86. cartography/intel/azure/data_factory_linked_service.py +119 -0
  87. cartography/intel/azure/data_factory_pipeline.py +142 -0
  88. cartography/intel/azure/data_lake.py +124 -0
  89. cartography/intel/azure/event_grid.py +94 -0
  90. cartography/intel/azure/functions.py +124 -0
  91. cartography/intel/azure/load_balancers.py +263 -0
  92. cartography/intel/azure/logic_apps.py +101 -0
  93. cartography/intel/azure/monitor.py +105 -0
  94. cartography/intel/azure/network.py +467 -0
  95. cartography/intel/azure/permission_relationships.py +466 -0
  96. cartography/intel/azure/rbac.py +309 -0
  97. cartography/intel/azure/resource_groups.py +82 -0
  98. cartography/intel/azure/security_center.py +106 -0
  99. cartography/intel/azure/sql.py +145 -292
  100. cartography/intel/azure/storage.py +185 -262
  101. cartography/intel/azure/subscription.py +21 -43
  102. cartography/intel/azure/tenant.py +39 -30
  103. cartography/intel/azure/util/common.py +13 -0
  104. cartography/intel/azure/util/credentials.py +49 -174
  105. cartography/intel/azure/util/tag.py +41 -0
  106. cartography/intel/create_indexes.py +2 -1
  107. cartography/intel/crowdstrike/spotlight.py +5 -2
  108. cartography/intel/dns.py +5 -2
  109. cartography/intel/entra/__init__.py +100 -1
  110. cartography/intel/entra/app_role_assignments.py +284 -0
  111. cartography/intel/entra/applications.py +182 -0
  112. cartography/intel/entra/federation/__init__.py +0 -0
  113. cartography/intel/entra/federation/aws_identity_center.py +77 -0
  114. cartography/intel/entra/groups.py +198 -0
  115. cartography/intel/entra/ou.py +48 -24
  116. cartography/intel/entra/service_principals.py +217 -0
  117. cartography/intel/entra/users.py +105 -57
  118. cartography/intel/gcp/__init__.py +334 -396
  119. cartography/intel/gcp/bigtable_app_profile.py +101 -0
  120. cartography/intel/gcp/bigtable_backup.py +91 -0
  121. cartography/intel/gcp/bigtable_cluster.py +93 -0
  122. cartography/intel/gcp/bigtable_instance.py +86 -0
  123. cartography/intel/gcp/bigtable_table.py +87 -0
  124. cartography/intel/gcp/cai.py +292 -0
  125. cartography/intel/gcp/clients.py +112 -0
  126. cartography/intel/gcp/compute.py +128 -119
  127. cartography/intel/gcp/crm/__init__.py +0 -0
  128. cartography/intel/gcp/crm/folders.py +114 -0
  129. cartography/intel/gcp/crm/orgs.py +70 -0
  130. cartography/intel/gcp/crm/projects.py +120 -0
  131. cartography/intel/gcp/dns.py +83 -169
  132. cartography/intel/gcp/gke.py +72 -113
  133. cartography/intel/gcp/iam.py +111 -91
  134. cartography/intel/gcp/permission_relationships.py +394 -0
  135. cartography/intel/gcp/policy_bindings.py +225 -0
  136. cartography/intel/gcp/storage.py +75 -159
  137. cartography/intel/github/__init__.py +62 -25
  138. cartography/intel/github/commits.py +423 -0
  139. cartography/intel/github/repos.py +463 -85
  140. cartography/intel/github/teams.py +3 -3
  141. cartography/intel/github/users.py +5 -0
  142. cartography/intel/github/util.py +12 -0
  143. cartography/intel/googleworkspace/__init__.py +193 -0
  144. cartography/intel/googleworkspace/devices.py +254 -0
  145. cartography/intel/googleworkspace/groups.py +568 -0
  146. cartography/intel/googleworkspace/oauth_apps.py +259 -0
  147. cartography/intel/googleworkspace/tenant.py +85 -0
  148. cartography/intel/googleworkspace/users.py +138 -0
  149. cartography/intel/gsuite/__init__.py +17 -9
  150. cartography/intel/gsuite/groups.py +291 -0
  151. cartography/intel/gsuite/users.py +142 -0
  152. cartography/intel/jamf/computers.py +7 -1
  153. cartography/intel/keycloak/__init__.py +153 -0
  154. cartography/intel/keycloak/authenticationexecutions.py +322 -0
  155. cartography/intel/keycloak/authenticationflows.py +77 -0
  156. cartography/intel/keycloak/clients.py +187 -0
  157. cartography/intel/keycloak/groups.py +126 -0
  158. cartography/intel/keycloak/identityproviders.py +94 -0
  159. cartography/intel/keycloak/organizations.py +163 -0
  160. cartography/intel/keycloak/realms.py +61 -0
  161. cartography/intel/keycloak/roles.py +202 -0
  162. cartography/intel/keycloak/scopes.py +73 -0
  163. cartography/intel/keycloak/users.py +70 -0
  164. cartography/intel/keycloak/util.py +47 -0
  165. cartography/intel/kubernetes/__init__.py +60 -14
  166. cartography/intel/kubernetes/clusters.py +86 -0
  167. cartography/intel/kubernetes/eks.py +402 -0
  168. cartography/intel/kubernetes/namespaces.py +59 -57
  169. cartography/intel/kubernetes/pods.py +168 -75
  170. cartography/intel/kubernetes/rbac.py +597 -0
  171. cartography/intel/kubernetes/secrets.py +95 -45
  172. cartography/intel/kubernetes/services.py +131 -67
  173. cartography/intel/kubernetes/util.py +142 -14
  174. cartography/intel/oci/iam.py +23 -9
  175. cartography/intel/oci/organizations.py +3 -1
  176. cartography/intel/oci/utils.py +28 -5
  177. cartography/intel/okta/applications.py +15 -5
  178. cartography/intel/okta/awssaml.py +14 -10
  179. cartography/intel/okta/factors.py +3 -1
  180. cartography/intel/okta/groups.py +5 -2
  181. cartography/intel/okta/organization.py +3 -1
  182. cartography/intel/okta/origins.py +3 -1
  183. cartography/intel/okta/roles.py +5 -2
  184. cartography/intel/okta/users.py +10 -2
  185. cartography/intel/ontology/__init__.py +44 -0
  186. cartography/intel/ontology/devices.py +54 -0
  187. cartography/intel/ontology/users.py +54 -0
  188. cartography/intel/ontology/utils.py +176 -0
  189. cartography/intel/pagerduty/escalation_policies.py +13 -6
  190. cartography/intel/pagerduty/schedules.py +9 -4
  191. cartography/intel/pagerduty/services.py +7 -3
  192. cartography/intel/pagerduty/teams.py +5 -2
  193. cartography/intel/pagerduty/users.py +3 -1
  194. cartography/intel/pagerduty/vendors.py +3 -1
  195. cartography/intel/scaleway/__init__.py +127 -0
  196. cartography/intel/scaleway/iam/__init__.py +0 -0
  197. cartography/intel/scaleway/iam/apikeys.py +71 -0
  198. cartography/intel/scaleway/iam/applications.py +71 -0
  199. cartography/intel/scaleway/iam/groups.py +71 -0
  200. cartography/intel/scaleway/iam/users.py +71 -0
  201. cartography/intel/scaleway/instances/__init__.py +0 -0
  202. cartography/intel/scaleway/instances/flexibleips.py +86 -0
  203. cartography/intel/scaleway/instances/instances.py +92 -0
  204. cartography/intel/scaleway/projects.py +79 -0
  205. cartography/intel/scaleway/storage/__init__.py +0 -0
  206. cartography/intel/scaleway/storage/snapshots.py +86 -0
  207. cartography/intel/scaleway/storage/volumes.py +84 -0
  208. cartography/intel/scaleway/utils.py +37 -0
  209. cartography/intel/sentinelone/__init__.py +75 -0
  210. cartography/intel/sentinelone/account.py +140 -0
  211. cartography/intel/sentinelone/agent.py +139 -0
  212. cartography/intel/sentinelone/api.py +124 -0
  213. cartography/intel/sentinelone/application.py +248 -0
  214. cartography/intel/sentinelone/cve.py +119 -0
  215. cartography/intel/sentinelone/utils.py +28 -0
  216. cartography/intel/slack/__init__.py +78 -0
  217. cartography/intel/slack/channels.py +80 -0
  218. cartography/intel/slack/groups.py +90 -0
  219. cartography/intel/slack/teams.py +65 -0
  220. cartography/intel/slack/users.py +57 -0
  221. cartography/intel/slack/utils.py +29 -0
  222. cartography/intel/spacelift/__init__.py +161 -0
  223. cartography/intel/spacelift/account.py +73 -0
  224. cartography/intel/spacelift/ec2_ownership.py +280 -0
  225. cartography/intel/spacelift/runs.py +463 -0
  226. cartography/intel/spacelift/spaces.py +112 -0
  227. cartography/intel/spacelift/stacks.py +119 -0
  228. cartography/intel/spacelift/util.py +122 -0
  229. cartography/intel/spacelift/workerpools.py +131 -0
  230. cartography/intel/spacelift/workers.py +128 -0
  231. cartography/intel/trivy/__init__.py +272 -0
  232. cartography/intel/trivy/scanner.py +386 -0
  233. cartography/models/airbyte/__init__.py +0 -0
  234. cartography/models/airbyte/connection.py +138 -0
  235. cartography/models/airbyte/destination.py +75 -0
  236. cartography/models/airbyte/organization.py +19 -0
  237. cartography/models/airbyte/source.py +75 -0
  238. cartography/models/airbyte/stream.py +74 -0
  239. cartography/models/airbyte/tag.py +69 -0
  240. cartography/models/airbyte/user.py +115 -0
  241. cartography/models/airbyte/workspace.py +46 -0
  242. cartography/models/anthropic/apikey.py +4 -0
  243. cartography/models/anthropic/user.py +4 -0
  244. cartography/models/aws/acm/__init__.py +0 -0
  245. cartography/models/aws/acm/certificate.py +75 -0
  246. cartography/models/aws/apigateway/__init__.py +0 -0
  247. cartography/models/aws/apigateway/apigatewaydeployment.py +74 -0
  248. cartography/models/aws/apigateway/apigatewayintegration.py +79 -0
  249. cartography/models/aws/apigateway/apigatewaymethod.py +74 -0
  250. cartography/models/aws/apigatewayv2/__init__.py +0 -0
  251. cartography/models/aws/apigatewayv2/apigatewayv2.py +53 -0
  252. cartography/models/aws/cloudtrail/management_events.py +153 -0
  253. cartography/models/aws/cloudtrail/trail.py +45 -0
  254. cartography/models/aws/cloudwatch/log_metric_filter.py +79 -0
  255. cartography/models/aws/cloudwatch/metric_alarm.py +53 -0
  256. cartography/models/aws/codebuild/__init__.py +0 -0
  257. cartography/models/aws/codebuild/project.py +49 -0
  258. cartography/models/aws/cognito/__init__.py +0 -0
  259. cartography/models/aws/cognito/identity_pool.py +70 -0
  260. cartography/models/aws/cognito/user_pool.py +47 -0
  261. cartography/models/aws/dynamodb/tables.py +2 -0
  262. cartography/models/aws/ec2/instances.py +25 -1
  263. cartography/models/aws/ec2/networkinterfaces.py +4 -0
  264. cartography/models/aws/ec2/security_group_rules.py +109 -0
  265. cartography/models/aws/ec2/security_groups.py +90 -0
  266. cartography/models/aws/ec2/snapshots.py +58 -0
  267. cartography/models/aws/ec2/subnet_instance.py +2 -0
  268. cartography/models/aws/ec2/subnet_networkinterface.py +2 -0
  269. cartography/models/aws/ec2/subnets.py +65 -0
  270. cartography/models/aws/ec2/volumes.py +20 -0
  271. cartography/models/aws/ec2/vpc.py +46 -0
  272. cartography/models/aws/ec2/vpc_cidr.py +102 -0
  273. cartography/models/aws/ec2/vpc_peering.py +157 -0
  274. cartography/models/aws/ecr/__init__.py +0 -0
  275. cartography/models/aws/ecr/image.py +146 -0
  276. cartography/models/aws/ecr/image_layer.py +107 -0
  277. cartography/models/aws/ecr/repository.py +72 -0
  278. cartography/models/aws/ecr/repository_image.py +95 -0
  279. cartography/models/aws/ecs/__init__.py +0 -0
  280. cartography/models/aws/ecs/clusters.py +64 -0
  281. cartography/models/aws/ecs/container_definitions.py +93 -0
  282. cartography/models/aws/ecs/container_instances.py +84 -0
  283. cartography/models/aws/ecs/containers.py +101 -0
  284. cartography/models/aws/ecs/services.py +134 -0
  285. cartography/models/aws/ecs/task_definitions.py +135 -0
  286. cartography/models/aws/ecs/tasks.py +134 -0
  287. cartography/models/aws/efs/access_point.py +77 -0
  288. cartography/models/aws/efs/file_system.py +60 -0
  289. cartography/models/aws/efs/mount_target.py +29 -2
  290. cartography/models/aws/elasticache/__init__.py +0 -0
  291. cartography/models/aws/elasticache/cluster.py +65 -0
  292. cartography/models/aws/elasticache/topic.py +67 -0
  293. cartography/models/aws/eventbridge/__init__.py +0 -0
  294. cartography/models/aws/eventbridge/rule.py +77 -0
  295. cartography/models/aws/eventbridge/target.py +71 -0
  296. cartography/models/aws/glue/__init__.py +0 -0
  297. cartography/models/aws/glue/connection.py +51 -0
  298. cartography/models/aws/glue/job.py +69 -0
  299. cartography/models/aws/guardduty/__init__.py +1 -0
  300. cartography/models/aws/guardduty/detectors.py +50 -0
  301. cartography/models/aws/guardduty/findings.py +121 -0
  302. cartography/models/aws/iam/access_key.py +103 -0
  303. cartography/models/aws/iam/account_role.py +24 -0
  304. cartography/models/aws/iam/federated_principal.py +60 -0
  305. cartography/models/aws/iam/group.py +60 -0
  306. cartography/models/aws/iam/group_membership.py +27 -0
  307. cartography/models/aws/iam/inline_policy.py +78 -0
  308. cartography/models/aws/iam/managed_policy.py +51 -0
  309. cartography/models/aws/iam/policy_statement.py +57 -0
  310. cartography/models/aws/iam/role.py +83 -0
  311. cartography/models/aws/iam/root_principal.py +52 -0
  312. cartography/models/aws/iam/service_principal.py +30 -0
  313. cartography/models/aws/iam/sts_assumerole_allow.py +38 -0
  314. cartography/models/aws/iam/user.py +59 -0
  315. cartography/models/aws/identitycenter/awsidentitycenter.py +1 -0
  316. cartography/models/aws/identitycenter/awspermissionset.py +70 -0
  317. cartography/models/aws/identitycenter/awssogroup.py +70 -0
  318. cartography/models/aws/identitycenter/awsssouser.py +49 -9
  319. cartography/models/aws/inspector/findings.py +37 -0
  320. cartography/models/aws/inspector/packages.py +1 -31
  321. cartography/models/aws/kms/__init__.py +0 -0
  322. cartography/models/aws/kms/aliases.py +86 -0
  323. cartography/models/aws/kms/grants.py +65 -0
  324. cartography/models/aws/kms/keys.py +88 -0
  325. cartography/models/aws/lambda_function/__init__.py +0 -0
  326. cartography/models/aws/lambda_function/alias.py +74 -0
  327. cartography/models/aws/lambda_function/event_source_mapping.py +88 -0
  328. cartography/models/aws/lambda_function/lambda_function.py +91 -0
  329. cartography/models/aws/lambda_function/layer.py +72 -0
  330. cartography/models/aws/rds/__init__.py +0 -0
  331. cartography/models/aws/rds/cluster.py +91 -0
  332. cartography/models/aws/rds/event_subscription.py +146 -0
  333. cartography/models/aws/rds/instance.py +156 -0
  334. cartography/models/aws/rds/snapshot.py +108 -0
  335. cartography/models/aws/rds/subnet_group.py +101 -0
  336. cartography/models/aws/route53/__init__.py +0 -0
  337. cartography/models/aws/route53/dnsrecord.py +235 -0
  338. cartography/models/aws/route53/nameserver.py +63 -0
  339. cartography/models/aws/route53/subzone.py +40 -0
  340. cartography/models/aws/route53/zone.py +47 -0
  341. cartography/models/aws/s3/notification.py +24 -0
  342. cartography/models/aws/secretsmanager/secret.py +106 -0
  343. cartography/models/aws/secretsmanager/secret_version.py +0 -2
  344. cartography/models/aws/sns/topic_subscription.py +74 -0
  345. cartography/models/aws/sqs/__init__.py +0 -0
  346. cartography/models/aws/sqs/queue.py +89 -0
  347. cartography/models/azure/__init__.py +0 -0
  348. cartography/models/azure/aks_cluster.py +54 -0
  349. cartography/models/azure/aks_nodepool.py +54 -0
  350. cartography/models/azure/app_service.py +59 -0
  351. cartography/models/azure/container_instance.py +57 -0
  352. cartography/models/azure/cosmosdb/__init__.py +0 -0
  353. cartography/models/azure/cosmosdb/account.py +77 -0
  354. cartography/models/azure/cosmosdb/accountfailoverpolicy.py +77 -0
  355. cartography/models/azure/cosmosdb/cassandrakeyspace.py +82 -0
  356. cartography/models/azure/cosmosdb/cassandratable.py +81 -0
  357. cartography/models/azure/cosmosdb/corspolicy.py +74 -0
  358. cartography/models/azure/cosmosdb/dblocation.py +120 -0
  359. cartography/models/azure/cosmosdb/mongodbcollection.py +82 -0
  360. cartography/models/azure/cosmosdb/mongodbdatabase.py +78 -0
  361. cartography/models/azure/cosmosdb/privateendpointconnection.py +81 -0
  362. cartography/models/azure/cosmosdb/sqlcontainer.py +88 -0
  363. cartography/models/azure/cosmosdb/sqldatabase.py +78 -0
  364. cartography/models/azure/cosmosdb/tableresource.py +76 -0
  365. cartography/models/azure/cosmosdb/virtualnetworkrule.py +78 -0
  366. cartography/models/azure/data_factory/__init__.py +0 -0
  367. cartography/models/azure/data_factory/data_factory.py +51 -0
  368. cartography/models/azure/data_factory/data_factory_dataset.py +94 -0
  369. cartography/models/azure/data_factory/data_factory_linked_service.py +78 -0
  370. cartography/models/azure/data_factory/data_factory_pipeline.py +93 -0
  371. cartography/models/azure/data_lake_filesystem.py +51 -0
  372. cartography/models/azure/event_grid_topic.py +57 -0
  373. cartography/models/azure/function_app.py +59 -0
  374. cartography/models/azure/load_balancer/__init__.py +0 -0
  375. cartography/models/azure/load_balancer/load_balancer.py +49 -0
  376. cartography/models/azure/load_balancer/load_balancer_backend_pool.py +73 -0
  377. cartography/models/azure/load_balancer/load_balancer_frontend_ip.py +75 -0
  378. cartography/models/azure/load_balancer/load_balancer_inbound_nat_rule.py +78 -0
  379. cartography/models/azure/load_balancer/load_balancer_rule.py +108 -0
  380. cartography/models/azure/logic_apps.py +56 -0
  381. cartography/models/azure/monitor.py +54 -0
  382. cartography/models/azure/network_interface.py +112 -0
  383. cartography/models/azure/network_security_group.py +50 -0
  384. cartography/models/azure/permission_relationships.py +60 -0
  385. cartography/models/azure/principal.py +41 -0
  386. cartography/models/azure/public_ip_address.py +50 -0
  387. cartography/models/azure/rbac.py +268 -0
  388. cartography/models/azure/resource_groups.py +52 -0
  389. cartography/models/azure/security_center.py +50 -0
  390. cartography/models/azure/sql/__init__.py +0 -0
  391. cartography/models/azure/sql/databasethreatdetectionpolicy.py +85 -0
  392. cartography/models/azure/sql/elasticpool.py +77 -0
  393. cartography/models/azure/sql/failovergroup.py +73 -0
  394. cartography/models/azure/sql/recoverabledatabase.py +75 -0
  395. cartography/models/azure/sql/replicationlink.py +81 -0
  396. cartography/models/azure/sql/restorabledroppeddatabase.py +82 -0
  397. cartography/models/azure/sql/restorepoint.py +74 -0
  398. cartography/models/azure/sql/serveradadministrator.py +74 -0
  399. cartography/models/azure/sql/serverdnsalias.py +71 -0
  400. cartography/models/azure/sql/sqldatabase.py +85 -0
  401. cartography/models/azure/sql/sqlserver.py +50 -0
  402. cartography/models/azure/sql/transparentdataencryption.py +76 -0
  403. cartography/models/azure/storage/__init__.py +0 -0
  404. cartography/models/azure/storage/account.py +59 -0
  405. cartography/models/azure/storage/blobcontainer.py +85 -0
  406. cartography/models/azure/storage/blobservice.py +71 -0
  407. cartography/models/azure/storage/fileservice.py +71 -0
  408. cartography/models/azure/storage/fileshare.py +82 -0
  409. cartography/models/azure/storage/queue.py +71 -0
  410. cartography/models/azure/storage/queueservice.py +73 -0
  411. cartography/models/azure/storage/table.py +72 -0
  412. cartography/models/azure/storage/tableservice.py +73 -0
  413. cartography/models/azure/subnet.py +101 -0
  414. cartography/models/azure/subscription.py +47 -0
  415. cartography/models/azure/tags/__init__.py +0 -0
  416. cartography/models/azure/tags/storage_tag.py +40 -0
  417. cartography/models/azure/tags/tag.py +37 -0
  418. cartography/models/azure/tenant.py +17 -0
  419. cartography/models/azure/virtual_network.py +49 -0
  420. cartography/models/azure/vm/__init__.py +0 -0
  421. cartography/models/azure/vm/datadisk.py +80 -0
  422. cartography/models/azure/vm/disk.py +55 -0
  423. cartography/models/azure/vm/snapshot.py +56 -0
  424. cartography/models/azure/vm/virtualmachine.py +59 -0
  425. cartography/models/bigfix/bigfix_computer.py +1 -1
  426. cartography/models/cloudflare/member.py +4 -0
  427. cartography/models/core/common.py +1 -0
  428. cartography/models/core/nodes.py +15 -2
  429. cartography/models/core/relationships.py +44 -0
  430. cartography/models/crowdstrike/hosts.py +1 -1
  431. cartography/models/digitalocean/droplet.py +2 -0
  432. cartography/models/duo/endpoint.py +1 -1
  433. cartography/models/duo/phone.py +2 -2
  434. cartography/models/duo/user.py +4 -0
  435. cartography/models/entra/app_role_assignment.py +115 -0
  436. cartography/models/entra/application.py +49 -0
  437. cartography/models/entra/entra_user_to_aws_sso.py +41 -0
  438. cartography/models/entra/group.py +117 -0
  439. cartography/models/entra/service_principal.py +104 -0
  440. cartography/models/entra/user.py +42 -51
  441. cartography/models/gcp/__init__.py +0 -0
  442. cartography/models/gcp/bigtable/__init__.py +0 -0
  443. cartography/models/gcp/bigtable/app_profile.py +94 -0
  444. cartography/models/gcp/bigtable/backup.py +91 -0
  445. cartography/models/gcp/bigtable/cluster.py +73 -0
  446. cartography/models/gcp/bigtable/instance.py +52 -0
  447. cartography/models/gcp/bigtable/table.py +69 -0
  448. cartography/models/gcp/compute/__init__.py +0 -0
  449. cartography/models/gcp/compute/subnet.py +74 -0
  450. cartography/models/gcp/compute/vpc.py +50 -0
  451. cartography/models/gcp/crm/__init__.py +0 -0
  452. cartography/models/gcp/crm/folders.py +98 -0
  453. cartography/models/gcp/crm/organizations.py +21 -0
  454. cartography/models/gcp/crm/projects.py +100 -0
  455. cartography/models/gcp/dns.py +109 -0
  456. cartography/models/gcp/gke.py +69 -0
  457. cartography/models/gcp/iam.py +3 -0
  458. cartography/models/gcp/permission_relationships.py +61 -0
  459. cartography/models/gcp/policy_bindings.py +93 -0
  460. cartography/models/gcp/storage/__init__.py +0 -0
  461. cartography/models/gcp/storage/bucket.py +119 -0
  462. cartography/models/github/commits.py +63 -0
  463. cartography/models/github/dependencies.py +73 -0
  464. cartography/models/github/manifests.py +49 -0
  465. cartography/models/github/users.py +10 -0
  466. cartography/models/googleworkspace/__init__.py +0 -0
  467. cartography/models/googleworkspace/device.py +132 -0
  468. cartography/models/googleworkspace/group.py +382 -0
  469. cartography/models/googleworkspace/oauth_app.py +124 -0
  470. cartography/models/googleworkspace/tenant.py +30 -0
  471. cartography/models/googleworkspace/user.py +113 -0
  472. cartography/models/gsuite/__init__.py +0 -0
  473. cartography/models/gsuite/group.py +218 -0
  474. cartography/models/gsuite/tenant.py +29 -0
  475. cartography/models/gsuite/user.py +107 -0
  476. cartography/models/kandji/device.py +1 -2
  477. cartography/models/keycloak/__init__.py +0 -0
  478. cartography/models/keycloak/authenticationexecution.py +160 -0
  479. cartography/models/keycloak/authenticationflow.py +54 -0
  480. cartography/models/keycloak/client.py +179 -0
  481. cartography/models/keycloak/group.py +101 -0
  482. cartography/models/keycloak/identityprovider.py +89 -0
  483. cartography/models/keycloak/organization.py +116 -0
  484. cartography/models/keycloak/organizationdomain.py +73 -0
  485. cartography/models/keycloak/realm.py +173 -0
  486. cartography/models/keycloak/role.py +126 -0
  487. cartography/models/keycloak/scope.py +73 -0
  488. cartography/models/keycloak/user.py +55 -0
  489. cartography/models/kubernetes/__init__.py +0 -0
  490. cartography/models/kubernetes/clusterrolebindings.py +138 -0
  491. cartography/models/kubernetes/clusterroles.py +52 -0
  492. cartography/models/kubernetes/clusters.py +26 -0
  493. cartography/models/kubernetes/containers.py +133 -0
  494. cartography/models/kubernetes/groups.py +107 -0
  495. cartography/models/kubernetes/namespaces.py +51 -0
  496. cartography/models/kubernetes/oidc.py +51 -0
  497. cartography/models/kubernetes/pods.py +80 -0
  498. cartography/models/kubernetes/rolebindings.py +159 -0
  499. cartography/models/kubernetes/roles.py +76 -0
  500. cartography/models/kubernetes/secrets.py +79 -0
  501. cartography/models/kubernetes/serviceaccounts.py +77 -0
  502. cartography/models/kubernetes/services.py +108 -0
  503. cartography/models/kubernetes/users.py +105 -0
  504. cartography/models/lastpass/user.py +4 -0
  505. cartography/models/ontology/__init__.py +0 -0
  506. cartography/models/ontology/device.py +137 -0
  507. cartography/models/ontology/mapping/__init__.py +76 -0
  508. cartography/models/ontology/mapping/data/__init__.py +0 -0
  509. cartography/models/ontology/mapping/data/apikeys.py +93 -0
  510. cartography/models/ontology/mapping/data/computeinstance.py +95 -0
  511. cartography/models/ontology/mapping/data/containers.py +88 -0
  512. cartography/models/ontology/mapping/data/databases.py +182 -0
  513. cartography/models/ontology/mapping/data/devices.py +194 -0
  514. cartography/models/ontology/mapping/data/thirdpartyapps.py +140 -0
  515. cartography/models/ontology/mapping/data/useraccounts.py +416 -0
  516. cartography/models/ontology/mapping/data/users.py +63 -0
  517. cartography/models/ontology/mapping/specs.py +85 -0
  518. cartography/models/ontology/user.py +51 -0
  519. cartography/models/openai/adminapikey.py +4 -0
  520. cartography/models/openai/apikey.py +4 -0
  521. cartography/models/openai/user.py +4 -0
  522. cartography/models/scaleway/__init__.py +0 -0
  523. cartography/models/scaleway/iam/__init__.py +0 -0
  524. cartography/models/scaleway/iam/apikey.py +100 -0
  525. cartography/models/scaleway/iam/application.py +52 -0
  526. cartography/models/scaleway/iam/group.py +95 -0
  527. cartography/models/scaleway/iam/user.py +64 -0
  528. cartography/models/scaleway/instance/__init__.py +0 -0
  529. cartography/models/scaleway/instance/flexibleip.py +52 -0
  530. cartography/models/scaleway/instance/instance.py +120 -0
  531. cartography/models/scaleway/organization.py +19 -0
  532. cartography/models/scaleway/project.py +48 -0
  533. cartography/models/scaleway/storage/__init__.py +0 -0
  534. cartography/models/scaleway/storage/snapshot.py +78 -0
  535. cartography/models/scaleway/storage/volume.py +51 -0
  536. cartography/models/sentinelone/__init__.py +1 -0
  537. cartography/models/sentinelone/account.py +40 -0
  538. cartography/models/sentinelone/agent.py +50 -0
  539. cartography/models/sentinelone/application.py +44 -0
  540. cartography/models/sentinelone/application_version.py +96 -0
  541. cartography/models/sentinelone/cve.py +73 -0
  542. cartography/models/slack/__init__.py +0 -0
  543. cartography/models/slack/channels.py +92 -0
  544. cartography/models/slack/group.py +129 -0
  545. cartography/models/slack/team.py +22 -0
  546. cartography/models/slack/user.py +62 -0
  547. cartography/models/snipeit/asset.py +2 -0
  548. cartography/models/snipeit/user.py +4 -0
  549. cartography/models/spacelift/__init__.py +0 -0
  550. cartography/models/spacelift/cloudtrailevent.py +120 -0
  551. cartography/models/spacelift/run.py +162 -0
  552. cartography/models/spacelift/space.py +131 -0
  553. cartography/models/spacelift/spaceliftaccount.py +31 -0
  554. cartography/models/spacelift/spaceliftgitcommit.py +157 -0
  555. cartography/models/spacelift/stack.py +96 -0
  556. cartography/models/spacelift/user.py +63 -0
  557. cartography/models/spacelift/worker.py +97 -0
  558. cartography/models/spacelift/workerpool.py +90 -0
  559. cartography/models/tailscale/device.py +2 -1
  560. cartography/models/tailscale/user.py +6 -1
  561. cartography/models/trivy/__init__.py +0 -0
  562. cartography/models/trivy/findings.py +66 -0
  563. cartography/models/trivy/fix.py +66 -0
  564. cartography/models/trivy/package.py +71 -0
  565. cartography/rules/README.md +1 -0
  566. cartography/rules/__init__.py +0 -0
  567. cartography/rules/cli.py +261 -0
  568. cartography/rules/data/__init__.py +0 -0
  569. cartography/rules/data/rules/__init__.py +46 -0
  570. cartography/rules/data/rules/cloud_security_product_deactivated.py +49 -0
  571. cartography/rules/data/rules/compute_instance_exposed.py +51 -0
  572. cartography/rules/data/rules/database_instance_exposed.py +53 -0
  573. cartography/rules/data/rules/delegation_boundary_modifiable.py +90 -0
  574. cartography/rules/data/rules/identity_administration_privileges.py +100 -0
  575. cartography/rules/data/rules/inactive_user_active_accounts.py +48 -0
  576. cartography/rules/data/rules/malicious_npm_dependencies_shai_hulud.py +2222 -0
  577. cartography/rules/data/rules/mfa_missing.py +46 -0
  578. cartography/rules/data/rules/object_storage_public.py +100 -0
  579. cartography/rules/data/rules/policy_administration_privileges.py +104 -0
  580. cartography/rules/data/rules/unmanaged_accounts.py +43 -0
  581. cartography/rules/data/rules/workload_identity_admin_capabilities.py +193 -0
  582. cartography/rules/formatters.py +108 -0
  583. cartography/rules/runners.py +216 -0
  584. cartography/rules/spec/__init__.py +0 -0
  585. cartography/rules/spec/model.py +267 -0
  586. cartography/rules/spec/result.py +38 -0
  587. cartography/sync.py +25 -5
  588. cartography/util.py +101 -31
  589. {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/METADATA +61 -22
  590. cartography-0.123.0.dist-info/RECORD +856 -0
  591. {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/entry_points.txt +1 -0
  592. cartography/data/jobs/cleanup/aws_dns_cleanup.json +0 -65
  593. cartography/data/jobs/cleanup/aws_import_account_access_key_cleanup.json +0 -17
  594. cartography/data/jobs/cleanup/aws_import_ec2_security_groupinfo_cleanup.json +0 -24
  595. cartography/data/jobs/cleanup/aws_import_groups_cleanup.json +0 -13
  596. cartography/data/jobs/cleanup/aws_import_identity_center_cleanup.json +0 -16
  597. cartography/data/jobs/cleanup/aws_import_lambda_cleanup.json +0 -50
  598. cartography/data/jobs/cleanup/aws_import_principals_cleanup.json +0 -30
  599. cartography/data/jobs/cleanup/aws_import_rds_clusters_cleanup.json +0 -23
  600. cartography/data/jobs/cleanup/aws_import_rds_instances_cleanup.json +0 -47
  601. cartography/data/jobs/cleanup/aws_import_rds_snapshots_cleanup.json +0 -23
  602. cartography/data/jobs/cleanup/aws_import_roles_cleanup.json +0 -13
  603. cartography/data/jobs/cleanup/aws_import_secrets_cleanup.json +0 -8
  604. cartography/data/jobs/cleanup/aws_import_snapshots_cleanup.json +0 -30
  605. cartography/data/jobs/cleanup/aws_import_users_cleanup.json +0 -8
  606. cartography/data/jobs/cleanup/aws_import_vpc_cleanup.json +0 -23
  607. cartography/data/jobs/cleanup/aws_import_vpc_peering_cleanup.json +0 -45
  608. cartography/data/jobs/cleanup/aws_kms_details.json +0 -10
  609. cartography/data/jobs/cleanup/azure_cosmosdb_cassandra_keyspace_cleanup.json +0 -25
  610. cartography/data/jobs/cleanup/azure_cosmosdb_cors_details.json +0 -15
  611. cartography/data/jobs/cleanup/azure_cosmosdb_mongodb_database_cleanup.json +0 -25
  612. cartography/data/jobs/cleanup/azure_cosmosdb_sql_database_cleanup.json +0 -25
  613. cartography/data/jobs/cleanup/azure_cosmosdb_table_resources_cleanup.json +0 -15
  614. cartography/data/jobs/cleanup/azure_database_account_cleanup.json +0 -85
  615. cartography/data/jobs/cleanup/azure_import_disks_cleanup.json +0 -15
  616. cartography/data/jobs/cleanup/azure_import_snapshots_cleanup.json +0 -15
  617. cartography/data/jobs/cleanup/azure_import_virtual_machines_cleanup.json +0 -25
  618. cartography/data/jobs/cleanup/azure_sql_server_cleanup.json +0 -125
  619. cartography/data/jobs/cleanup/azure_storage_account_cleanup.json +0 -95
  620. cartography/data/jobs/cleanup/azure_subscriptions_cleanup.json +0 -14
  621. cartography/data/jobs/cleanup/azure_tenant_cleanup.json +0 -9
  622. cartography/data/jobs/cleanup/gcp_compute_vpc_subnet_cleanup.json +0 -35
  623. cartography/data/jobs/cleanup/gcp_crm_folder_cleanup.json +0 -23
  624. cartography/data/jobs/cleanup/gcp_crm_organization_cleanup.json +0 -17
  625. cartography/data/jobs/cleanup/gcp_crm_project_cleanup.json +0 -23
  626. cartography/data/jobs/cleanup/gcp_dns_cleanup.json +0 -29
  627. cartography/data/jobs/cleanup/gcp_gke_cluster_cleanup.json +0 -17
  628. cartography/data/jobs/cleanup/gcp_storage_bucket_cleanup.json +0 -29
  629. cartography/data/jobs/cleanup/gsuite_ingest_groups_cleanup.json +0 -23
  630. cartography/data/jobs/cleanup/gsuite_ingest_users_cleanup.json +0 -11
  631. cartography/data/jobs/cleanup/kubernetes_import_cleanup.json +0 -70
  632. cartography/intel/gcp/crm.py +0 -355
  633. cartography/intel/gsuite/api.py +0 -342
  634. cartography-0.104.0rc2.dist-info/RECORD +0 -455
  635. /cartography/data/jobs/{analysis → scoped_analysis}/aws_s3acl_analysis.json +0 -0
  636. /cartography/models/aws/{apigateway.py → apigateway/apigateway.py} +0 -0
  637. /cartography/models/aws/{apigatewaycertificate.py → apigateway/apigatewaycertificate.py} +0 -0
  638. /cartography/models/aws/{apigatewayresource.py → apigateway/apigatewayresource.py} +0 -0
  639. /cartography/models/aws/{apigatewaystage.py → apigateway/apigatewaystage.py} +0 -0
  640. {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/WHEEL +0 -0
  641. {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/licenses/LICENSE +0 -0
  642. {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/top_level.txt +0 -0
@@ -31,33 +31,49 @@ def get_gcp_service_accounts(
31
31
  :return: A list of dictionaries representing GCP service accounts.
32
32
  """
33
33
  service_accounts: List[Dict[str, Any]] = []
34
- try:
34
+ request = (
35
+ iam_client.projects()
36
+ .serviceAccounts()
37
+ .list(
38
+ name=f"projects/{project_id}",
39
+ )
40
+ )
41
+ while request is not None:
42
+ response = request.execute()
43
+ if "accounts" in response:
44
+ service_accounts.extend(response["accounts"])
35
45
  request = (
36
46
  iam_client.projects()
37
47
  .serviceAccounts()
38
- .list(
39
- name=f"projects/{project_id}",
40
- )
41
- )
42
- while request is not None:
43
- response = request.execute()
44
- if "accounts" in response:
45
- service_accounts.extend(response["accounts"])
46
- request = (
47
- iam_client.projects()
48
- .serviceAccounts()
49
- .list_next(
50
- previous_request=request,
51
- previous_response=response,
52
- )
48
+ .list_next(
49
+ previous_request=request,
50
+ previous_response=response,
53
51
  )
54
- except Exception as e:
55
- logger.warning(
56
- f"Error retrieving service accounts for project {project_id}: {e}"
57
52
  )
58
53
  return service_accounts
59
54
 
60
55
 
56
+ @timeit
57
+ def get_gcp_predefined_roles(iam_client: Resource) -> List[Dict]:
58
+ """
59
+ Retrieve all predefined (Google-managed) IAM roles.
60
+
61
+ Predefined roles are global and not project-specific, so they can be fetched once
62
+ and reused across all target projects. This is useful for the CAI fallback where
63
+ the target project may not have the IAM API enabled.
64
+
65
+ :param iam_client: The IAM resource object created by googleapiclient.discovery.build().
66
+ :return: A list of dictionaries representing GCP predefined roles.
67
+ """
68
+ roles: List[Dict] = []
69
+ predefined_req = iam_client.roles().list(view="FULL")
70
+ while predefined_req is not None:
71
+ resp = predefined_req.execute()
72
+ roles.extend(resp.get("roles", []))
73
+ predefined_req = iam_client.roles().list_next(predefined_req, resp)
74
+ return roles
75
+
76
+
61
77
  @timeit
62
78
  def get_gcp_roles(iam_client: Resource, project_id: str) -> List[Dict]:
63
79
  """
@@ -67,27 +83,42 @@ def get_gcp_roles(iam_client: Resource, project_id: str) -> List[Dict]:
67
83
  :param project_id: The GCP Project ID to retrieve roles from.
68
84
  :return: A list of dictionaries representing GCP roles.
69
85
  """
70
- try:
71
- roles = []
86
+ roles = []
72
87
 
73
- # Get custom roles
74
- custom_req = iam_client.projects().roles().list(parent=f"projects/{project_id}")
75
- while custom_req is not None:
76
- resp = custom_req.execute()
77
- roles.extend(resp.get("roles", []))
78
- custom_req = iam_client.projects().roles().list_next(custom_req, resp)
88
+ # Get custom roles
89
+ custom_req = iam_client.projects().roles().list(parent=f"projects/{project_id}")
90
+ while custom_req is not None:
91
+ resp = custom_req.execute()
92
+ roles.extend(resp.get("roles", []))
93
+ custom_req = iam_client.projects().roles().list_next(custom_req, resp)
79
94
 
80
- # Get predefined roles
81
- predefined_req = iam_client.roles().list(view="FULL")
82
- while predefined_req is not None:
83
- resp = predefined_req.execute()
84
- roles.extend(resp.get("roles", []))
85
- predefined_req = iam_client.roles().list_next(predefined_req, resp)
95
+ # Get predefined roles (global, not project-specific)
96
+ roles.extend(get_gcp_predefined_roles(iam_client))
86
97
 
87
- return roles
88
- except Exception as e:
89
- logger.warning(f"Error getting GCP roles - {e}")
90
- return []
98
+ return roles
99
+
100
+
101
+ def transform_gcp_service_accounts(
102
+ raw_accounts: List[Dict[str, Any]],
103
+ project_id: str,
104
+ ) -> List[Dict[str, Any]]:
105
+ """
106
+ Transform raw GCP service accounts into loader-friendly dicts.
107
+ """
108
+ result: List[Dict[str, Any]] = []
109
+ for sa in raw_accounts:
110
+ result.append(
111
+ {
112
+ "id": sa["uniqueId"],
113
+ "email": sa.get("email"),
114
+ "displayName": sa.get("displayName"),
115
+ "oauth2ClientId": sa.get("oauth2ClientId"),
116
+ "uniqueId": sa.get("uniqueId"),
117
+ "disabled": sa.get("disabled", False),
118
+ "projectId": project_id,
119
+ },
120
+ )
121
+ return result
91
122
 
92
123
 
93
124
  @timeit
@@ -99,38 +130,55 @@ def load_gcp_service_accounts(
99
130
  ) -> None:
100
131
  """
101
132
  Load GCP service account data into Neo4j.
102
-
103
- :param neo4j_session: The Neo4j session.
104
- :param service_accounts: A list of service account data to load.
105
- :param project_id: The GCP Project ID associated with the service accounts.
106
- :param gcp_update_tag: The timestamp of the current sync run.
107
133
  """
108
134
  logger.debug(
109
135
  f"Loading {len(service_accounts)} service accounts for project {project_id}"
110
136
  )
111
- transformed_service_accounts = []
112
- for sa in service_accounts:
113
- transformed_sa = {
114
- "id": sa["uniqueId"],
115
- "email": sa.get("email"),
116
- "displayName": sa.get("displayName"),
117
- "oauth2ClientId": sa.get("oauth2ClientId"),
118
- "uniqueId": sa.get("uniqueId"),
119
- "disabled": sa.get("disabled", False),
120
- "projectId": project_id,
121
- }
122
- transformed_service_accounts.append(transformed_sa)
123
137
 
124
138
  load(
125
139
  neo4j_session,
126
140
  GCPServiceAccountSchema(),
127
- transformed_service_accounts,
141
+ service_accounts,
128
142
  lastupdated=gcp_update_tag,
129
143
  projectId=project_id,
130
- additional_labels=["GCPPrincipal"],
131
144
  )
132
145
 
133
146
 
147
+ def transform_gcp_roles(
148
+ raw_roles: List[Dict[str, Any]],
149
+ project_id: str,
150
+ ) -> List[Dict[str, Any]]:
151
+ """
152
+ Transform raw GCP roles into loader-friendly dicts.
153
+ """
154
+ result: List[Dict[str, Any]] = []
155
+ for role in raw_roles:
156
+ role_name = role["name"]
157
+ if role_name.startswith("roles/"):
158
+ role_type = (
159
+ "BASIC"
160
+ if role_name in ["roles/owner", "roles/editor", "roles/viewer"]
161
+ else "PREDEFINED"
162
+ )
163
+ else:
164
+ role_type = "CUSTOM"
165
+
166
+ result.append(
167
+ {
168
+ "id": role_name,
169
+ "name": role_name,
170
+ "title": role.get("title"),
171
+ "description": role.get("description"),
172
+ "deleted": role.get("deleted", False),
173
+ "etag": role.get("etag"),
174
+ "includedPermissions": role.get("includedPermissions", []),
175
+ "roleType": role_type,
176
+ "projectId": project_id,
177
+ },
178
+ )
179
+ return result
180
+
181
+
134
182
  @timeit
135
183
  def load_gcp_roles(
136
184
  neo4j_session: neo4j.Session,
@@ -140,41 +188,13 @@ def load_gcp_roles(
140
188
  ) -> None:
141
189
  """
142
190
  Load GCP role data into Neo4j.
143
-
144
- :param neo4j_session: The Neo4j session.
145
- :param roles: A list of role data to load.
146
- :param project_id: The GCP Project ID associated with the roles.
147
- :param gcp_update_tag: The timestamp of the current sync run.
148
191
  """
149
192
  logger.debug(f"Loading {len(roles)} roles for project {project_id}")
150
- transformed_roles = []
151
- for role in roles:
152
- role_name = role["name"]
153
- if role_name.startswith("roles/"):
154
- if role_name in ["roles/owner", "roles/editor", "roles/viewer"]:
155
- role_type = "BASIC"
156
- else:
157
- role_type = "PREDEFINED"
158
- else:
159
- role_type = "CUSTOM"
160
-
161
- transformed_role = {
162
- "id": role_name,
163
- "name": role_name,
164
- "title": role.get("title"),
165
- "description": role.get("description"),
166
- "deleted": role.get("deleted", False),
167
- "etag": role.get("etag"),
168
- "includedPermissions": role.get("includedPermissions", []),
169
- "roleType": role_type,
170
- "projectId": project_id,
171
- }
172
- transformed_roles.append(transformed_role)
173
193
 
174
194
  load(
175
195
  neo4j_session,
176
196
  GCPRoleSchema(),
177
- transformed_roles,
197
+ roles,
178
198
  lastupdated=gcp_update_tag,
179
199
  projectId=project_id,
180
200
  )
@@ -224,18 +244,18 @@ def sync(
224
244
  """
225
245
  logger.info(f"Syncing GCP IAM for project {project_id}")
226
246
 
227
- # Get and load service accounts
228
- service_accounts = get_gcp_service_accounts(iam_client, project_id)
247
+ service_accounts_raw = get_gcp_service_accounts(iam_client, project_id)
229
248
  logger.info(
230
- f"Found {len(service_accounts)} service accounts in project {project_id}"
249
+ f"Found {len(service_accounts_raw)} service accounts in project {project_id}"
231
250
  )
251
+ service_accounts = transform_gcp_service_accounts(service_accounts_raw, project_id)
232
252
  load_gcp_service_accounts(
233
253
  neo4j_session, service_accounts, project_id, gcp_update_tag
234
254
  )
235
255
 
236
- # Get and load roles
237
- roles = get_gcp_roles(iam_client, project_id)
238
- logger.info(f"Found {len(roles)} roles in project {project_id}")
256
+ roles_raw = get_gcp_roles(iam_client, project_id)
257
+ logger.info(f"Found {len(roles_raw)} roles in project {project_id}")
258
+ roles = transform_gcp_roles(roles_raw, project_id)
239
259
  load_gcp_roles(neo4j_session, roles, project_id, gcp_update_tag)
240
260
 
241
261
  # Run cleanup
@@ -0,0 +1,394 @@
1
+ import logging
2
+ import os
3
+ import re
4
+ from string import Template
5
+ from typing import Any
6
+
7
+ import neo4j
8
+ import yaml
9
+
10
+ from cartography.client.core.tx import load_matchlinks
11
+ from cartography.client.core.tx import read_list_of_dicts_tx
12
+ from cartography.client.core.tx import read_list_of_values_tx
13
+ from cartography.graph.job import GraphJob
14
+ from cartography.models.gcp.permission_relationships import GCPPermissionMatchLink
15
+ from cartography.util import timeit
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ def resolve_gcp_scope(scope: str, project_id: str) -> str:
21
+ """
22
+ Resolve GCP scope to follow the standard hierarchy pattern.
23
+
24
+ Process breakdown:
25
+ - If scope starts with cloudresourcemanager.googleapis.com, return project/{project_id}/* (ORG,FOLDER,PROJECT)
26
+ - Otherwise, return project/{project_id}/resource/{resource_id} where resource_id is scope.split("/")[-1]
27
+
28
+ Typical Scope Examples:
29
+ - ORG: //cloudresourcemanager.googleapis.com/organizations/{id}
30
+ - FOLDER: //cloudresourcemanager.googleapis.com/folders/{id}
31
+ - PROJECT: //cloudresourcemanager.googleapis.com/projects/{id}
32
+ - BUCKET: //storage.googleapis.com/buckets/{bucket_name}
33
+ - INSTANCE: //compute.googleapis.com/projects/{project_id}/zones/{zone}/instances/{instance_id}
34
+ """
35
+ if "cloudresourcemanager.googleapis.com" in scope:
36
+ return f"project/{project_id}/*"
37
+
38
+ return f"project/{project_id}/resource/{scope.split('/')[-1]}"
39
+
40
+
41
+ def compile_gcp_regex(item: str) -> re.Pattern:
42
+ # Escape special regex characters
43
+ item = item.replace(".", "\\.").replace("*", ".*")
44
+ try:
45
+ return re.compile(item, flags=re.IGNORECASE)
46
+ except re.error:
47
+ logger.warning(f"GCP regex did not compile for {item}")
48
+ # Return a regex that matches nothing -> no false positives
49
+ return re.compile("", flags=re.IGNORECASE)
50
+
51
+
52
+ def evaluate_clause(clause: re.Pattern, match: str) -> bool:
53
+ """
54
+ Evaluates a clause in GCP IAM. Clauses can be permissions, denied permissions, or scopes.
55
+
56
+ Arguments:
57
+ clause {re.Pattern} -- The compiled regex pattern to evaluate against. Clauses can use
58
+ variable length wildcards (*)
59
+ match {str} -- The item to match against.
60
+
61
+ Returns:
62
+ [bool] -- True if the clause matched, False otherwise
63
+ """
64
+ result = clause.fullmatch(match)
65
+ return result is not None
66
+
67
+
68
+ def evaluate_scope_for_resource(assignment: dict, resource_scope: str) -> bool:
69
+ scope = assignment["scope"]
70
+ # scope is now a compiled regex pattern
71
+ return evaluate_clause(scope, resource_scope)
72
+
73
+
74
+ def evaluate_denied_permission_for_permission(
75
+ permissions: dict, permission: str
76
+ ) -> bool:
77
+ for clause in permissions["denied_permissions"]:
78
+ if evaluate_clause(clause, permission):
79
+ return True
80
+ return False
81
+
82
+
83
+ def evaluate_permission_for_permission(permissions: dict, permission: str) -> bool:
84
+ for clause in permissions["permissions"]:
85
+ if evaluate_clause(clause, permission):
86
+ return True
87
+ return False
88
+
89
+
90
+ def evaluate_policy_binding_for_permissions(
91
+ assignment_data: dict[str, Any],
92
+ permissions: list[str],
93
+ resource_scope: str,
94
+ ) -> bool:
95
+ permissions_dict = assignment_data["permissions"]
96
+ scope = assignment_data["scope"]
97
+
98
+ # Level 1: Check scope matching
99
+ if not evaluate_scope_for_resource({"scope": scope}, resource_scope):
100
+ return False
101
+
102
+ for permission in permissions:
103
+ # Level 2: Check denied permissions
104
+ if not evaluate_denied_permission_for_permission(permissions_dict, permission):
105
+ # Level 3: Check allowed permissions
106
+ if evaluate_permission_for_permission(permissions_dict, permission):
107
+ return True
108
+
109
+ return False
110
+
111
+
112
+ def principal_allowed_on_resource(
113
+ policy_bindings: dict[str, Any],
114
+ resource_scope: str,
115
+ permissions: list[str],
116
+ ) -> bool:
117
+ for _, assignment_data in policy_bindings.items():
118
+ if evaluate_policy_binding_for_permissions(
119
+ assignment_data, permissions, resource_scope
120
+ ):
121
+ return True
122
+
123
+ return False
124
+
125
+
126
+ def calculate_permission_relationships(
127
+ principals: dict[str, Any],
128
+ resource_dict: dict[str, str],
129
+ permissions: list[str],
130
+ ) -> list[dict[str, Any]]:
131
+ allowed_mappings: list[dict[str, Any]] = []
132
+ for resource_id, resource_scope in resource_dict.items():
133
+ for principal_email, policy_bindings in principals.items():
134
+ if principal_allowed_on_resource(
135
+ policy_bindings, resource_scope, permissions
136
+ ):
137
+ allowed_mappings.append(
138
+ {
139
+ "principal_email": principal_email,
140
+ "resource_id": resource_id,
141
+ }
142
+ )
143
+ return allowed_mappings
144
+
145
+
146
+ @timeit
147
+ def get_principals_for_project(
148
+ neo4j_session: neo4j.Session, project_id: str
149
+ ) -> dict[str, Any]:
150
+ """
151
+ Get all principals (users, service accounts, groups) with their policy bindings
152
+ for a given GCP project.
153
+ """
154
+ get_principals_query = """
155
+ MATCH
156
+ (project:GCPProject{id: $ProjectId})-[:RESOURCE]->
157
+ (binding:GCPPolicyBinding)-[:GRANTS_ROLE]->
158
+ (role:GCPRole)
159
+ MATCH
160
+ (principal:GCPPrincipal)-[:HAS_ALLOW_POLICY]->(binding)
161
+ WHERE binding.has_condition = false
162
+ RETURN
163
+ DISTINCT principal.email as principal_email, binding.id as binding_id,
164
+ binding.resource as binding_resource, role.permissions as role_permissions
165
+ """
166
+
167
+ results = neo4j_session.execute_read(
168
+ read_list_of_dicts_tx,
169
+ get_principals_query,
170
+ ProjectId=project_id,
171
+ )
172
+
173
+ principals: dict[str, Any] = {}
174
+ for r in results:
175
+ principal_email = r["principal_email"]
176
+ binding_id = r["binding_id"]
177
+ binding_resource = r["binding_resource"]
178
+ role_permissions = r["role_permissions"] or []
179
+
180
+ if principal_email not in principals:
181
+ principals[principal_email] = {}
182
+
183
+ # Compile permissions from role
184
+ compiled_permissions = compile_permissions_from_role(role_permissions)
185
+ compiled_scope = compile_gcp_regex(
186
+ resolve_gcp_scope(binding_resource, project_id)
187
+ )
188
+
189
+ principals[principal_email][binding_id] = {
190
+ "permissions": compiled_permissions,
191
+ "scope": compiled_scope,
192
+ }
193
+
194
+ return principals
195
+
196
+
197
+ def compile_permissions_from_role(role_permissions: list[str]) -> dict[str, Any]:
198
+ permissions: dict[str, list[str]] = {
199
+ "permissions": [],
200
+ "denied_permissions": [],
201
+ }
202
+
203
+ # For now, we only handle allow policies
204
+ # GCP can have denied permissions and denied principals (under deny policies),
205
+ # but we'd need to fetch that separately using the IAM API.
206
+ permissions["permissions"] = role_permissions
207
+
208
+ return compile_permissions(permissions)
209
+
210
+
211
+ def compile_permissions(permissions: dict[str, Any]) -> dict[str, Any]:
212
+ compiled_permissions = {}
213
+ compiled_permissions["permissions"] = [
214
+ compile_gcp_regex(item) for item in permissions["permissions"]
215
+ ]
216
+ compiled_permissions["denied_permissions"] = [
217
+ compile_gcp_regex(item) for item in permissions["denied_permissions"]
218
+ ]
219
+
220
+ return compiled_permissions
221
+
222
+
223
+ @timeit
224
+ def get_resource_ids(
225
+ neo4j_session: neo4j.Session, project_id: str, target_label: str
226
+ ) -> dict[str, str]:
227
+ """
228
+ Get resource IDs for a given resource type in a project.
229
+ Returns a dictionary mapping actual resource IDs to their expected scopes.
230
+ """
231
+ get_resource_query = Template(
232
+ """
233
+ MATCH (project:GCPProject{id: $ProjectId})-[:RESOURCE]->(resource:$node_label)
234
+ RETURN resource.id as resource_id
235
+ """,
236
+ )
237
+ get_resource_query_template = get_resource_query.safe_substitute(
238
+ node_label=target_label,
239
+ )
240
+ resource_ids = neo4j_session.execute_read(
241
+ read_list_of_values_tx,
242
+ get_resource_query_template,
243
+ ProjectId=project_id,
244
+ )
245
+ resource_dict = {
246
+ resource_id: f'project/{project_id}/resource/{resource_id.split("/")[-1]}'
247
+ # Resource scope is project/{project_id}/resource/{last part of resource_id when separated by /}
248
+ # Resource_id as key for loading and resource scope as value for scope evaluation
249
+ for resource_id in resource_ids
250
+ }
251
+ return resource_dict
252
+
253
+
254
+ def parse_permission_relationships_file(file_path: str) -> list[dict[str, Any]]:
255
+ try:
256
+ if os.path.isabs(file_path):
257
+ resolved_file_path = file_path
258
+ else:
259
+ resolved_file_path = os.path.join(os.getcwd(), file_path)
260
+ with open(resolved_file_path) as f:
261
+ relationship_mapping = yaml.load(f, Loader=yaml.FullLoader)
262
+ return relationship_mapping or []
263
+ except FileNotFoundError:
264
+ logger.warning(
265
+ f"GCP permission relationships file not found. Original filename passed to sync: '{file_path}', "
266
+ f"resolved full path: '{resolved_file_path}'. Skipping sync stage {__name__}. "
267
+ f"If you want to run this sync, please explicitly set a value for --gcp-permission-relationships-file in the "
268
+ f"command line interface."
269
+ )
270
+ return []
271
+
272
+
273
+ def is_valid_gcp_rpr(rpr: dict[str, Any]) -> bool:
274
+ required_fields = ["permissions", "relationship_name", "target_label"]
275
+ for field in required_fields:
276
+ if field not in rpr:
277
+ return False
278
+ return True
279
+
280
+
281
+ @timeit
282
+ def load_principal_mappings(
283
+ neo4j_session: neo4j.Session,
284
+ principal_mappings: list[dict[str, Any]],
285
+ matchlink_schema: GCPPermissionMatchLink,
286
+ update_tag: int,
287
+ project_id: str,
288
+ ) -> None:
289
+ """
290
+ Load principal mappings into Neo4j using MatchLinks.
291
+ """
292
+ if not principal_mappings:
293
+ return
294
+
295
+ logger.info(
296
+ f"Loading {len(principal_mappings)} {matchlink_schema.rel_label} relationships "
297
+ f"for {matchlink_schema.source_node_label} -> {matchlink_schema.target_node_label}"
298
+ )
299
+
300
+ load_matchlinks(
301
+ neo4j_session,
302
+ matchlink_schema,
303
+ principal_mappings,
304
+ lastupdated=update_tag,
305
+ _sub_resource_label="GCPProject",
306
+ _sub_resource_id=project_id,
307
+ )
308
+
309
+
310
+ @timeit
311
+ def cleanup_rpr(
312
+ neo4j_session: neo4j.Session,
313
+ matchlink_schema: GCPPermissionMatchLink,
314
+ update_tag: int,
315
+ project_id: str,
316
+ ) -> None:
317
+ logger.info(
318
+ "Cleaning up relationship '%s' for node label '%s'",
319
+ matchlink_schema.rel_label,
320
+ matchlink_schema.target_node_label,
321
+ )
322
+
323
+ GraphJob.from_matchlink(
324
+ matchlink_schema,
325
+ "GCPProject",
326
+ project_id,
327
+ update_tag,
328
+ ).run(neo4j_session)
329
+
330
+
331
+ @timeit
332
+ def sync(
333
+ neo4j_session: neo4j.Session,
334
+ project_id: str,
335
+ update_tag: int,
336
+ common_job_parameters: dict[str, Any],
337
+ ) -> None:
338
+ logger.info("Syncing GCP Permission Relationships for project '%s'.", project_id)
339
+
340
+ pr_file = common_job_parameters.get("gcp_permission_relationships_file")
341
+ if not pr_file:
342
+ logger.warning(
343
+ "GCP permission relationships file was not specified, skipping. If this is not expected, please check your "
344
+ "value of --gcp-permission-relationships-file"
345
+ )
346
+ return
347
+
348
+ # 1. GET - Fetch all GCP principals in suitable dict format
349
+ principals = get_principals_for_project(neo4j_session, project_id)
350
+
351
+ # 2. PARSE - Parse relationship file
352
+ relationship_mapping = parse_permission_relationships_file(pr_file)
353
+
354
+ # 3. EVALUATE - Evaluate each relationship and resource ID
355
+ for rpr in relationship_mapping:
356
+ if not is_valid_gcp_rpr(rpr):
357
+ logger.error(f"Invalid permission relationship configuration: {rpr}")
358
+ continue
359
+
360
+ target_label = rpr["target_label"]
361
+ relationship_name = rpr["relationship_name"]
362
+ permissions = rpr["permissions"]
363
+
364
+ resource_dict = get_resource_ids(neo4j_session, project_id, target_label)
365
+
366
+ logger.info(
367
+ f"Evaluating relationship '{relationship_name}' for resource type '{target_label}'"
368
+ )
369
+ matches = calculate_permission_relationships(
370
+ principals, resource_dict, permissions
371
+ )
372
+
373
+ # Create MatchLink schema with dynamic attributes
374
+ matchlink_schema = GCPPermissionMatchLink(
375
+ source_node_label="GCPPrincipal",
376
+ target_node_label=target_label,
377
+ rel_label=relationship_name,
378
+ )
379
+
380
+ load_principal_mappings(
381
+ neo4j_session,
382
+ matches,
383
+ matchlink_schema,
384
+ update_tag,
385
+ project_id,
386
+ )
387
+ cleanup_rpr(
388
+ neo4j_session,
389
+ matchlink_schema,
390
+ update_tag,
391
+ project_id,
392
+ )
393
+
394
+ logger.info(f"Completed GCP Permission Relationships sync for project {project_id}")