cartography 0.93.0rc1__py3-none-any.whl → 0.123.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (822) hide show
  1. cartography/__main__.py +1 -2
  2. cartography/_version.py +34 -0
  3. cartography/cli.py +903 -225
  4. cartography/client/aws/__init__.py +19 -0
  5. cartography/client/aws/ecr.py +51 -0
  6. cartography/client/core/tx.py +400 -27
  7. cartography/config.py +215 -10
  8. cartography/data/azure_permission_relationships.yaml +20 -0
  9. cartography/data/gcp_permission_relationships.yaml +21 -0
  10. cartography/data/indexes.cypher +1 -200
  11. cartography/data/jobs/analysis/aws_ec2_asset_exposure.json +17 -2
  12. cartography/data/jobs/analysis/aws_ec2_keypair_analysis.json +2 -2
  13. cartography/data/jobs/analysis/gcp_compute_asset_inet_exposure.json +1 -1
  14. cartography/data/jobs/analysis/keycloak_inheritance.json +30 -0
  15. cartography/data/jobs/cleanup/crowdstrike_import_cleanup.json +0 -5
  16. cartography/data/jobs/cleanup/gcp_compute_vpc_cleanup.json +0 -12
  17. cartography/data/jobs/cleanup/github_repos_cleanup.json +27 -0
  18. cartography/data/jobs/scoped_analysis/aws_ec2_iaminstanceprofile.json +15 -0
  19. cartography/data/jobs/scoped_analysis/semgrep_sca_risk_analysis.json +13 -13
  20. cartography/driftdetect/__main__.py +1 -2
  21. cartography/driftdetect/add_shortcut.py +10 -2
  22. cartography/driftdetect/cli.py +72 -75
  23. cartography/driftdetect/detect_deviations.py +7 -3
  24. cartography/driftdetect/get_states.py +20 -8
  25. cartography/driftdetect/model.py +5 -5
  26. cartography/driftdetect/serializers.py +8 -6
  27. cartography/driftdetect/storage.py +2 -2
  28. cartography/graph/cleanupbuilder.py +255 -35
  29. cartography/graph/job.py +104 -20
  30. cartography/graph/querybuilder.py +689 -91
  31. cartography/graph/statement.py +49 -36
  32. cartography/intel/airbyte/__init__.py +105 -0
  33. cartography/intel/airbyte/connections.py +120 -0
  34. cartography/intel/airbyte/destinations.py +81 -0
  35. cartography/intel/airbyte/organizations.py +59 -0
  36. cartography/intel/airbyte/sources.py +78 -0
  37. cartography/intel/airbyte/tags.py +64 -0
  38. cartography/intel/airbyte/users.py +106 -0
  39. cartography/intel/airbyte/util.py +122 -0
  40. cartography/intel/airbyte/workspaces.py +63 -0
  41. cartography/intel/analysis.py +4 -1
  42. cartography/intel/anthropic/__init__.py +62 -0
  43. cartography/intel/anthropic/apikeys.py +72 -0
  44. cartography/intel/anthropic/users.py +75 -0
  45. cartography/intel/anthropic/util.py +51 -0
  46. cartography/intel/anthropic/workspaces.py +95 -0
  47. cartography/intel/aws/__init__.py +137 -59
  48. cartography/intel/aws/acm.py +124 -0
  49. cartography/intel/aws/apigateway.py +482 -217
  50. cartography/intel/aws/apigatewayv2.py +116 -0
  51. cartography/intel/aws/cloudtrail.py +105 -0
  52. cartography/intel/aws/cloudtrail_management_events.py +962 -0
  53. cartography/intel/aws/cloudwatch.py +239 -0
  54. cartography/intel/aws/codebuild.py +132 -0
  55. cartography/intel/aws/cognito.py +201 -0
  56. cartography/intel/aws/config.py +63 -23
  57. cartography/intel/aws/dynamodb.py +108 -40
  58. cartography/intel/aws/ec2/__init__.py +2 -2
  59. cartography/intel/aws/ec2/auto_scaling_groups.py +254 -189
  60. cartography/intel/aws/ec2/elastic_ip_addresses.py +44 -14
  61. cartography/intel/aws/ec2/images.py +74 -39
  62. cartography/intel/aws/ec2/instances.py +262 -137
  63. cartography/intel/aws/ec2/internet_gateways.py +44 -13
  64. cartography/intel/aws/ec2/key_pairs.py +72 -39
  65. cartography/intel/aws/ec2/launch_templates.py +143 -66
  66. cartography/intel/aws/ec2/load_balancer_v2s.py +119 -45
  67. cartography/intel/aws/ec2/load_balancers.py +165 -147
  68. cartography/intel/aws/ec2/network_acls.py +233 -0
  69. cartography/intel/aws/ec2/network_interfaces.py +150 -87
  70. cartography/intel/aws/ec2/reserved_instances.py +48 -17
  71. cartography/intel/aws/ec2/route_tables.py +327 -0
  72. cartography/intel/aws/ec2/security_groups.py +189 -121
  73. cartography/intel/aws/ec2/snapshots.py +93 -91
  74. cartography/intel/aws/ec2/subnets.py +70 -58
  75. cartography/intel/aws/ec2/tgw.py +111 -39
  76. cartography/intel/aws/ec2/util.py +1 -1
  77. cartography/intel/aws/ec2/volumes.py +69 -41
  78. cartography/intel/aws/ec2/vpc.py +157 -116
  79. cartography/intel/aws/ec2/vpc_peerings.py +317 -121
  80. cartography/intel/aws/ecr.py +336 -93
  81. cartography/intel/aws/ecr_image_layers.py +923 -0
  82. cartography/intel/aws/ecs.py +310 -403
  83. cartography/intel/aws/efs.py +261 -0
  84. cartography/intel/aws/eks.py +55 -29
  85. cartography/intel/aws/elasticache.py +130 -83
  86. cartography/intel/aws/elasticsearch.py +70 -24
  87. cartography/intel/aws/emr.py +61 -23
  88. cartography/intel/aws/eventbridge.py +164 -0
  89. cartography/intel/aws/glue.py +181 -0
  90. cartography/intel/aws/guardduty.py +443 -0
  91. cartography/intel/aws/iam.py +978 -464
  92. cartography/intel/aws/iam_instance_profiles.py +73 -0
  93. cartography/intel/aws/identitycenter.py +847 -0
  94. cartography/intel/aws/inspector.py +330 -133
  95. cartography/intel/aws/kms.py +235 -209
  96. cartography/intel/aws/lambda_function.py +328 -176
  97. cartography/intel/aws/organizations.py +40 -19
  98. cartography/intel/aws/permission_relationships.py +144 -68
  99. cartography/intel/aws/rds.py +467 -412
  100. cartography/intel/aws/redshift.py +116 -50
  101. cartography/intel/aws/resourcegroupstaggingapi.py +198 -82
  102. cartography/intel/aws/resources.py +80 -42
  103. cartography/intel/aws/route53.py +419 -318
  104. cartography/intel/aws/s3.py +489 -96
  105. cartography/intel/aws/s3accountpublicaccessblock.py +157 -0
  106. cartography/intel/aws/secretsmanager.py +217 -40
  107. cartography/intel/aws/securityhub.py +23 -10
  108. cartography/intel/aws/sns.py +226 -0
  109. cartography/intel/aws/sqs.py +74 -96
  110. cartography/intel/aws/ssm.py +142 -33
  111. cartography/intel/aws/util/arns.py +7 -7
  112. cartography/intel/aws/util/common.py +31 -4
  113. cartography/intel/azure/__init__.py +259 -46
  114. cartography/intel/azure/aks.py +175 -0
  115. cartography/intel/azure/app_service.py +105 -0
  116. cartography/intel/azure/compute.py +141 -120
  117. cartography/intel/azure/container_instances.py +95 -0
  118. cartography/intel/azure/cosmosdb.py +706 -519
  119. cartography/intel/azure/data_factory.py +85 -0
  120. cartography/intel/azure/data_factory_dataset.py +128 -0
  121. cartography/intel/azure/data_factory_linked_service.py +119 -0
  122. cartography/intel/azure/data_factory_pipeline.py +142 -0
  123. cartography/intel/azure/data_lake.py +124 -0
  124. cartography/intel/azure/event_grid.py +94 -0
  125. cartography/intel/azure/functions.py +124 -0
  126. cartography/intel/azure/load_balancers.py +263 -0
  127. cartography/intel/azure/logic_apps.py +101 -0
  128. cartography/intel/azure/monitor.py +105 -0
  129. cartography/intel/azure/network.py +467 -0
  130. cartography/intel/azure/permission_relationships.py +466 -0
  131. cartography/intel/azure/rbac.py +309 -0
  132. cartography/intel/azure/resource_groups.py +82 -0
  133. cartography/intel/azure/security_center.py +106 -0
  134. cartography/intel/azure/sql.py +436 -392
  135. cartography/intel/azure/storage.py +467 -335
  136. cartography/intel/azure/subscription.py +49 -55
  137. cartography/intel/azure/tenant.py +46 -28
  138. cartography/intel/azure/util/common.py +13 -0
  139. cartography/intel/azure/util/credentials.py +58 -143
  140. cartography/intel/azure/util/tag.py +41 -0
  141. cartography/intel/bigfix/__init__.py +2 -2
  142. cartography/intel/bigfix/computers.py +93 -65
  143. cartography/intel/cloudflare/__init__.py +74 -0
  144. cartography/intel/cloudflare/accounts.py +57 -0
  145. cartography/intel/cloudflare/dnsrecords.py +64 -0
  146. cartography/intel/cloudflare/members.py +75 -0
  147. cartography/intel/cloudflare/roles.py +65 -0
  148. cartography/intel/cloudflare/zones.py +64 -0
  149. cartography/intel/create_indexes.py +5 -3
  150. cartography/intel/crowdstrike/__init__.py +26 -12
  151. cartography/intel/crowdstrike/endpoints.py +17 -45
  152. cartography/intel/crowdstrike/spotlight.py +13 -5
  153. cartography/intel/cve/__init__.py +91 -26
  154. cartography/intel/cve/feed.py +77 -56
  155. cartography/intel/digitalocean/__init__.py +22 -13
  156. cartography/intel/digitalocean/compute.py +75 -108
  157. cartography/intel/digitalocean/management.py +44 -80
  158. cartography/intel/digitalocean/platform.py +48 -43
  159. cartography/intel/dns.py +41 -12
  160. cartography/intel/duo/__init__.py +21 -16
  161. cartography/intel/duo/api_host.py +14 -9
  162. cartography/intel/duo/endpoints.py +50 -45
  163. cartography/intel/duo/groups.py +18 -14
  164. cartography/intel/duo/phones.py +37 -34
  165. cartography/intel/duo/tokens.py +26 -23
  166. cartography/intel/duo/users.py +54 -50
  167. cartography/intel/duo/web_authn_credentials.py +30 -25
  168. cartography/intel/entra/__init__.py +160 -0
  169. cartography/intel/entra/app_role_assignments.py +284 -0
  170. cartography/intel/entra/applications.py +182 -0
  171. cartography/intel/entra/federation/__init__.py +0 -0
  172. cartography/intel/entra/federation/aws_identity_center.py +77 -0
  173. cartography/intel/entra/groups.py +198 -0
  174. cartography/intel/entra/ou.py +136 -0
  175. cartography/intel/entra/service_principals.py +217 -0
  176. cartography/intel/entra/users.py +259 -0
  177. cartography/intel/gcp/__init__.py +381 -175
  178. cartography/intel/gcp/bigtable_app_profile.py +101 -0
  179. cartography/intel/gcp/bigtable_backup.py +91 -0
  180. cartography/intel/gcp/bigtable_cluster.py +93 -0
  181. cartography/intel/gcp/bigtable_instance.py +86 -0
  182. cartography/intel/gcp/bigtable_table.py +87 -0
  183. cartography/intel/gcp/cai.py +292 -0
  184. cartography/intel/gcp/clients.py +112 -0
  185. cartography/intel/gcp/compute.py +521 -325
  186. cartography/intel/gcp/crm/__init__.py +0 -0
  187. cartography/intel/gcp/crm/folders.py +114 -0
  188. cartography/intel/gcp/crm/orgs.py +70 -0
  189. cartography/intel/gcp/crm/projects.py +120 -0
  190. cartography/intel/gcp/dns.py +134 -179
  191. cartography/intel/gcp/gke.py +100 -107
  192. cartography/intel/gcp/iam.py +262 -0
  193. cartography/intel/gcp/permission_relationships.py +394 -0
  194. cartography/intel/gcp/policy_bindings.py +225 -0
  195. cartography/intel/gcp/storage.py +103 -158
  196. cartography/intel/github/__init__.py +66 -27
  197. cartography/intel/github/commits.py +423 -0
  198. cartography/intel/github/repos.py +871 -160
  199. cartography/intel/github/teams.py +386 -53
  200. cartography/intel/github/users.py +214 -49
  201. cartography/intel/github/util.py +50 -35
  202. cartography/intel/googleworkspace/__init__.py +193 -0
  203. cartography/intel/googleworkspace/devices.py +254 -0
  204. cartography/intel/googleworkspace/groups.py +568 -0
  205. cartography/intel/googleworkspace/oauth_apps.py +259 -0
  206. cartography/intel/googleworkspace/tenant.py +85 -0
  207. cartography/intel/googleworkspace/users.py +138 -0
  208. cartography/intel/gsuite/__init__.py +101 -42
  209. cartography/intel/gsuite/groups.py +291 -0
  210. cartography/intel/gsuite/users.py +142 -0
  211. cartography/intel/jamf/__init__.py +19 -1
  212. cartography/intel/jamf/computers.py +37 -8
  213. cartography/intel/jamf/util.py +7 -2
  214. cartography/intel/kandji/__init__.py +6 -3
  215. cartography/intel/kandji/devices.py +40 -10
  216. cartography/intel/keycloak/__init__.py +153 -0
  217. cartography/intel/keycloak/authenticationexecutions.py +322 -0
  218. cartography/intel/keycloak/authenticationflows.py +77 -0
  219. cartography/intel/keycloak/clients.py +187 -0
  220. cartography/intel/keycloak/groups.py +126 -0
  221. cartography/intel/keycloak/identityproviders.py +94 -0
  222. cartography/intel/keycloak/organizations.py +163 -0
  223. cartography/intel/keycloak/realms.py +61 -0
  224. cartography/intel/keycloak/roles.py +202 -0
  225. cartography/intel/keycloak/scopes.py +73 -0
  226. cartography/intel/keycloak/users.py +70 -0
  227. cartography/intel/keycloak/util.py +47 -0
  228. cartography/intel/kubernetes/__init__.py +60 -14
  229. cartography/intel/kubernetes/clusters.py +86 -0
  230. cartography/intel/kubernetes/eks.py +402 -0
  231. cartography/intel/kubernetes/namespaces.py +60 -55
  232. cartography/intel/kubernetes/pods.py +171 -75
  233. cartography/intel/kubernetes/rbac.py +597 -0
  234. cartography/intel/kubernetes/secrets.py +95 -45
  235. cartography/intel/kubernetes/services.py +131 -63
  236. cartography/intel/kubernetes/util.py +142 -14
  237. cartography/intel/lastpass/__init__.py +2 -2
  238. cartography/intel/lastpass/users.py +23 -12
  239. cartography/intel/oci/__init__.py +44 -11
  240. cartography/intel/oci/iam.py +157 -47
  241. cartography/intel/oci/organizations.py +16 -7
  242. cartography/intel/oci/utils.py +71 -25
  243. cartography/intel/okta/__init__.py +66 -15
  244. cartography/intel/okta/applications.py +57 -25
  245. cartography/intel/okta/awssaml.py +105 -41
  246. cartography/intel/okta/factors.py +19 -5
  247. cartography/intel/okta/groups.py +61 -31
  248. cartography/intel/okta/organization.py +8 -2
  249. cartography/intel/okta/origins.py +9 -3
  250. cartography/intel/okta/roles.py +20 -7
  251. cartography/intel/okta/users.py +31 -10
  252. cartography/intel/okta/utils.py +6 -4
  253. cartography/intel/ontology/__init__.py +44 -0
  254. cartography/intel/ontology/devices.py +54 -0
  255. cartography/intel/ontology/users.py +54 -0
  256. cartography/intel/ontology/utils.py +176 -0
  257. cartography/intel/openai/__init__.py +86 -0
  258. cartography/intel/openai/adminapikeys.py +89 -0
  259. cartography/intel/openai/apikeys.py +96 -0
  260. cartography/intel/openai/projects.py +97 -0
  261. cartography/intel/openai/serviceaccounts.py +82 -0
  262. cartography/intel/openai/users.py +75 -0
  263. cartography/intel/openai/util.py +45 -0
  264. cartography/intel/pagerduty/__init__.py +8 -7
  265. cartography/intel/pagerduty/escalation_policies.py +31 -12
  266. cartography/intel/pagerduty/schedules.py +21 -8
  267. cartography/intel/pagerduty/services.py +18 -7
  268. cartography/intel/pagerduty/teams.py +13 -5
  269. cartography/intel/pagerduty/users.py +6 -2
  270. cartography/intel/pagerduty/vendors.py +6 -2
  271. cartography/intel/scaleway/__init__.py +127 -0
  272. cartography/intel/scaleway/iam/__init__.py +0 -0
  273. cartography/intel/scaleway/iam/apikeys.py +71 -0
  274. cartography/intel/scaleway/iam/applications.py +71 -0
  275. cartography/intel/scaleway/iam/groups.py +71 -0
  276. cartography/intel/scaleway/iam/users.py +71 -0
  277. cartography/intel/scaleway/instances/__init__.py +0 -0
  278. cartography/intel/scaleway/instances/flexibleips.py +86 -0
  279. cartography/intel/scaleway/instances/instances.py +92 -0
  280. cartography/intel/scaleway/projects.py +79 -0
  281. cartography/intel/scaleway/storage/__init__.py +0 -0
  282. cartography/intel/scaleway/storage/snapshots.py +86 -0
  283. cartography/intel/scaleway/storage/volumes.py +84 -0
  284. cartography/intel/scaleway/utils.py +37 -0
  285. cartography/intel/semgrep/__init__.py +30 -5
  286. cartography/intel/semgrep/dependencies.py +255 -0
  287. cartography/intel/semgrep/deployment.py +69 -0
  288. cartography/intel/semgrep/findings.py +157 -117
  289. cartography/intel/sentinelone/__init__.py +75 -0
  290. cartography/intel/sentinelone/account.py +140 -0
  291. cartography/intel/sentinelone/agent.py +139 -0
  292. cartography/intel/sentinelone/api.py +124 -0
  293. cartography/intel/sentinelone/application.py +248 -0
  294. cartography/intel/sentinelone/cve.py +119 -0
  295. cartography/intel/sentinelone/utils.py +28 -0
  296. cartography/intel/slack/__init__.py +78 -0
  297. cartography/intel/slack/channels.py +80 -0
  298. cartography/intel/slack/groups.py +90 -0
  299. cartography/intel/slack/teams.py +65 -0
  300. cartography/intel/slack/users.py +57 -0
  301. cartography/intel/slack/utils.py +29 -0
  302. cartography/intel/snipeit/__init__.py +44 -0
  303. cartography/intel/snipeit/asset.py +80 -0
  304. cartography/intel/snipeit/user.py +78 -0
  305. cartography/intel/snipeit/util.py +40 -0
  306. cartography/intel/spacelift/__init__.py +161 -0
  307. cartography/intel/spacelift/account.py +73 -0
  308. cartography/intel/spacelift/ec2_ownership.py +280 -0
  309. cartography/intel/spacelift/runs.py +463 -0
  310. cartography/intel/spacelift/spaces.py +112 -0
  311. cartography/intel/spacelift/stacks.py +119 -0
  312. cartography/intel/spacelift/util.py +122 -0
  313. cartography/intel/spacelift/workerpools.py +131 -0
  314. cartography/intel/spacelift/workers.py +128 -0
  315. cartography/intel/tailscale/__init__.py +77 -0
  316. cartography/intel/tailscale/acls.py +146 -0
  317. cartography/intel/tailscale/devices.py +127 -0
  318. cartography/intel/tailscale/postureintegrations.py +81 -0
  319. cartography/intel/tailscale/tailnets.py +76 -0
  320. cartography/intel/tailscale/users.py +80 -0
  321. cartography/intel/tailscale/utils.py +132 -0
  322. cartography/intel/trivy/__init__.py +272 -0
  323. cartography/intel/trivy/scanner.py +386 -0
  324. cartography/models/airbyte/__init__.py +0 -0
  325. cartography/models/airbyte/connection.py +138 -0
  326. cartography/models/airbyte/destination.py +75 -0
  327. cartography/models/airbyte/organization.py +19 -0
  328. cartography/models/airbyte/source.py +75 -0
  329. cartography/models/airbyte/stream.py +74 -0
  330. cartography/models/airbyte/tag.py +69 -0
  331. cartography/models/airbyte/user.py +115 -0
  332. cartography/models/airbyte/workspace.py +46 -0
  333. cartography/models/anthropic/__init__.py +0 -0
  334. cartography/models/anthropic/apikey.py +94 -0
  335. cartography/models/anthropic/organization.py +19 -0
  336. cartography/models/anthropic/user.py +52 -0
  337. cartography/models/anthropic/workspace.py +90 -0
  338. cartography/models/aws/acm/__init__.py +0 -0
  339. cartography/models/aws/acm/certificate.py +75 -0
  340. cartography/models/aws/apigateway/__init__.py +0 -0
  341. cartography/models/aws/apigateway/apigateway.py +51 -0
  342. cartography/models/aws/apigateway/apigatewaycertificate.py +72 -0
  343. cartography/models/aws/apigateway/apigatewaydeployment.py +74 -0
  344. cartography/models/aws/apigateway/apigatewayintegration.py +79 -0
  345. cartography/models/aws/apigateway/apigatewaymethod.py +74 -0
  346. cartography/models/aws/apigateway/apigatewayresource.py +70 -0
  347. cartography/models/aws/apigateway/apigatewaystage.py +75 -0
  348. cartography/models/aws/apigatewayv2/__init__.py +0 -0
  349. cartography/models/aws/apigatewayv2/apigatewayv2.py +53 -0
  350. cartography/models/aws/cloudtrail/__init__.py +0 -0
  351. cartography/models/aws/cloudtrail/management_events.py +153 -0
  352. cartography/models/aws/cloudtrail/trail.py +106 -0
  353. cartography/models/aws/cloudwatch/__init__.py +0 -0
  354. cartography/models/aws/cloudwatch/log_metric_filter.py +79 -0
  355. cartography/models/aws/cloudwatch/loggroup.py +52 -0
  356. cartography/models/aws/cloudwatch/metric_alarm.py +53 -0
  357. cartography/models/aws/codebuild/__init__.py +0 -0
  358. cartography/models/aws/codebuild/project.py +49 -0
  359. cartography/models/aws/cognito/__init__.py +0 -0
  360. cartography/models/aws/cognito/identity_pool.py +70 -0
  361. cartography/models/aws/cognito/user_pool.py +47 -0
  362. cartography/models/aws/dynamodb/gsi.py +30 -22
  363. cartography/models/aws/dynamodb/tables.py +27 -17
  364. cartography/models/aws/ec2/auto_scaling_groups.py +224 -0
  365. cartography/models/aws/ec2/images.py +36 -34
  366. cartography/models/aws/ec2/instances.py +85 -38
  367. cartography/models/aws/ec2/keypair.py +59 -0
  368. cartography/models/aws/ec2/keypair_instance.py +76 -0
  369. cartography/models/aws/ec2/launch_configurations.py +59 -0
  370. cartography/models/aws/ec2/launch_template_versions.py +48 -38
  371. cartography/models/aws/ec2/launch_templates.py +21 -17
  372. cartography/models/aws/ec2/load_balancer_listeners.py +72 -0
  373. cartography/models/aws/ec2/load_balancers.py +112 -0
  374. cartography/models/aws/ec2/network_acl_rules.py +106 -0
  375. cartography/models/aws/ec2/network_acls.py +95 -0
  376. cartography/models/aws/ec2/networkinterface_instance.py +52 -39
  377. cartography/models/aws/ec2/networkinterfaces.py +57 -37
  378. cartography/models/aws/ec2/privateip_networkinterface.py +32 -22
  379. cartography/models/aws/ec2/reservations.py +18 -14
  380. cartography/models/aws/ec2/route_table_associations.py +97 -0
  381. cartography/models/aws/ec2/route_tables.py +128 -0
  382. cartography/models/aws/ec2/routes.py +85 -0
  383. cartography/models/aws/ec2/security_group_rules.py +109 -0
  384. cartography/models/aws/ec2/security_groups.py +90 -0
  385. cartography/models/aws/ec2/securitygroup_instance.py +29 -20
  386. cartography/models/aws/ec2/securitygroup_networkinterface.py +24 -15
  387. cartography/models/aws/ec2/snapshots.py +58 -0
  388. cartography/models/aws/ec2/subnet_instance.py +26 -19
  389. cartography/models/aws/ec2/subnet_networkinterface.py +42 -31
  390. cartography/models/aws/ec2/subnets.py +65 -0
  391. cartography/models/aws/ec2/volumes.py +67 -40
  392. cartography/models/aws/ec2/vpc.py +46 -0
  393. cartography/models/aws/ec2/vpc_cidr.py +102 -0
  394. cartography/models/aws/ec2/vpc_peering.py +157 -0
  395. cartography/models/aws/ecr/__init__.py +0 -0
  396. cartography/models/aws/ecr/image.py +146 -0
  397. cartography/models/aws/ecr/image_layer.py +107 -0
  398. cartography/models/aws/ecr/repository.py +72 -0
  399. cartography/models/aws/ecr/repository_image.py +95 -0
  400. cartography/models/aws/ecs/__init__.py +0 -0
  401. cartography/models/aws/ecs/clusters.py +64 -0
  402. cartography/models/aws/ecs/container_definitions.py +93 -0
  403. cartography/models/aws/ecs/container_instances.py +84 -0
  404. cartography/models/aws/ecs/containers.py +101 -0
  405. cartography/models/aws/ecs/services.py +134 -0
  406. cartography/models/aws/ecs/task_definitions.py +135 -0
  407. cartography/models/aws/ecs/tasks.py +134 -0
  408. cartography/models/aws/efs/__init__.py +0 -0
  409. cartography/models/aws/efs/access_point.py +77 -0
  410. cartography/models/aws/efs/file_system.py +60 -0
  411. cartography/models/aws/efs/mount_target.py +79 -0
  412. cartography/models/aws/eks/clusters.py +23 -21
  413. cartography/models/aws/elasticache/__init__.py +0 -0
  414. cartography/models/aws/elasticache/cluster.py +65 -0
  415. cartography/models/aws/elasticache/topic.py +67 -0
  416. cartography/models/aws/emr.py +32 -30
  417. cartography/models/aws/eventbridge/__init__.py +0 -0
  418. cartography/models/aws/eventbridge/rule.py +77 -0
  419. cartography/models/aws/eventbridge/target.py +71 -0
  420. cartography/models/aws/glue/__init__.py +0 -0
  421. cartography/models/aws/glue/connection.py +51 -0
  422. cartography/models/aws/glue/job.py +69 -0
  423. cartography/models/aws/guardduty/__init__.py +1 -0
  424. cartography/models/aws/guardduty/detectors.py +50 -0
  425. cartography/models/aws/guardduty/findings.py +121 -0
  426. cartography/models/aws/iam/__init__.py +0 -0
  427. cartography/models/aws/iam/access_key.py +103 -0
  428. cartography/models/aws/iam/account_role.py +24 -0
  429. cartography/models/aws/iam/federated_principal.py +60 -0
  430. cartography/models/aws/iam/group.py +60 -0
  431. cartography/models/aws/iam/group_membership.py +27 -0
  432. cartography/models/aws/iam/inline_policy.py +78 -0
  433. cartography/models/aws/iam/instanceprofile.py +76 -0
  434. cartography/models/aws/iam/managed_policy.py +51 -0
  435. cartography/models/aws/iam/policy_statement.py +57 -0
  436. cartography/models/aws/iam/role.py +83 -0
  437. cartography/models/aws/iam/root_principal.py +52 -0
  438. cartography/models/aws/iam/service_principal.py +30 -0
  439. cartography/models/aws/iam/sts_assumerole_allow.py +38 -0
  440. cartography/models/aws/iam/user.py +59 -0
  441. cartography/models/aws/identitycenter/__init__.py +0 -0
  442. cartography/models/aws/identitycenter/awsidentitycenter.py +49 -0
  443. cartography/models/aws/identitycenter/awspermissionset.py +162 -0
  444. cartography/models/aws/identitycenter/awssogroup.py +70 -0
  445. cartography/models/aws/identitycenter/awsssouser.py +110 -0
  446. cartography/models/aws/inspector/findings.py +124 -58
  447. cartography/models/aws/inspector/packages.py +18 -42
  448. cartography/models/aws/kms/__init__.py +0 -0
  449. cartography/models/aws/kms/aliases.py +86 -0
  450. cartography/models/aws/kms/grants.py +65 -0
  451. cartography/models/aws/kms/keys.py +88 -0
  452. cartography/models/aws/lambda_function/__init__.py +0 -0
  453. cartography/models/aws/lambda_function/alias.py +74 -0
  454. cartography/models/aws/lambda_function/event_source_mapping.py +88 -0
  455. cartography/models/aws/lambda_function/lambda_function.py +91 -0
  456. cartography/models/aws/lambda_function/layer.py +72 -0
  457. cartography/models/aws/rds/__init__.py +0 -0
  458. cartography/models/aws/rds/cluster.py +91 -0
  459. cartography/models/aws/rds/event_subscription.py +146 -0
  460. cartography/models/aws/rds/instance.py +156 -0
  461. cartography/models/aws/rds/snapshot.py +108 -0
  462. cartography/models/aws/rds/subnet_group.py +101 -0
  463. cartography/models/aws/route53/__init__.py +0 -0
  464. cartography/models/aws/route53/dnsrecord.py +235 -0
  465. cartography/models/aws/route53/nameserver.py +63 -0
  466. cartography/models/aws/route53/subzone.py +40 -0
  467. cartography/models/aws/route53/zone.py +47 -0
  468. cartography/models/aws/s3/__init__.py +0 -0
  469. cartography/models/aws/s3/account_public_access_block.py +51 -0
  470. cartography/models/aws/s3/notification.py +24 -0
  471. cartography/models/aws/secretsmanager/__init__.py +0 -0
  472. cartography/models/aws/secretsmanager/secret.py +106 -0
  473. cartography/models/aws/secretsmanager/secret_version.py +114 -0
  474. cartography/models/aws/sns/__init__.py +0 -0
  475. cartography/models/aws/sns/topic.py +50 -0
  476. cartography/models/aws/sns/topic_subscription.py +74 -0
  477. cartography/models/aws/sqs/__init__.py +0 -0
  478. cartography/models/aws/sqs/queue.py +89 -0
  479. cartography/models/aws/ssm/instance_information.py +51 -39
  480. cartography/models/aws/ssm/instance_patch.py +32 -26
  481. cartography/models/aws/ssm/parameters.py +84 -0
  482. cartography/models/azure/__init__.py +0 -0
  483. cartography/models/azure/aks_cluster.py +54 -0
  484. cartography/models/azure/aks_nodepool.py +54 -0
  485. cartography/models/azure/app_service.py +59 -0
  486. cartography/models/azure/container_instance.py +57 -0
  487. cartography/models/azure/cosmosdb/__init__.py +0 -0
  488. cartography/models/azure/cosmosdb/account.py +77 -0
  489. cartography/models/azure/cosmosdb/accountfailoverpolicy.py +77 -0
  490. cartography/models/azure/cosmosdb/cassandrakeyspace.py +82 -0
  491. cartography/models/azure/cosmosdb/cassandratable.py +81 -0
  492. cartography/models/azure/cosmosdb/corspolicy.py +74 -0
  493. cartography/models/azure/cosmosdb/dblocation.py +120 -0
  494. cartography/models/azure/cosmosdb/mongodbcollection.py +82 -0
  495. cartography/models/azure/cosmosdb/mongodbdatabase.py +78 -0
  496. cartography/models/azure/cosmosdb/privateendpointconnection.py +81 -0
  497. cartography/models/azure/cosmosdb/sqlcontainer.py +88 -0
  498. cartography/models/azure/cosmosdb/sqldatabase.py +78 -0
  499. cartography/models/azure/cosmosdb/tableresource.py +76 -0
  500. cartography/models/azure/cosmosdb/virtualnetworkrule.py +78 -0
  501. cartography/models/azure/data_factory/__init__.py +0 -0
  502. cartography/models/azure/data_factory/data_factory.py +51 -0
  503. cartography/models/azure/data_factory/data_factory_dataset.py +94 -0
  504. cartography/models/azure/data_factory/data_factory_linked_service.py +78 -0
  505. cartography/models/azure/data_factory/data_factory_pipeline.py +93 -0
  506. cartography/models/azure/data_lake_filesystem.py +51 -0
  507. cartography/models/azure/event_grid_topic.py +57 -0
  508. cartography/models/azure/function_app.py +59 -0
  509. cartography/models/azure/load_balancer/__init__.py +0 -0
  510. cartography/models/azure/load_balancer/load_balancer.py +49 -0
  511. cartography/models/azure/load_balancer/load_balancer_backend_pool.py +73 -0
  512. cartography/models/azure/load_balancer/load_balancer_frontend_ip.py +75 -0
  513. cartography/models/azure/load_balancer/load_balancer_inbound_nat_rule.py +78 -0
  514. cartography/models/azure/load_balancer/load_balancer_rule.py +108 -0
  515. cartography/models/azure/logic_apps.py +56 -0
  516. cartography/models/azure/monitor.py +54 -0
  517. cartography/models/azure/network_interface.py +112 -0
  518. cartography/models/azure/network_security_group.py +50 -0
  519. cartography/models/azure/permission_relationships.py +60 -0
  520. cartography/models/azure/principal.py +41 -0
  521. cartography/models/azure/public_ip_address.py +50 -0
  522. cartography/models/azure/rbac.py +268 -0
  523. cartography/models/azure/resource_groups.py +52 -0
  524. cartography/models/azure/security_center.py +50 -0
  525. cartography/models/azure/sql/__init__.py +0 -0
  526. cartography/models/azure/sql/databasethreatdetectionpolicy.py +85 -0
  527. cartography/models/azure/sql/elasticpool.py +77 -0
  528. cartography/models/azure/sql/failovergroup.py +73 -0
  529. cartography/models/azure/sql/recoverabledatabase.py +75 -0
  530. cartography/models/azure/sql/replicationlink.py +81 -0
  531. cartography/models/azure/sql/restorabledroppeddatabase.py +82 -0
  532. cartography/models/azure/sql/restorepoint.py +74 -0
  533. cartography/models/azure/sql/serveradadministrator.py +74 -0
  534. cartography/models/azure/sql/serverdnsalias.py +71 -0
  535. cartography/models/azure/sql/sqldatabase.py +85 -0
  536. cartography/models/azure/sql/sqlserver.py +50 -0
  537. cartography/models/azure/sql/transparentdataencryption.py +76 -0
  538. cartography/models/azure/storage/__init__.py +0 -0
  539. cartography/models/azure/storage/account.py +59 -0
  540. cartography/models/azure/storage/blobcontainer.py +85 -0
  541. cartography/models/azure/storage/blobservice.py +71 -0
  542. cartography/models/azure/storage/fileservice.py +71 -0
  543. cartography/models/azure/storage/fileshare.py +82 -0
  544. cartography/models/azure/storage/queue.py +71 -0
  545. cartography/models/azure/storage/queueservice.py +73 -0
  546. cartography/models/azure/storage/table.py +72 -0
  547. cartography/models/azure/storage/tableservice.py +73 -0
  548. cartography/models/azure/subnet.py +101 -0
  549. cartography/models/azure/subscription.py +47 -0
  550. cartography/models/azure/tags/__init__.py +0 -0
  551. cartography/models/azure/tags/storage_tag.py +40 -0
  552. cartography/models/azure/tags/tag.py +37 -0
  553. cartography/models/azure/tenant.py +17 -0
  554. cartography/models/azure/virtual_network.py +49 -0
  555. cartography/models/azure/vm/__init__.py +0 -0
  556. cartography/models/azure/vm/datadisk.py +80 -0
  557. cartography/models/azure/vm/disk.py +55 -0
  558. cartography/models/azure/vm/snapshot.py +56 -0
  559. cartography/models/azure/vm/virtualmachine.py +59 -0
  560. cartography/models/bigfix/bigfix_computer.py +42 -38
  561. cartography/models/bigfix/bigfix_root.py +3 -3
  562. cartography/models/cloudflare/__init__.py +0 -0
  563. cartography/models/cloudflare/account.py +25 -0
  564. cartography/models/cloudflare/dnsrecord.py +55 -0
  565. cartography/models/cloudflare/member.py +86 -0
  566. cartography/models/cloudflare/role.py +44 -0
  567. cartography/models/cloudflare/zone.py +59 -0
  568. cartography/models/core/common.py +53 -2
  569. cartography/models/core/nodes.py +20 -4
  570. cartography/models/core/relationships.py +58 -6
  571. cartography/models/crowdstrike/__init__.py +0 -0
  572. cartography/models/crowdstrike/hosts.py +51 -0
  573. cartography/models/cve/cve.py +34 -32
  574. cartography/models/cve/cve_feed.py +6 -6
  575. cartography/models/digitalocean/__init__.py +0 -0
  576. cartography/models/digitalocean/account.py +21 -0
  577. cartography/models/digitalocean/droplet.py +58 -0
  578. cartography/models/digitalocean/project.py +48 -0
  579. cartography/models/duo/api_host.py +3 -3
  580. cartography/models/duo/endpoint.py +43 -41
  581. cartography/models/duo/group.py +14 -14
  582. cartography/models/duo/phone.py +27 -27
  583. cartography/models/duo/token.py +16 -16
  584. cartography/models/duo/user.py +50 -44
  585. cartography/models/duo/web_authn_credential.py +27 -19
  586. cartography/models/entra/__init__.py +0 -0
  587. cartography/models/entra/app_role_assignment.py +115 -0
  588. cartography/models/entra/application.py +49 -0
  589. cartography/models/entra/entra_user_to_aws_sso.py +41 -0
  590. cartography/models/entra/group.py +117 -0
  591. cartography/models/entra/ou.py +48 -0
  592. cartography/models/entra/service_principal.py +104 -0
  593. cartography/models/entra/tenant.py +39 -0
  594. cartography/models/entra/user.py +90 -0
  595. cartography/models/gcp/__init__.py +0 -0
  596. cartography/models/gcp/bigtable/__init__.py +0 -0
  597. cartography/models/gcp/bigtable/app_profile.py +94 -0
  598. cartography/models/gcp/bigtable/backup.py +91 -0
  599. cartography/models/gcp/bigtable/cluster.py +73 -0
  600. cartography/models/gcp/bigtable/instance.py +52 -0
  601. cartography/models/gcp/bigtable/table.py +69 -0
  602. cartography/models/gcp/compute/__init__.py +0 -0
  603. cartography/models/gcp/compute/subnet.py +74 -0
  604. cartography/models/gcp/compute/vpc.py +50 -0
  605. cartography/models/gcp/crm/__init__.py +0 -0
  606. cartography/models/gcp/crm/folders.py +98 -0
  607. cartography/models/gcp/crm/organizations.py +21 -0
  608. cartography/models/gcp/crm/projects.py +100 -0
  609. cartography/models/gcp/dns.py +109 -0
  610. cartography/models/gcp/gke.py +69 -0
  611. cartography/models/gcp/iam.py +73 -0
  612. cartography/models/gcp/permission_relationships.py +61 -0
  613. cartography/models/gcp/policy_bindings.py +93 -0
  614. cartography/models/gcp/storage/__init__.py +0 -0
  615. cartography/models/gcp/storage/bucket.py +119 -0
  616. cartography/models/github/commits.py +63 -0
  617. cartography/models/github/dependencies.py +73 -0
  618. cartography/models/github/manifests.py +49 -0
  619. cartography/models/github/orgs.py +27 -0
  620. cartography/models/github/teams.py +74 -22
  621. cartography/models/github/users.py +149 -0
  622. cartography/models/googleworkspace/__init__.py +0 -0
  623. cartography/models/googleworkspace/device.py +132 -0
  624. cartography/models/googleworkspace/group.py +382 -0
  625. cartography/models/googleworkspace/oauth_app.py +124 -0
  626. cartography/models/googleworkspace/tenant.py +30 -0
  627. cartography/models/googleworkspace/user.py +113 -0
  628. cartography/models/gsuite/__init__.py +0 -0
  629. cartography/models/gsuite/group.py +218 -0
  630. cartography/models/gsuite/tenant.py +29 -0
  631. cartography/models/gsuite/user.py +107 -0
  632. cartography/models/kandji/device.py +22 -17
  633. cartography/models/kandji/tenant.py +6 -4
  634. cartography/models/keycloak/__init__.py +0 -0
  635. cartography/models/keycloak/authenticationexecution.py +160 -0
  636. cartography/models/keycloak/authenticationflow.py +54 -0
  637. cartography/models/keycloak/client.py +179 -0
  638. cartography/models/keycloak/group.py +101 -0
  639. cartography/models/keycloak/identityprovider.py +89 -0
  640. cartography/models/keycloak/organization.py +116 -0
  641. cartography/models/keycloak/organizationdomain.py +73 -0
  642. cartography/models/keycloak/realm.py +173 -0
  643. cartography/models/keycloak/role.py +126 -0
  644. cartography/models/keycloak/scope.py +73 -0
  645. cartography/models/keycloak/user.py +55 -0
  646. cartography/models/kubernetes/__init__.py +0 -0
  647. cartography/models/kubernetes/clusterrolebindings.py +138 -0
  648. cartography/models/kubernetes/clusterroles.py +52 -0
  649. cartography/models/kubernetes/clusters.py +26 -0
  650. cartography/models/kubernetes/containers.py +133 -0
  651. cartography/models/kubernetes/groups.py +107 -0
  652. cartography/models/kubernetes/namespaces.py +51 -0
  653. cartography/models/kubernetes/oidc.py +51 -0
  654. cartography/models/kubernetes/pods.py +80 -0
  655. cartography/models/kubernetes/rolebindings.py +159 -0
  656. cartography/models/kubernetes/roles.py +76 -0
  657. cartography/models/kubernetes/secrets.py +79 -0
  658. cartography/models/kubernetes/serviceaccounts.py +77 -0
  659. cartography/models/kubernetes/services.py +108 -0
  660. cartography/models/kubernetes/users.py +105 -0
  661. cartography/models/lastpass/tenant.py +3 -3
  662. cartography/models/lastpass/user.py +36 -28
  663. cartography/models/ontology/__init__.py +0 -0
  664. cartography/models/ontology/device.py +137 -0
  665. cartography/models/ontology/mapping/__init__.py +76 -0
  666. cartography/models/ontology/mapping/data/__init__.py +0 -0
  667. cartography/models/ontology/mapping/data/apikeys.py +93 -0
  668. cartography/models/ontology/mapping/data/computeinstance.py +95 -0
  669. cartography/models/ontology/mapping/data/containers.py +88 -0
  670. cartography/models/ontology/mapping/data/databases.py +182 -0
  671. cartography/models/ontology/mapping/data/devices.py +194 -0
  672. cartography/models/ontology/mapping/data/thirdpartyapps.py +140 -0
  673. cartography/models/ontology/mapping/data/useraccounts.py +416 -0
  674. cartography/models/ontology/mapping/data/users.py +63 -0
  675. cartography/models/ontology/mapping/specs.py +85 -0
  676. cartography/models/ontology/user.py +51 -0
  677. cartography/models/openai/__init__.py +0 -0
  678. cartography/models/openai/adminapikey.py +94 -0
  679. cartography/models/openai/apikey.py +88 -0
  680. cartography/models/openai/organization.py +17 -0
  681. cartography/models/openai/project.py +89 -0
  682. cartography/models/openai/serviceaccount.py +50 -0
  683. cartography/models/openai/user.py +53 -0
  684. cartography/models/scaleway/__init__.py +0 -0
  685. cartography/models/scaleway/iam/__init__.py +0 -0
  686. cartography/models/scaleway/iam/apikey.py +100 -0
  687. cartography/models/scaleway/iam/application.py +52 -0
  688. cartography/models/scaleway/iam/group.py +95 -0
  689. cartography/models/scaleway/iam/user.py +64 -0
  690. cartography/models/scaleway/instance/__init__.py +0 -0
  691. cartography/models/scaleway/instance/flexibleip.py +52 -0
  692. cartography/models/scaleway/instance/instance.py +120 -0
  693. cartography/models/scaleway/organization.py +19 -0
  694. cartography/models/scaleway/project.py +48 -0
  695. cartography/models/scaleway/storage/__init__.py +0 -0
  696. cartography/models/scaleway/storage/snapshot.py +78 -0
  697. cartography/models/scaleway/storage/volume.py +51 -0
  698. cartography/models/semgrep/dependencies.py +102 -0
  699. cartography/models/semgrep/deployment.py +5 -5
  700. cartography/models/semgrep/findings.py +58 -40
  701. cartography/models/semgrep/locations.py +27 -21
  702. cartography/models/sentinelone/__init__.py +1 -0
  703. cartography/models/sentinelone/account.py +40 -0
  704. cartography/models/sentinelone/agent.py +50 -0
  705. cartography/models/sentinelone/application.py +44 -0
  706. cartography/models/sentinelone/application_version.py +96 -0
  707. cartography/models/sentinelone/cve.py +73 -0
  708. cartography/models/slack/__init__.py +0 -0
  709. cartography/models/slack/channels.py +92 -0
  710. cartography/models/slack/group.py +129 -0
  711. cartography/models/slack/team.py +22 -0
  712. cartography/models/slack/user.py +62 -0
  713. cartography/models/snipeit/__init__.py +0 -0
  714. cartography/models/snipeit/asset.py +92 -0
  715. cartography/models/snipeit/tenant.py +19 -0
  716. cartography/models/snipeit/user.py +60 -0
  717. cartography/models/spacelift/__init__.py +0 -0
  718. cartography/models/spacelift/cloudtrailevent.py +120 -0
  719. cartography/models/spacelift/run.py +162 -0
  720. cartography/models/spacelift/space.py +131 -0
  721. cartography/models/spacelift/spaceliftaccount.py +31 -0
  722. cartography/models/spacelift/spaceliftgitcommit.py +157 -0
  723. cartography/models/spacelift/stack.py +96 -0
  724. cartography/models/spacelift/user.py +63 -0
  725. cartography/models/spacelift/worker.py +97 -0
  726. cartography/models/spacelift/workerpool.py +90 -0
  727. cartography/models/tailscale/__init__.py +0 -0
  728. cartography/models/tailscale/device.py +96 -0
  729. cartography/models/tailscale/group.py +86 -0
  730. cartography/models/tailscale/postureintegration.py +58 -0
  731. cartography/models/tailscale/tag.py +102 -0
  732. cartography/models/tailscale/tailnet.py +29 -0
  733. cartography/models/tailscale/user.py +57 -0
  734. cartography/models/trivy/__init__.py +0 -0
  735. cartography/models/trivy/findings.py +66 -0
  736. cartography/models/trivy/fix.py +66 -0
  737. cartography/models/trivy/package.py +71 -0
  738. cartography/rules/README.md +1 -0
  739. cartography/rules/__init__.py +0 -0
  740. cartography/rules/cli.py +261 -0
  741. cartography/rules/data/__init__.py +0 -0
  742. cartography/rules/data/rules/__init__.py +46 -0
  743. cartography/rules/data/rules/cloud_security_product_deactivated.py +49 -0
  744. cartography/rules/data/rules/compute_instance_exposed.py +51 -0
  745. cartography/rules/data/rules/database_instance_exposed.py +53 -0
  746. cartography/rules/data/rules/delegation_boundary_modifiable.py +90 -0
  747. cartography/rules/data/rules/identity_administration_privileges.py +100 -0
  748. cartography/rules/data/rules/inactive_user_active_accounts.py +48 -0
  749. cartography/rules/data/rules/malicious_npm_dependencies_shai_hulud.py +2222 -0
  750. cartography/rules/data/rules/mfa_missing.py +46 -0
  751. cartography/rules/data/rules/object_storage_public.py +100 -0
  752. cartography/rules/data/rules/policy_administration_privileges.py +104 -0
  753. cartography/rules/data/rules/unmanaged_accounts.py +43 -0
  754. cartography/rules/data/rules/workload_identity_admin_capabilities.py +193 -0
  755. cartography/rules/formatters.py +108 -0
  756. cartography/rules/runners.py +216 -0
  757. cartography/rules/spec/__init__.py +0 -0
  758. cartography/rules/spec/model.py +267 -0
  759. cartography/rules/spec/result.py +38 -0
  760. cartography/stats.py +4 -4
  761. cartography/sync.py +137 -31
  762. cartography/util.py +187 -77
  763. cartography-0.123.0.dist-info/METADATA +230 -0
  764. cartography-0.123.0.dist-info/RECORD +856 -0
  765. {cartography-0.93.0rc1.dist-info → cartography-0.123.0.dist-info}/WHEEL +1 -1
  766. {cartography-0.93.0rc1.dist-info → cartography-0.123.0.dist-info}/entry_points.txt +1 -0
  767. {cartography-0.93.0rc1.dist-info → cartography-0.123.0.dist-info/licenses}/LICENSE +1 -1
  768. cartography/data/jobs/analysis/aws_ec2_iaminstance.json +0 -10
  769. cartography/data/jobs/analysis/aws_ec2_iaminstanceprofile.json +0 -10
  770. cartography/data/jobs/cleanup/aws_apigateway_details.json +0 -10
  771. cartography/data/jobs/cleanup/aws_dns_cleanup.json +0 -65
  772. cartography/data/jobs/cleanup/aws_import_account_access_key_cleanup.json +0 -17
  773. cartography/data/jobs/cleanup/aws_import_apigateway_cleanup.json +0 -45
  774. cartography/data/jobs/cleanup/aws_import_ec2_security_groupinfo_cleanup.json +0 -24
  775. cartography/data/jobs/cleanup/aws_import_groups_cleanup.json +0 -13
  776. cartography/data/jobs/cleanup/aws_import_lambda_cleanup.json +0 -50
  777. cartography/data/jobs/cleanup/aws_import_principals_cleanup.json +0 -30
  778. cartography/data/jobs/cleanup/aws_import_rds_clusters_cleanup.json +0 -23
  779. cartography/data/jobs/cleanup/aws_import_rds_instances_cleanup.json +0 -47
  780. cartography/data/jobs/cleanup/aws_import_rds_snapshots_cleanup.json +0 -23
  781. cartography/data/jobs/cleanup/aws_import_roles_cleanup.json +0 -13
  782. cartography/data/jobs/cleanup/aws_import_secrets_cleanup.json +0 -8
  783. cartography/data/jobs/cleanup/aws_import_snapshots_cleanup.json +0 -30
  784. cartography/data/jobs/cleanup/aws_import_users_cleanup.json +0 -8
  785. cartography/data/jobs/cleanup/aws_import_vpc_cleanup.json +0 -23
  786. cartography/data/jobs/cleanup/aws_import_vpc_peering_cleanup.json +0 -45
  787. cartography/data/jobs/cleanup/aws_kms_details.json +0 -10
  788. cartography/data/jobs/cleanup/azure_cosmosdb_cassandra_keyspace_cleanup.json +0 -25
  789. cartography/data/jobs/cleanup/azure_cosmosdb_cors_details.json +0 -15
  790. cartography/data/jobs/cleanup/azure_cosmosdb_mongodb_database_cleanup.json +0 -25
  791. cartography/data/jobs/cleanup/azure_cosmosdb_sql_database_cleanup.json +0 -25
  792. cartography/data/jobs/cleanup/azure_cosmosdb_table_resources_cleanup.json +0 -15
  793. cartography/data/jobs/cleanup/azure_database_account_cleanup.json +0 -85
  794. cartography/data/jobs/cleanup/azure_import_disks_cleanup.json +0 -15
  795. cartography/data/jobs/cleanup/azure_import_snapshots_cleanup.json +0 -15
  796. cartography/data/jobs/cleanup/azure_import_virtual_machines_cleanup.json +0 -25
  797. cartography/data/jobs/cleanup/azure_sql_server_cleanup.json +0 -125
  798. cartography/data/jobs/cleanup/azure_storage_account_cleanup.json +0 -95
  799. cartography/data/jobs/cleanup/azure_subscriptions_cleanup.json +0 -14
  800. cartography/data/jobs/cleanup/azure_tenant_cleanup.json +0 -9
  801. cartography/data/jobs/cleanup/crxcavator_import_cleanup.json +0 -18
  802. cartography/data/jobs/cleanup/gcp_compute_vpc_subnet_cleanup.json +0 -35
  803. cartography/data/jobs/cleanup/gcp_crm_folder_cleanup.json +0 -23
  804. cartography/data/jobs/cleanup/gcp_crm_organization_cleanup.json +0 -17
  805. cartography/data/jobs/cleanup/gcp_crm_project_cleanup.json +0 -23
  806. cartography/data/jobs/cleanup/gcp_dns_cleanup.json +0 -29
  807. cartography/data/jobs/cleanup/gcp_gke_cluster_cleanup.json +0 -17
  808. cartography/data/jobs/cleanup/gcp_storage_bucket_cleanup.json +0 -29
  809. cartography/data/jobs/cleanup/github_users_cleanup.json +0 -23
  810. cartography/data/jobs/cleanup/gsuite_ingest_groups_cleanup.json +0 -23
  811. cartography/data/jobs/cleanup/gsuite_ingest_users_cleanup.json +0 -11
  812. cartography/data/jobs/cleanup/kubernetes_import_cleanup.json +0 -70
  813. cartography/intel/crxcavator/__init__.py +0 -44
  814. cartography/intel/crxcavator/crxcavator.py +0 -329
  815. cartography/intel/gcp/crm.py +0 -302
  816. cartography/intel/gsuite/api.py +0 -284
  817. cartography/models/aws/ec2/keypairs.py +0 -64
  818. cartography-0.93.0rc1.dist-info/METADATA +0 -55
  819. cartography-0.93.0rc1.dist-info/NOTICE +0 -4
  820. cartography-0.93.0rc1.dist-info/RECORD +0 -341
  821. /cartography/data/jobs/{analysis → scoped_analysis}/aws_s3acl_analysis.json +0 -0
  822. {cartography-0.93.0rc1.dist-info → cartography-0.123.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,7 @@
1
1
  import enum
2
2
  import json
3
3
  import logging
4
+ from collections import namedtuple
4
5
  from typing import Any
5
6
  from typing import Dict
6
7
  from typing import List
@@ -9,11 +10,27 @@ from typing import Tuple
9
10
  import boto3
10
11
  import neo4j
11
12
 
12
- from cartography.intel.aws.permission_relationships import parse_statement_node
13
+ from cartography.client.core.tx import load
14
+ from cartography.client.core.tx import load_matchlinks
15
+ from cartography.client.core.tx import read_list_of_dicts_tx
16
+ from cartography.client.core.tx import read_list_of_values_tx
17
+ from cartography.graph.job import GraphJob
13
18
  from cartography.intel.aws.permission_relationships import principal_allowed_on_resource
19
+ from cartography.models.aws.iam.access_key import AccountAccessKeySchema
20
+ from cartography.models.aws.iam.account_role import AWSAccountAWSRoleSchema
21
+ from cartography.models.aws.iam.federated_principal import AWSFederatedPrincipalSchema
22
+ from cartography.models.aws.iam.group import AWSGroupSchema
23
+ from cartography.models.aws.iam.inline_policy import AWSInlinePolicySchema
24
+ from cartography.models.aws.iam.managed_policy import AWSManagedPolicySchema
25
+ from cartography.models.aws.iam.policy_statement import AWSPolicyStatementSchema
26
+ from cartography.models.aws.iam.role import AWSRoleSchema
27
+ from cartography.models.aws.iam.root_principal import AWSRootPrincipalSchema
28
+ from cartography.models.aws.iam.service_principal import AWSServicePrincipalSchema
29
+ from cartography.models.aws.iam.sts_assumerole_allow import STSAssumeRoleAllowMatchLink
30
+ from cartography.models.aws.iam.user import AWSUserSchema
14
31
  from cartography.stats import get_stats_client
32
+ from cartography.util import aws_handle_regions
15
33
  from cartography.util import merge_module_sync_metadata
16
- from cartography.util import run_cleanup_job
17
34
  from cartography.util import timeit
18
35
 
19
36
  logger = logging.getLogger(__name__)
@@ -24,8 +41,28 @@ stat_handler = get_stats_client(__name__)
24
41
 
25
42
 
26
43
  class PolicyType(enum.Enum):
27
- managed = 'managed'
28
- inline = 'inline'
44
+ managed = "managed"
45
+ inline = "inline"
46
+
47
+
48
+ TransformedRoleData = namedtuple(
49
+ "TransformedRoleData",
50
+ [
51
+ "role_data",
52
+ "federated_principals",
53
+ "service_principals",
54
+ "external_aws_accounts",
55
+ ],
56
+ )
57
+
58
+ TransformedPolicyData = namedtuple(
59
+ "TransformedPolicyData",
60
+ [
61
+ "managed_policies",
62
+ "inline_policies",
63
+ "statements_by_policy_id",
64
+ ],
65
+ )
29
66
 
30
67
 
31
68
  def get_policy_name_from_arn(arn: str) -> str:
@@ -33,50 +70,69 @@ def get_policy_name_from_arn(arn: str) -> str:
33
70
 
34
71
 
35
72
  @timeit
36
- def get_group_policies(boto3_session: boto3.session.Session, group_name: str) -> Dict:
37
- client = boto3_session.client('iam')
38
- paginator = client.get_paginator('list_group_policies')
73
+ def get_group_policies(boto3_session: boto3.Session, group_name: str) -> Dict:
74
+ client = boto3_session.client("iam")
75
+ paginator = client.get_paginator("list_group_policies")
39
76
  policy_names: List[Dict] = []
40
77
  for page in paginator.paginate(GroupName=group_name):
41
- policy_names.extend(page['PolicyNames'])
42
- return {'PolicyNames': policy_names}
78
+ policy_names.extend(page["PolicyNames"])
79
+ return {"PolicyNames": policy_names}
43
80
 
44
81
 
45
82
  @timeit
46
83
  def get_group_policy_info(
47
- boto3_session: boto3.session.Session, group_name: str, policy_name: str,
84
+ boto3_session: boto3.Session,
85
+ group_name: str,
86
+ policy_name: str,
48
87
  ) -> Any:
49
- client = boto3_session.client('iam')
88
+ client = boto3_session.client("iam")
50
89
  return client.get_group_policy(GroupName=group_name, PolicyName=policy_name)
51
90
 
52
91
 
53
92
  @timeit
54
- def get_group_membership_data(boto3_session: boto3.session.Session, group_name: str) -> Dict:
55
- client = boto3_session.client('iam')
93
+ def get_group_membership_data(
94
+ boto3_session: boto3.Session,
95
+ group_name: str,
96
+ ) -> Dict:
97
+ client = boto3_session.client("iam")
56
98
  try:
57
99
  memberships = client.get_group(GroupName=group_name)
58
100
  return memberships
59
101
  except client.exceptions.NoSuchEntityException:
60
102
  # Avoid crashing the sync
61
- logger.warning("client.get_group(GroupName='%s') failed with NoSuchEntityException; skipping.", group_name)
103
+ logger.warning(
104
+ "client.get_group(GroupName='%s') failed with NoSuchEntityException; skipping.",
105
+ group_name,
106
+ )
62
107
  return {}
63
108
 
64
109
 
65
110
  @timeit
66
- def get_group_policy_data(boto3_session: boto3.session.Session, group_list: List[Dict]) -> Dict:
67
- resource_client = boto3_session.resource('iam')
111
+ @aws_handle_regions
112
+ def get_group_policy_data(
113
+ boto3_session: boto3.Session,
114
+ group_list: List[Dict],
115
+ ) -> Dict:
116
+ resource_client = boto3_session.resource("iam")
68
117
  policies = {}
69
118
  for group in group_list:
70
119
  name = group["GroupName"]
71
120
  arn = group["Arn"]
72
121
  resource_group = resource_client.Group(name)
73
- policies[arn] = policies[arn] = {p.name: p.policy_document["Statement"] for p in resource_group.policies.all()}
122
+ policies[arn] = policies[arn] = {
123
+ p.name: p.policy_document["Statement"]
124
+ for p in resource_group.policies.all()
125
+ }
74
126
  return policies
75
127
 
76
128
 
77
129
  @timeit
78
- def get_group_managed_policy_data(boto3_session: boto3.session.Session, group_list: List[Dict]) -> Dict:
79
- resource_client = boto3_session.resource('iam')
130
+ @aws_handle_regions
131
+ def get_group_managed_policy_data(
132
+ boto3_session: boto3.Session,
133
+ group_list: List[Dict],
134
+ ) -> Dict:
135
+ resource_client = boto3_session.resource("iam")
80
136
  policies = {}
81
137
  for group in group_list:
82
138
  name = group["GroupName"]
@@ -90,15 +146,22 @@ def get_group_managed_policy_data(boto3_session: boto3.session.Session, group_li
90
146
 
91
147
 
92
148
  @timeit
93
- def get_user_policy_data(boto3_session: boto3.session.Session, user_list: List[Dict]) -> Dict:
94
- resource_client = boto3_session.resource('iam')
149
+ @aws_handle_regions
150
+ def get_user_policy_data(
151
+ boto3_session: boto3.Session,
152
+ user_list: List[Dict],
153
+ ) -> Dict:
154
+ resource_client = boto3_session.resource("iam")
95
155
  policies = {}
96
156
  for user in user_list:
97
157
  name = user["UserName"]
98
158
  arn = user["Arn"]
99
159
  resource_user = resource_client.User(name)
100
160
  try:
101
- policies[arn] = {p.name: p.policy_document["Statement"] for p in resource_user.policies.all()}
161
+ policies[arn] = {
162
+ p.name: p.policy_document["Statement"]
163
+ for p in resource_user.policies.all()
164
+ }
102
165
  except resource_client.meta.client.exceptions.NoSuchEntityException:
103
166
  logger.warning(
104
167
  f"Could not get policies for user {name} due to NoSuchEntityException; skipping.",
@@ -107,8 +170,12 @@ def get_user_policy_data(boto3_session: boto3.session.Session, user_list: List[D
107
170
 
108
171
 
109
172
  @timeit
110
- def get_user_managed_policy_data(boto3_session: boto3.session.Session, user_list: List[Dict]) -> Dict:
111
- resource_client = boto3_session.resource('iam')
173
+ @aws_handle_regions
174
+ def get_user_managed_policy_data(
175
+ boto3_session: boto3.Session,
176
+ user_list: List[Dict],
177
+ ) -> Dict:
178
+ resource_client = boto3_session.resource("iam")
112
179
  policies = {}
113
180
  for user in user_list:
114
181
  name = user["UserName"]
@@ -127,15 +194,22 @@ def get_user_managed_policy_data(boto3_session: boto3.session.Session, user_list
127
194
 
128
195
 
129
196
  @timeit
130
- def get_role_policy_data(boto3_session: boto3.session.Session, role_list: List[Dict]) -> Dict:
131
- resource_client = boto3_session.resource('iam')
197
+ @aws_handle_regions
198
+ def get_role_policy_data(
199
+ boto3_session: boto3.Session,
200
+ role_list: List[Dict],
201
+ ) -> Dict:
202
+ resource_client = boto3_session.resource("iam")
132
203
  policies = {}
133
204
  for role in role_list:
134
205
  name = role["RoleName"]
135
206
  arn = role["Arn"]
136
207
  resource_role = resource_client.Role(name)
137
208
  try:
138
- policies[arn] = {p.name: p.policy_document["Statement"] for p in resource_role.policies.all()}
209
+ policies[arn] = {
210
+ p.name: p.policy_document["Statement"]
211
+ for p in resource_role.policies.all()
212
+ }
139
213
  except resource_client.meta.client.exceptions.NoSuchEntityException:
140
214
  logger.warning(
141
215
  f"Could not get policies for role {name} due to NoSuchEntityException; skipping.",
@@ -144,8 +218,12 @@ def get_role_policy_data(boto3_session: boto3.session.Session, role_list: List[D
144
218
 
145
219
 
146
220
  @timeit
147
- def get_role_managed_policy_data(boto3_session: boto3.session.Session, role_list: List[Dict]) -> Dict:
148
- resource_client = boto3_session.resource('iam')
221
+ @aws_handle_regions
222
+ def get_role_managed_policy_data(
223
+ boto3_session: boto3.Session,
224
+ role_list: List[Dict],
225
+ ) -> Dict:
226
+ resource_client = boto3_session.resource("iam")
149
227
  policies = {}
150
228
  for role in role_list:
151
229
  name = role["RoleName"]
@@ -164,9 +242,10 @@ def get_role_managed_policy_data(boto3_session: boto3.session.Session, role_list
164
242
 
165
243
 
166
244
  @timeit
167
- def get_role_tags(boto3_session: boto3.session.Session) -> List[Dict]:
168
- role_list = get_role_list_data(boto3_session)['Roles']
169
- resource_client = boto3_session.resource('iam')
245
+ @aws_handle_regions
246
+ def get_role_tags(boto3_session: boto3.Session) -> List[Dict]:
247
+ role_list = get_role_list_data(boto3_session)["Roles"]
248
+ resource_client = boto3_session.resource("iam")
170
249
  role_tag_data: List[Dict] = []
171
250
  for role in role_list:
172
251
  name = role["RoleName"]
@@ -177,8 +256,8 @@ def get_role_tags(boto3_session: boto3.session.Session) -> List[Dict]:
177
256
  continue
178
257
 
179
258
  tag_data = {
180
- 'ResourceARN': role_arn,
181
- 'Tags': resource_role.tags,
259
+ "ResourceARN": role_arn,
260
+ "Tags": resource_role.tags,
182
261
  }
183
262
  role_tag_data.append(tag_data)
184
263
 
@@ -186,39 +265,66 @@ def get_role_tags(boto3_session: boto3.session.Session) -> List[Dict]:
186
265
 
187
266
 
188
267
  @timeit
189
- def get_user_list_data(boto3_session: boto3.session.Session) -> Dict:
190
- client = boto3_session.client('iam')
268
+ def get_user_list_data(boto3_session: boto3.Session) -> Dict:
269
+ client = boto3_session.client("iam")
191
270
 
192
- paginator = client.get_paginator('list_users')
271
+ paginator = client.get_paginator("list_users")
193
272
  users: List[Dict] = []
194
273
  for page in paginator.paginate():
195
- users.extend(page['Users'])
196
- return {'Users': users}
274
+ users.extend(page["Users"])
275
+ return {"Users": users}
197
276
 
198
277
 
199
278
  @timeit
200
- def get_group_list_data(boto3_session: boto3.session.Session) -> Dict:
201
- client = boto3_session.client('iam')
202
- paginator = client.get_paginator('list_groups')
279
+ def get_group_list_data(boto3_session: boto3.Session) -> Dict:
280
+ client = boto3_session.client("iam")
281
+ paginator = client.get_paginator("list_groups")
203
282
  groups: List[Dict] = []
204
283
  for page in paginator.paginate():
205
- groups.extend(page['Groups'])
206
- return {'Groups': groups}
284
+ groups.extend(page["Groups"])
285
+ return {"Groups": groups}
207
286
 
208
287
 
209
288
  @timeit
210
- def get_role_list_data(boto3_session: boto3.session.Session) -> Dict:
211
- client = boto3_session.client('iam')
212
- paginator = client.get_paginator('list_roles')
289
+ def get_role_list_data(boto3_session: boto3.Session) -> Dict:
290
+ client = boto3_session.client("iam")
291
+ paginator = client.get_paginator("list_roles")
213
292
  roles: List[Dict] = []
214
293
  for page in paginator.paginate():
215
- roles.extend(page['Roles'])
216
- return {'Roles': roles}
294
+ roles.extend(page["Roles"])
295
+ return {"Roles": roles}
217
296
 
218
297
 
219
298
  @timeit
220
- def get_account_access_key_data(boto3_session: boto3.session.Session, username: str) -> Dict:
221
- client = boto3_session.client('iam')
299
+ def get_user_access_keys_data(
300
+ boto3_session: boto3.Session,
301
+ users: list[dict[str, Any]],
302
+ ) -> dict[str, list[dict[str, Any]]]:
303
+ """
304
+ Get access key data for all users.
305
+ Returns a dict mapping user ARN to list of access key data.
306
+ """
307
+ user_access_keys = {}
308
+
309
+ for user in users:
310
+ username = user["name"]
311
+ user_arn = user["arn"]
312
+
313
+ access_keys = get_account_access_key_data(boto3_session, username)
314
+ if access_keys and "AccessKeyMetadata" in access_keys:
315
+ user_access_keys[user_arn] = access_keys["AccessKeyMetadata"]
316
+ else:
317
+ user_access_keys[user_arn] = []
318
+
319
+ return user_access_keys
320
+
321
+
322
+ @timeit
323
+ def get_account_access_key_data(
324
+ boto3_session: boto3.Session,
325
+ username: str,
326
+ ) -> Dict:
327
+ client = boto3_session.client("iam")
222
328
  # NOTE we can get away without using a paginator here because users are limited to two access keys
223
329
  access_keys: Dict = {}
224
330
  try:
@@ -228,304 +334,327 @@ def get_account_access_key_data(boto3_session: boto3.session.Session, username:
228
334
  f"Could not get access key for user {username} due to NoSuchEntityException; skipping.",
229
335
  )
230
336
  return access_keys
231
- for access_key in access_keys['AccessKeyMetadata']:
232
- access_key_id = access_key['AccessKeyId']
337
+ for access_key in access_keys["AccessKeyMetadata"]:
338
+ access_key_id = access_key["AccessKeyId"]
233
339
  last_used_info = client.get_access_key_last_used(
234
340
  AccessKeyId=access_key_id,
235
- )['AccessKeyLastUsed']
341
+ )["AccessKeyLastUsed"]
236
342
  # only LastUsedDate may be null
237
343
  # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iam/client/get_access_key_last_used.html
238
- access_key['LastUsedDate'] = last_used_info.get('LastUsedDate')
239
- access_key['LastUsedService'] = last_used_info['ServiceName']
240
- access_key['LastUsedRegion'] = last_used_info['Region']
344
+ access_key["LastUsedDate"] = last_used_info.get("LastUsedDate")
345
+ access_key["LastUsedService"] = last_used_info["ServiceName"]
346
+ access_key["LastUsedRegion"] = last_used_info["Region"]
241
347
  return access_keys
242
348
 
243
349
 
244
350
  @timeit
245
- def load_users(
246
- neo4j_session: neo4j.Session, users: List[Dict], current_aws_account_id: str, aws_update_tag: int,
247
- ) -> None:
248
- ingest_user = """
249
- MERGE (unode:AWSUser{arn: $ARN})
250
- ON CREATE SET unode:AWSPrincipal, unode.userid = $USERID, unode.firstseen = timestamp(),
251
- unode.createdate = $CREATE_DATE
252
- SET unode.name = $USERNAME, unode.path = $PATH, unode.passwordlastused = $PASSWORD_LASTUSED,
253
- unode.lastupdated = $aws_update_tag
254
- WITH unode
255
- MATCH (aa:AWSAccount{id: $AWS_ACCOUNT_ID})
256
- MERGE (aa)-[r:RESOURCE]->(unode)
257
- ON CREATE SET r.firstseen = timestamp()
258
- SET r.lastupdated = $aws_update_tag
351
+ def get_group_memberships(
352
+ boto3_session: boto3.Session, groups: list[dict[str, Any]]
353
+ ) -> dict[str, list[str]]:
259
354
  """
260
- logger.info(f"Loading {len(users)} IAM users.")
261
- for user in users:
262
- neo4j_session.run(
263
- ingest_user,
264
- ARN=user["Arn"],
265
- USERID=user["UserId"],
266
- CREATE_DATE=str(user["CreateDate"]),
267
- USERNAME=user["UserName"],
268
- PATH=user["Path"],
269
- PASSWORD_LASTUSED=str(user.get("PasswordLastUsed", "")),
270
- AWS_ACCOUNT_ID=current_aws_account_id,
271
- aws_update_tag=aws_update_tag,
272
- )
355
+ Get membership data for all groups.
356
+ Returns a dict mapping group ARN to list of user ARNs.
357
+ """
358
+ memberships = {}
359
+ for group in groups:
360
+ try:
361
+ membership_data = get_group_membership_data(
362
+ boto3_session, group["GroupName"]
363
+ )
364
+ if membership_data and "Users" in membership_data:
365
+ memberships[group["Arn"]] = [
366
+ user["Arn"] for user in membership_data["Users"]
367
+ ]
368
+ else:
369
+ memberships[group["Arn"]] = []
370
+ except Exception:
371
+ logger.warning(
372
+ f"Could not get membership data for group {group['GroupName']}",
373
+ exc_info=True,
374
+ )
375
+ memberships[group["Arn"]] = []
376
+
377
+ return memberships
273
378
 
274
379
 
275
380
  @timeit
276
- def load_groups(
277
- neo4j_session: neo4j.Session, groups: List[Dict], current_aws_account_id: str, aws_update_tag: int,
278
- ) -> None:
279
- ingest_group = """
280
- MERGE (gnode:AWSGroup{arn: $ARN})
281
- ON CREATE SET gnode.groupid = $GROUP_ID, gnode.firstseen = timestamp(), gnode.createdate = $CREATE_DATE
282
- SET gnode:AWSPrincipal, gnode.name = $GROUP_NAME, gnode.path = $PATH,gnode.lastupdated = $aws_update_tag
283
- WITH gnode
284
- MATCH (aa:AWSAccount{id: $AWS_ACCOUNT_ID})
285
- MERGE (aa)-[r:RESOURCE]->(gnode)
286
- ON CREATE SET r.firstseen = timestamp()
287
- SET r.lastupdated = $aws_update_tag
381
+ def get_policies_for_principal(
382
+ neo4j_session: neo4j.Session,
383
+ principal_arn: str,
384
+ ) -> Dict:
385
+ get_policy_query = """
386
+ MATCH
387
+ (principal:AWSPrincipal{arn:$Arn})-[:POLICY]->
388
+ (policy:AWSPolicy)-[:STATEMENT]->
389
+ (statements:AWSPolicyStatement)
390
+ RETURN
391
+ DISTINCT policy.id AS policy_id,
392
+ COLLECT(DISTINCT statements) AS statements
288
393
  """
289
- logger.info(f"Loading {len(groups)} IAM groups to the graph.")
290
- for group in groups:
291
- neo4j_session.run(
292
- ingest_group,
293
- ARN=group["Arn"],
294
- GROUP_ID=group["GroupId"],
295
- CREATE_DATE=str(group["CreateDate"]),
296
- GROUP_NAME=group["GroupName"],
297
- PATH=group["Path"],
298
- AWS_ACCOUNT_ID=current_aws_account_id,
299
- aws_update_tag=aws_update_tag,
300
- )
394
+ results = neo4j_session.execute_read(
395
+ read_list_of_dicts_tx,
396
+ get_policy_query,
397
+ Arn=principal_arn,
398
+ )
399
+ policies = {r["policy_id"]: r["statements"] for r in results}
400
+ return policies
301
401
 
302
402
 
303
- def _parse_principal_entries(principal: Dict) -> List[Tuple[Any, Any]]:
304
- """
305
- Returns a list of tuples of the form (principal_type, principal_value)
306
- e.g. [('AWS', 'example-role-name'), ('Service', 'example-service')]
307
- """
308
- principal_entries = []
309
- for principal_type in principal:
310
- principal_values = principal[principal_type]
311
- if not isinstance(principal_values, list):
312
- principal_values = [principal_values]
313
- for principal_value in principal_values:
314
- principal_entries.append((principal_type, principal_value))
315
- return principal_entries
403
+ def transform_users(users: list[dict[str, Any]]) -> list[dict[str, Any]]:
404
+ user_data = []
405
+ for user in users:
406
+ user_record = {
407
+ "arn": user["Arn"],
408
+ "userid": user["UserId"],
409
+ "name": user["UserName"],
410
+ "path": user["Path"],
411
+ "createdate": str(user["CreateDate"]),
412
+ "passwordlastused": str(user.get("PasswordLastUsed", "")),
413
+ }
414
+ user_data.append(user_record)
316
415
 
416
+ return user_data
317
417
 
318
- @timeit
319
- def load_roles(
320
- neo4j_session: neo4j.Session, roles: List[Dict], current_aws_account_id: str, aws_update_tag: int,
321
- ) -> None:
322
- ingest_role = """
323
- MERGE (rnode:AWSPrincipal{arn: $Arn})
324
- ON CREATE SET rnode.firstseen = timestamp()
325
- SET
326
- rnode:AWSRole,
327
- rnode.roleid = $RoleId,
328
- rnode.createdate = $CreateDate,
329
- rnode.name = $RoleName,
330
- rnode.path = $Path,
331
- rnode.lastupdated = $aws_update_tag
332
- WITH rnode
333
- MATCH (aa:AWSAccount{id: $AWS_ACCOUNT_ID})
334
- MERGE (aa)-[r:RESOURCE]->(rnode)
335
- ON CREATE SET r.firstseen = timestamp()
336
- SET r.lastupdated = $aws_update_tag
337
- """
338
418
 
339
- ingest_policy_statement = """
340
- MERGE (spnnode:AWSPrincipal{arn: $SpnArn})
341
- ON CREATE SET spnnode.firstseen = timestamp()
342
- SET spnnode.lastupdated = $aws_update_tag, spnnode.type = $SpnType
343
- WITH spnnode
344
- MATCH (role:AWSRole{arn: $RoleArn})
345
- MERGE (role)-[r:TRUSTS_AWS_PRINCIPAL]->(spnnode)
346
- ON CREATE SET r.firstseen = timestamp()
347
- SET r.lastupdated = $aws_update_tag
419
+ def transform_groups(
420
+ groups: list[dict[str, Any]], group_memberships: dict[str, list[str]]
421
+ ) -> list[dict[str, Any]]:
422
+ group_data = []
423
+ for group in groups:
424
+ group_record = {
425
+ "arn": group["Arn"],
426
+ "groupid": group["GroupId"],
427
+ "name": group["GroupName"],
428
+ "path": group["Path"],
429
+ "createdate": str(group["CreateDate"]),
430
+ "user_arns": group_memberships.get(group["Arn"], []),
431
+ }
432
+ group_data.append(group_record)
433
+
434
+ return group_data
435
+
436
+
437
+ def transform_access_keys(
438
+ user_access_keys: dict[str, list[dict[str, Any]]],
439
+ ) -> list[dict[str, Any]]:
440
+ access_key_data = []
441
+ for user_arn, access_keys in user_access_keys.items():
442
+ for access_key in access_keys:
443
+ if access_key.get("AccessKeyId"):
444
+ access_key_record = {
445
+ "accesskeyid": access_key["AccessKeyId"],
446
+ "createdate": str(access_key["CreateDate"]),
447
+ "status": access_key["Status"],
448
+ "lastuseddate": str(access_key.get("LastUsedDate", "")),
449
+ "lastusedservice": access_key.get("LastUsedService", ""),
450
+ "lastusedregion": access_key.get("LastUsedRegion", ""),
451
+ "user_arn": user_arn, # For the sub-resource relationship
452
+ }
453
+ access_key_data.append(access_key_record)
454
+
455
+ return access_key_data
456
+
457
+
458
+ def transform_role_trust_policies(
459
+ roles: list[dict[str, Any]], current_aws_account_id: str
460
+ ) -> TransformedRoleData:
348
461
  """
349
-
350
- # Note - why we don't set inscope or foreign attribute on the account
351
- #
352
- # we are agnostic here if this is the AWSAccount is part of the sync scope or
353
- # a foreign AWS account that contains a trusted principal. The account could also be inscope
354
- # but not sync yet.
355
- # - The inscope attribute - set when the account is being sync.
356
- # - The foreign attribute - the attribute assignment logic is in aws_foreign_accounts.json analysis job
357
- # - Why seperate statement is needed - the arn may point to service level principals ex - ec2.amazonaws.com
358
- ingest_spnmap_statement = """
359
- MERGE (aa:AWSAccount{id: $SpnAccountId})
360
- ON CREATE SET aa.firstseen = timestamp()
361
- SET aa.lastupdated = $aws_update_tag
362
- WITH aa
363
- MATCH (spnnode:AWSPrincipal{arn: $SpnArn})
364
- WITH spnnode, aa
365
- MERGE (aa)-[r:RESOURCE]->(spnnode)
366
- ON CREATE SET r.firstseen = timestamp()
462
+ Processes AWS role assumption policy documents in the list_roles response.
463
+ Returns a TransformedRoleData object containing the role data, federated principals, service principals, and external AWS accounts.
367
464
  """
465
+ role_data: list[dict[str, Any]] = []
466
+ federated_principals: list[dict[str, Any]] = []
467
+ service_principals: list[dict[str, Any]] = []
468
+ external_aws_accounts: list[dict[str, Any]] = []
368
469
 
369
- # TODO support conditions
370
- logger.info(f"Loading {len(roles)} IAM roles to the graph.")
371
470
  for role in roles:
372
- neo4j_session.run(
373
- ingest_role,
374
- Arn=role["Arn"],
375
- RoleId=role["RoleId"],
376
- CreateDate=str(role["CreateDate"]),
377
- RoleName=role["RoleName"],
378
- Path=role["Path"],
379
- AWS_ACCOUNT_ID=current_aws_account_id,
380
- aws_update_tag=aws_update_tag,
381
- )
471
+ role_arn = role["Arn"]
382
472
 
473
+ # List of principals of type "AWS" that this role trusts
474
+ trusted_aws_principals = set()
475
+ # Process each statement in the assume role policy document
476
+ # TODO support conditions
383
477
  for statement in role["AssumeRolePolicyDocument"]["Statement"]:
478
+
384
479
  principal_entries = _parse_principal_entries(statement["Principal"])
385
- for principal_type, principal_value in principal_entries:
386
- neo4j_session.run(
387
- ingest_policy_statement,
388
- SpnArn=principal_value,
389
- SpnType=principal_type,
390
- RoleArn=role['Arn'],
391
- aws_update_tag=aws_update_tag,
392
- )
393
- spn_arn = get_account_from_arn(principal_value)
394
- if spn_arn:
395
- neo4j_session.run(
396
- ingest_spnmap_statement,
397
- SpnArn=principal_value,
398
- SpnAccountId=get_account_from_arn(principal_value),
399
- aws_update_tag=aws_update_tag,
480
+ for principal_type, principal_arn in principal_entries:
481
+ if principal_type == "Federated":
482
+ # Add this to list of federated nodes to create
483
+ account_id = get_account_from_arn(principal_arn)
484
+ federated_principals.append(
485
+ {
486
+ "arn": principal_arn,
487
+ "type": "Federated",
488
+ "other_account_id": (
489
+ account_id
490
+ if account_id != current_aws_account_id
491
+ else None
492
+ ),
493
+ "role_arn": role_arn,
494
+ }
400
495
  )
496
+ trusted_aws_principals.add(principal_arn)
497
+ elif principal_type == "Service":
498
+ # Add to the list of service nodes to create
499
+ service_principals.append(
500
+ {
501
+ "arn": principal_arn,
502
+ "type": "Service",
503
+ }
504
+ )
505
+ # Service principals are global so there is no account id.
506
+ trusted_aws_principals.add(principal_arn)
507
+ elif principal_type == "AWS":
508
+ if "root" in principal_arn:
509
+ # The current principal trusts a root principal.
510
+
511
+ # First check if the root principal is in a different account than the current one.
512
+ # Add what we know about that account to the graph.
513
+ account_id = get_account_from_arn(principal_arn)
514
+ if account_id != current_aws_account_id:
515
+ external_aws_accounts.append({"id": account_id})
516
+ trusted_aws_principals.add(principal_arn)
517
+ else:
518
+ # This should not happen but who knows.
519
+ logger.warning(f"Unknown principal type: {principal_type}")
520
+
521
+ role_record = {
522
+ "arn": role["Arn"],
523
+ "roleid": role["RoleId"],
524
+ "name": role["RoleName"],
525
+ "path": role["Path"],
526
+ "createdate": str(role["CreateDate"]),
527
+ "trusted_aws_principals": list(trusted_aws_principals),
528
+ "account_id": get_account_from_arn(role["Arn"]),
529
+ }
530
+ role_data.append(role_record)
531
+
532
+ return TransformedRoleData(
533
+ role_data=role_data,
534
+ federated_principals=federated_principals,
535
+ service_principals=service_principals,
536
+ external_aws_accounts=external_aws_accounts,
537
+ )
401
538
 
402
539
 
403
540
  @timeit
404
- def load_group_memberships(neo4j_session: neo4j.Session, group_memberships: Dict, aws_update_tag: int) -> None:
405
- ingest_membership = """
406
- MATCH (group:AWSGroup{arn: $GroupArn})
407
- WITH group
408
- MATCH (user:AWSUser{arn: $PrincipalArn})
409
- MERGE (user)-[r:MEMBER_AWS_GROUP]->(group)
410
- ON CREATE SET r.firstseen = timestamp()
411
- SET r.lastupdated = $aws_update_tag
412
- WITH user, group
413
- MATCH (group)-[:POLICY]->(policy:AWSPolicy)
414
- MERGE (user)-[r2:POLICY]->(policy)
415
- SET r2.lastupdated = $aws_update_tag
416
- """
541
+ def load_users(
542
+ neo4j_session: neo4j.Session,
543
+ users: List[Dict],
544
+ current_aws_account_id: str,
545
+ aws_update_tag: int,
546
+ ) -> None:
547
+ load(
548
+ neo4j_session,
549
+ AWSUserSchema(),
550
+ users,
551
+ lastupdated=aws_update_tag,
552
+ AWS_ID=current_aws_account_id,
553
+ )
417
554
 
418
- for group_arn, membership_data in group_memberships.items():
419
- for info in membership_data.get("Users", []):
420
- principal_arn = info["Arn"]
421
- neo4j_session.run(
422
- ingest_membership,
423
- GroupArn=group_arn,
424
- PrincipalArn=principal_arn,
425
- aws_update_tag=aws_update_tag,
426
- )
555
+
556
+ @timeit
557
+ def load_groups(
558
+ neo4j_session: neo4j.Session,
559
+ groups: List[Dict],
560
+ current_aws_account_id: str,
561
+ aws_update_tag: int,
562
+ ) -> None:
563
+ load(
564
+ neo4j_session,
565
+ AWSGroupSchema(),
566
+ groups,
567
+ lastupdated=aws_update_tag,
568
+ AWS_ID=current_aws_account_id,
569
+ )
427
570
 
428
571
 
429
572
  @timeit
430
- def get_policies_for_principal(neo4j_session: neo4j.Session, principal_arn: str) -> Dict:
431
- get_policy_query = """
432
- MATCH
433
- (principal:AWSPrincipal{arn:$Arn})-[:POLICY]->
434
- (policy:AWSPolicy)-[:STATEMENT]->
435
- (statements:AWSPolicyStatement)
436
- RETURN
437
- DISTINCT policy.id AS policy_id,
438
- COLLECT(DISTINCT statements) AS statements
439
- """
440
- results = neo4j_session.run(
441
- get_policy_query,
442
- Arn=principal_arn,
573
+ def load_access_keys(
574
+ neo4j_session: neo4j.Session,
575
+ access_keys: List[Dict],
576
+ aws_update_tag: int,
577
+ current_aws_account_id: str,
578
+ ) -> None:
579
+ load(
580
+ neo4j_session,
581
+ AccountAccessKeySchema(),
582
+ access_keys,
583
+ lastupdated=aws_update_tag,
584
+ AWS_ID=current_aws_account_id,
443
585
  )
444
- policies = {r["policy_id"]: parse_statement_node(r["statements"]) for r in results}
445
- return policies
586
+
587
+
588
+ def _parse_principal_entries(principal: Dict) -> List[Tuple[Any, Any]]:
589
+ """
590
+ Returns a list of tuples of the form (principal_type, principal_value)
591
+ e.g. [('AWS', 'example-role-name'), ('Service', 'example-service')]
592
+ """
593
+ principal_entries = []
594
+ for principal_type in principal:
595
+ principal_values = principal[principal_type]
596
+ if not isinstance(principal_values, list):
597
+ principal_values = [principal_values]
598
+ for principal_value in principal_values:
599
+ principal_entries.append((principal_type, principal_value))
600
+ return principal_entries
446
601
 
447
602
 
448
603
  @timeit
449
604
  def sync_assumerole_relationships(
450
- neo4j_session: neo4j.Session, current_aws_account_id: str, aws_update_tag: int,
605
+ neo4j_session: neo4j.Session,
606
+ current_aws_account_id: str,
607
+ aws_update_tag: int,
451
608
  common_job_parameters: Dict,
452
609
  ) -> None:
453
610
  # Must be called after load_role
454
- # Computes and syncs the STS_ASSUME_ROLE allow relationship
455
- logger.info("Syncing assume role mappings for account '%s'.", current_aws_account_id)
611
+ # Computes and syncs the STS_ASSUMEROLE_ALLOW relationship
612
+ logger.info(
613
+ "Syncing assume role mappings for account '%s'.",
614
+ current_aws_account_id,
615
+ )
456
616
  query_potential_matches = """
457
617
  MATCH (:AWSAccount{id:$AccountId})-[:RESOURCE]->(target:AWSRole)-[:TRUSTS_AWS_PRINCIPAL]->(source:AWSPrincipal)
458
- WHERE NOT source.arn ENDS WITH 'root'
459
- AND NOT source.type = 'Service'
460
- AND NOT source.type = 'Federated'
461
- RETURN target.arn AS target_arn,
462
- source.arn AS source_arn
463
- """
464
-
465
- ingest_policies_assume_role = """
466
- MATCH (source:AWSPrincipal{arn: $SourceArn})
467
- WITH source
468
- MATCH (role:AWSRole{arn: $TargetArn})
469
- WITH role, source
470
- MERGE (source)-[r:STS_ASSUMEROLE_ALLOW]->(role)
471
- ON CREATE SET r.firstseen = timestamp()
472
- SET r.lastupdated = $aws_update_tag
618
+ WHERE NOT source:AWSRootPrincipal
619
+ AND NOT source:AWSServicePrincipal
620
+ AND NOT source:AWSFederatedPrincipal
621
+ RETURN target.arn AS target_arn, source.arn AS source_arn
473
622
  """
474
-
475
- results = neo4j_session.run(
623
+ results = neo4j_session.execute_read(
624
+ read_list_of_dicts_tx,
476
625
  query_potential_matches,
477
626
  AccountId=current_aws_account_id,
478
627
  )
479
- potential_matches = [(r["source_arn"], r["target_arn"]) for r in results]
480
- for source_arn, target_arn in potential_matches:
628
+
629
+ # Filter potential matches to only those where the source principal has sts:AssumeRole permission
630
+ valid_matches = []
631
+ for result in results:
632
+ source_arn = result["source_arn"]
633
+ target_arn = result["target_arn"]
481
634
  policies = get_policies_for_principal(neo4j_session, source_arn)
482
635
  if principal_allowed_on_resource(policies, target_arn, ["sts:AssumeRole"]):
483
- neo4j_session.run(
484
- ingest_policies_assume_role,
485
- SourceArn=source_arn,
486
- TargetArn=target_arn,
487
- aws_update_tag=aws_update_tag,
636
+ valid_matches.append(
637
+ {
638
+ "source_arn": source_arn,
639
+ "target_arn": target_arn,
640
+ }
488
641
  )
489
- run_cleanup_job(
490
- 'aws_import_roles_policy_cleanup.json',
642
+
643
+ load_matchlinks(
491
644
  neo4j_session,
492
- common_job_parameters,
645
+ STSAssumeRoleAllowMatchLink(),
646
+ valid_matches,
647
+ lastupdated=aws_update_tag,
648
+ _sub_resource_label="AWSAccount",
649
+ _sub_resource_id=current_aws_account_id,
493
650
  )
494
651
 
495
-
496
- @timeit
497
- def load_user_access_keys(neo4j_session: neo4j.Session, user_access_keys: Dict, aws_update_tag: int) -> None:
498
- # TODO change the node label to reflect that this is a user access key, not an account access key
499
- ingest_account_key = """
500
- MATCH (user:AWSUser{arn: $UserARN})
501
- WITH user
502
- MERGE (key:AccountAccessKey{accesskeyid: $AccessKeyId})
503
- ON CREATE SET key.firstseen = timestamp(), key.createdate = $CreateDate
504
- SET key.status = $Status,
505
- key.lastupdated = $aws_update_tag,
506
- key.lastuseddate = $LastUsedDate,
507
- key.lastusedservice = $LastUsedService,
508
- key.lastusedregion = $LastUsedRegion
509
- WITH user,key
510
- MERGE (user)-[r:AWS_ACCESS_KEY]->(key)
511
- ON CREATE SET r.firstseen = timestamp()
512
- SET r.lastupdated = $aws_update_tag
513
- """
514
-
515
- for arn, access_keys in user_access_keys.items():
516
- for key in access_keys["AccessKeyMetadata"]:
517
- if key.get('AccessKeyId'):
518
- neo4j_session.run(
519
- ingest_account_key,
520
- UserARN=arn,
521
- AccessKeyId=key['AccessKeyId'],
522
- CreateDate=str(key['CreateDate']),
523
- Status=key['Status'],
524
- LastUsedDate=key['LastUsedDate'],
525
- LastUsedService=key['LastUsedService'],
526
- LastUsedRegion=key['LastUsedRegion'],
527
- aws_update_tag=aws_update_tag,
528
- )
652
+ GraphJob.from_matchlink(
653
+ STSAssumeRoleAllowMatchLink(),
654
+ sub_resource_label="AWSAccount",
655
+ sub_resource_id=current_aws_account_id,
656
+ update_tag=aws_update_tag,
657
+ ).run(neo4j_session)
529
658
 
530
659
 
531
660
  def ensure_list(obj: Any) -> List[Any]:
@@ -534,296 +663,681 @@ def ensure_list(obj: Any) -> List[Any]:
534
663
  return obj
535
664
 
536
665
 
537
- def _transform_policy_statements(statements: Any, policy_id: str) -> List[Dict]:
666
+ def _transform_policy_statements(
667
+ statements: Any, policy_id: str
668
+ ) -> list[dict[str, Any]]:
669
+ result: List[Dict[str, Any]] = []
538
670
  count = 1
671
+
539
672
  if not isinstance(statements, list):
540
673
  statements = [statements]
674
+
541
675
  for stmt in statements:
542
- if "Sid" not in stmt:
676
+ # Determine statement ID
677
+ if "Sid" in stmt and stmt["Sid"]:
678
+ statement_id = stmt["Sid"]
679
+ else:
543
680
  statement_id = count
544
681
  count += 1
545
- else:
546
- statement_id = stmt["Sid"]
547
- stmt["id"] = f"{policy_id}/statement/{statement_id}"
682
+
683
+ transformed_stmt = {
684
+ "id": f"{policy_id}/statement/{statement_id}",
685
+ "policy_id": policy_id, # For the relationship to AWSPolicy
686
+ "Effect": stmt.get("Effect"),
687
+ "Sid": stmt.get("Sid"),
688
+ }
689
+
690
+ # Handle list fields
548
691
  if "Resource" in stmt:
549
- stmt["Resource"] = ensure_list(stmt["Resource"])
692
+ transformed_stmt["Resource"] = ensure_list(stmt["Resource"])
550
693
  if "Action" in stmt:
551
- stmt["Action"] = ensure_list(stmt["Action"])
694
+ transformed_stmt["Action"] = ensure_list(stmt["Action"])
552
695
  if "NotAction" in stmt:
553
- stmt["NotAction"] = ensure_list(stmt["NotAction"])
696
+ transformed_stmt["NotAction"] = ensure_list(stmt["NotAction"])
554
697
  if "NotResource" in stmt:
555
- stmt["NotResource"] = ensure_list(stmt["NotResource"])
698
+ transformed_stmt["NotResource"] = ensure_list(stmt["NotResource"])
556
699
  if "Condition" in stmt:
557
- stmt["Condition"] = json.dumps(ensure_list(stmt["Condition"]))
558
- return statements
700
+ transformed_stmt["Condition"] = json.dumps(ensure_list(stmt["Condition"]))
701
+
702
+ result.append(transformed_stmt)
703
+
704
+ return result
559
705
 
560
706
 
561
- def transform_policy_data(policy_map: Dict, policy_type: str) -> None:
707
+ def transform_policy_data(
708
+ policy_map: dict[str, dict[str, Any]], policy_type: str
709
+ ) -> TransformedPolicyData:
710
+ """
711
+ Processes AWS IAM policy documents. Returns a TransformedPolicyData object containing the managed policies, inline policies, and statements by policy id -- all ready to be loaded to the graph.
712
+ """
713
+ # First pass: collect all policies and their principals
714
+ policy_to_principals: dict[str, set[str]] = {}
715
+ policy_to_statements: dict[str, list[dict[str, Any]]] = {}
716
+ policy_to_name: dict[str, str] = {}
717
+
562
718
  for principal_arn, policy_statement_map in policy_map.items():
563
- logger.debug(f"Transforming IAM {policy_type} policies for principal {principal_arn}")
564
719
  for policy_key, statements in policy_statement_map.items():
565
- policy_id = transform_policy_id(
566
- principal_arn,
567
- policy_type,
568
- policy_key,
569
- ) if policy_type == PolicyType.inline.value else policy_key
570
- policy_statement_map[policy_key] = _transform_policy_statements(statements, policy_id)
720
+ policy_id = (
721
+ transform_policy_id(principal_arn, policy_type, policy_key)
722
+ if policy_type == PolicyType.inline.value
723
+ else policy_key
724
+ )
725
+ policy_name = (
726
+ policy_key
727
+ if policy_type == PolicyType.inline.value
728
+ else get_policy_name_from_arn(policy_key)
729
+ )
730
+ # Map policy id to the principal arns that have it
731
+ if policy_id not in policy_to_principals:
732
+ policy_to_principals[policy_id] = set()
733
+ policy_to_principals[policy_id].add(principal_arn)
734
+
735
+ # Map policy id to policy name
736
+ policy_to_name[policy_id] = policy_name
737
+
738
+ # Transform and store statements
739
+ transformed_statements = _transform_policy_statements(
740
+ statements,
741
+ policy_id,
742
+ )
743
+ policy_to_statements[policy_id] = transformed_statements
744
+
745
+ # Second pass: create consolidated policy data
746
+ managed_policy_data = []
747
+ inline_policy_data = []
748
+
749
+ for policy_id, principal_arns in policy_to_principals.items():
750
+ policy_name = policy_to_name[policy_id]
751
+
752
+ policy_data = {
753
+ "id": policy_id,
754
+ "name": policy_name,
755
+ "type": policy_type,
756
+ # AWS inline policies don't have arns
757
+ "arn": policy_id if policy_type == PolicyType.managed.value else None,
758
+ "principal_arns": list(principal_arns),
759
+ }
760
+
761
+ if policy_type == PolicyType.inline.value:
762
+ inline_policy_data.append(policy_data)
763
+ elif policy_type == PolicyType.managed.value:
764
+ managed_policy_data.append(policy_data)
765
+ else:
766
+ # This really should never happen so just explicitly having a `pass` here.
767
+ pass
768
+
769
+ return TransformedPolicyData(
770
+ managed_policies=managed_policy_data,
771
+ inline_policies=inline_policy_data,
772
+ statements_by_policy_id=policy_to_statements,
773
+ )
571
774
 
572
775
 
573
776
  def transform_policy_id(principal_arn: str, policy_type: str, name: str) -> str:
574
777
  return f"{principal_arn}/{policy_type}_policy/{name}"
575
778
 
576
779
 
577
- def _load_policy_tx(
578
- tx: neo4j.Transaction, policy_id: str, policy_name: str, policy_type: str, principal_arn: str,
780
+ def _load_policy(
781
+ neo4j_session: neo4j.Session,
782
+ managed_policy_data: list[dict[str, Any]],
783
+ inline_policy_data: list[dict[str, Any]],
784
+ account_id: str,
579
785
  aws_update_tag: int,
580
786
  ) -> None:
581
- ingest_policy = """
582
- MERGE (policy:AWSPolicy{id: $PolicyId})
583
- ON CREATE SET
584
- policy.firstseen = timestamp(),
585
- policy.type = $PolicyType,
586
- policy.name = $PolicyName
587
- SET policy.lastupdated = $aws_update_tag
588
- WITH policy
589
- MATCH (principal:AWSPrincipal{arn: $PrincipalArn})
590
- MERGE (policy) <-[r:POLICY]-(principal)
591
- SET r.lastupdated = $aws_update_tag
592
- """
593
- tx.run(
594
- ingest_policy,
595
- PolicyId=policy_id,
596
- PolicyName=policy_name,
597
- PolicyType=policy_type,
598
- PrincipalArn=principal_arn,
599
- aws_update_tag=aws_update_tag,
787
+ load(
788
+ neo4j_session,
789
+ AWSManagedPolicySchema(),
790
+ managed_policy_data,
791
+ lastupdated=aws_update_tag,
792
+ )
793
+ load(
794
+ neo4j_session,
795
+ AWSInlinePolicySchema(),
796
+ inline_policy_data,
797
+ lastupdated=aws_update_tag,
798
+ AWS_ID=account_id,
600
799
  )
601
800
 
602
801
 
603
802
  @timeit
604
- def load_policy(
605
- neo4j_session: neo4j.Session, policy_id: str, policy_name: str, policy_type: str, principal_arn: str,
803
+ def load_policy_statements(
804
+ neo4j_session: neo4j.Session,
805
+ statements: list[dict[str, Any]],
606
806
  aws_update_tag: int,
607
807
  ) -> None:
608
- neo4j_session.write_transaction(_load_policy_tx, policy_id, policy_name, policy_type, principal_arn, aws_update_tag)
808
+ load(
809
+ neo4j_session,
810
+ AWSPolicyStatementSchema(),
811
+ statements,
812
+ lastupdated=aws_update_tag,
813
+ POLICY_ID=statements[0]["policy_id"],
814
+ )
609
815
 
610
816
 
611
817
  @timeit
612
- def load_policy_statements(
613
- neo4j_session: neo4j.Session, policy_id: str, policy_name: str, statements: Any,
818
+ def _load_policy_statements(
819
+ neo4j_session: neo4j.Session,
820
+ policy_statements: dict[str, list[dict[str, Any]]],
614
821
  aws_update_tag: int,
615
822
  ) -> None:
616
- ingest_policy_statement = """
617
- MATCH (policy:AWSPolicy{id: $PolicyId})
618
- WITH policy
619
- UNWIND $Statements as statement_data
620
- MERGE (statement:AWSPolicyStatement{id: statement_data.id})
621
- SET
622
- statement.effect = statement_data.Effect,
623
- statement.action = statement_data.Action,
624
- statement.notaction = statement_data.NotAction,
625
- statement.resource = statement_data.Resource,
626
- statement.notresource = statement_data.NotResource,
627
- statement.condition = statement_data.Condition,
628
- statement.sid = statement_data.Sid,
629
- statement.lastupdated = $aws_update_tag
630
- MERGE (policy)-[r:STATEMENT]->(statement)
631
- ON CREATE SET r.firstseen = timestamp()
632
- SET r.lastupdated = $aws_update_tag
633
- """
634
- neo4j_session.run(
635
- ingest_policy_statement,
636
- PolicyId=policy_id,
637
- PolicyName=policy_name,
638
- Statements=statements,
639
- aws_update_tag=aws_update_tag,
640
- ).consume()
823
+ for policy_id, statements in policy_statements.items():
824
+ load(
825
+ neo4j_session,
826
+ AWSPolicyStatementSchema(),
827
+ statements,
828
+ lastupdated=aws_update_tag,
829
+ POLICY_ID=policy_id,
830
+ )
641
831
 
642
832
 
643
833
  @timeit
644
834
  def load_policy_data(
645
- neo4j_session: neo4j.Session,
646
- principal_policy_map: Dict[str, Dict[str, Any]],
647
- policy_type: str,
648
- aws_update_tag: int,
835
+ neo4j_session: neo4j.Session,
836
+ transformed_policy_data: TransformedPolicyData,
837
+ aws_update_tag: int,
838
+ current_aws_account_id: str,
649
839
  ) -> None:
650
- for principal_arn, policy_statement_map in principal_policy_map.items():
651
- logger.debug(f"Loading policies for principal {principal_arn}")
652
- for policy_key, statements in policy_statement_map.items():
653
- policy_name = policy_key if policy_type == PolicyType.inline.value else get_policy_name_from_arn(policy_key)
654
- policy_id = transform_policy_id(
655
- principal_arn,
656
- policy_type,
657
- policy_key,
658
- ) if policy_type == PolicyType.inline.value else policy_key
659
- load_policy(neo4j_session, policy_id, policy_name, policy_type, principal_arn, aws_update_tag)
660
- load_policy_statements(neo4j_session, policy_id, policy_name, statements, aws_update_tag)
840
+ _load_policy(
841
+ neo4j_session,
842
+ transformed_policy_data.managed_policies,
843
+ transformed_policy_data.inline_policies,
844
+ current_aws_account_id,
845
+ aws_update_tag,
846
+ )
847
+
848
+ _load_policy_statements(
849
+ neo4j_session,
850
+ transformed_policy_data.statements_by_policy_id,
851
+ aws_update_tag,
852
+ )
661
853
 
662
854
 
663
855
  @timeit
664
856
  def sync_users(
665
- neo4j_session: neo4j.Session, boto3_session: boto3.session.Session, current_aws_account_id: str,
666
- aws_update_tag: int, common_job_parameters: Dict,
857
+ neo4j_session: neo4j.Session,
858
+ boto3_session: boto3.Session,
859
+ current_aws_account_id: str,
860
+ aws_update_tag: int,
861
+ common_job_parameters: Dict,
667
862
  ) -> None:
668
863
  logger.info("Syncing IAM users for account '%s'.", current_aws_account_id)
669
864
  data = get_user_list_data(boto3_session)
670
- load_users(neo4j_session, data['Users'], current_aws_account_id, aws_update_tag)
865
+ user_data = transform_users(data["Users"])
866
+ load_users(neo4j_session, user_data, current_aws_account_id, aws_update_tag)
867
+
868
+ sync_user_inline_policies(
869
+ boto3_session, data, neo4j_session, aws_update_tag, current_aws_account_id
870
+ )
871
+
872
+ sync_user_managed_policies(
873
+ boto3_session, data, neo4j_session, aws_update_tag, current_aws_account_id
874
+ )
671
875
 
672
- sync_user_inline_policies(boto3_session, data, neo4j_session, aws_update_tag)
673
876
 
674
- sync_user_managed_policies(boto3_session, data, neo4j_session, aws_update_tag)
877
+ @timeit
878
+ def sync_user_access_keys(
879
+ neo4j_session: neo4j.Session,
880
+ boto3_session: boto3.Session,
881
+ current_aws_account_id: str,
882
+ aws_update_tag: int,
883
+ common_job_parameters: Dict,
884
+ ) -> None:
885
+ logger.info(
886
+ "Syncing IAM user access keys for account '%s'.", current_aws_account_id
887
+ )
675
888
 
676
- run_cleanup_job('aws_import_users_cleanup.json', neo4j_session, common_job_parameters)
889
+ # Query the graph for users instead of making another AWS API call
890
+ query = (
891
+ "MATCH (user:AWSUser)<-[:RESOURCE]-(:AWSAccount{id: $AWS_ID}) "
892
+ "RETURN user.name as name, user.arn as arn"
893
+ )
894
+ users = neo4j_session.execute_read(
895
+ read_list_of_dicts_tx,
896
+ query,
897
+ AWS_ID=current_aws_account_id,
898
+ )
899
+
900
+ user_access_keys = get_user_access_keys_data(boto3_session, users)
901
+ access_key_data = transform_access_keys(user_access_keys)
902
+ load_access_keys(
903
+ neo4j_session, access_key_data, aws_update_tag, current_aws_account_id
904
+ )
905
+ GraphJob.from_node_schema(AccountAccessKeySchema(), common_job_parameters).run(
906
+ neo4j_session
907
+ )
677
908
 
678
909
 
679
910
  @timeit
680
911
  def sync_user_managed_policies(
681
- boto3_session: boto3.session.Session, data: Dict, neo4j_session: neo4j.Session,
912
+ boto3_session: boto3.Session,
913
+ data: Dict,
914
+ neo4j_session: neo4j.Session,
682
915
  aws_update_tag: int,
916
+ current_aws_account_id: str,
683
917
  ) -> None:
684
- managed_policy_data = get_user_managed_policy_data(boto3_session, data['Users'])
685
- transform_policy_data(managed_policy_data, PolicyType.managed.value)
686
- load_policy_data(neo4j_session, managed_policy_data, PolicyType.managed.value, aws_update_tag)
918
+ managed_policy_data = get_user_managed_policy_data(boto3_session, data["Users"])
919
+ transformed_policy_data = transform_policy_data(
920
+ managed_policy_data, PolicyType.managed.value
921
+ )
922
+ load_policy_data(
923
+ neo4j_session,
924
+ transformed_policy_data,
925
+ aws_update_tag,
926
+ current_aws_account_id,
927
+ )
687
928
 
688
929
 
689
930
  @timeit
690
931
  def sync_user_inline_policies(
691
- boto3_session: boto3.session.Session, data: Dict, neo4j_session: neo4j.Session,
932
+ boto3_session: boto3.Session,
933
+ data: Dict,
934
+ neo4j_session: neo4j.Session,
692
935
  aws_update_tag: int,
936
+ current_aws_account_id: str,
693
937
  ) -> None:
694
- policy_data = get_user_policy_data(boto3_session, data['Users'])
695
- transform_policy_data(policy_data, PolicyType.inline.value)
696
- load_policy_data(neo4j_session, policy_data, PolicyType.inline.value, aws_update_tag)
938
+ policy_data = get_user_policy_data(boto3_session, data["Users"])
939
+ transformed_policy_data = transform_policy_data(
940
+ policy_data, PolicyType.inline.value
941
+ )
942
+ load_policy_data(
943
+ neo4j_session,
944
+ transformed_policy_data,
945
+ aws_update_tag,
946
+ current_aws_account_id,
947
+ )
697
948
 
698
949
 
699
950
  @timeit
700
951
  def sync_groups(
701
- neo4j_session: neo4j.Session, boto3_session: boto3.session.Session, current_aws_account_id: str,
702
- aws_update_tag: int, common_job_parameters: Dict,
952
+ neo4j_session: neo4j.Session,
953
+ boto3_session: boto3.Session,
954
+ current_aws_account_id: str,
955
+ aws_update_tag: int,
956
+ common_job_parameters: Dict,
703
957
  ) -> None:
704
958
  logger.info("Syncing IAM groups for account '%s'.", current_aws_account_id)
705
959
  data = get_group_list_data(boto3_session)
706
- load_groups(neo4j_session, data['Groups'], current_aws_account_id, aws_update_tag)
707
-
708
- sync_groups_inline_policies(boto3_session, data, neo4j_session, aws_update_tag)
960
+ group_memberships = get_group_memberships(boto3_session, data["Groups"])
961
+ group_data = transform_groups(data["Groups"], group_memberships)
962
+ load_groups(neo4j_session, group_data, current_aws_account_id, aws_update_tag)
709
963
 
710
- sync_group_managed_policies(boto3_session, data, neo4j_session, aws_update_tag)
964
+ sync_groups_inline_policies(
965
+ boto3_session, data, neo4j_session, aws_update_tag, current_aws_account_id
966
+ )
711
967
 
712
- run_cleanup_job('aws_import_groups_cleanup.json', neo4j_session, common_job_parameters)
968
+ sync_group_managed_policies(
969
+ boto3_session, data, neo4j_session, aws_update_tag, current_aws_account_id
970
+ )
713
971
 
714
972
 
715
973
  def sync_group_managed_policies(
716
- boto3_session: boto3.session.Session, data: Dict, neo4j_session: neo4j.Session,
974
+ boto3_session: boto3.Session,
975
+ data: Dict,
976
+ neo4j_session: neo4j.Session,
717
977
  aws_update_tag: int,
978
+ current_aws_account_id: str,
718
979
  ) -> None:
719
980
  managed_policy_data = get_group_managed_policy_data(boto3_session, data["Groups"])
720
- transform_policy_data(managed_policy_data, PolicyType.managed.value)
721
- load_policy_data(neo4j_session, managed_policy_data, PolicyType.managed.value, aws_update_tag)
981
+ transformed_policy_data = transform_policy_data(
982
+ managed_policy_data, PolicyType.managed.value
983
+ )
984
+ load_policy_data(
985
+ neo4j_session,
986
+ transformed_policy_data,
987
+ aws_update_tag,
988
+ current_aws_account_id,
989
+ )
722
990
 
723
991
 
724
992
  def sync_groups_inline_policies(
725
- boto3_session: boto3.session.Session, data: Dict, neo4j_session: neo4j.Session,
993
+ boto3_session: boto3.Session,
994
+ data: Dict,
995
+ neo4j_session: neo4j.Session,
726
996
  aws_update_tag: int,
997
+ current_aws_account_id: str,
727
998
  ) -> None:
728
999
  policy_data = get_group_policy_data(boto3_session, data["Groups"])
729
- transform_policy_data(policy_data, PolicyType.inline.value)
730
- load_policy_data(neo4j_session, policy_data, PolicyType.inline.value, aws_update_tag)
1000
+ transformed_policy_data = transform_policy_data(
1001
+ policy_data, PolicyType.inline.value
1002
+ )
1003
+ load_policy_data(
1004
+ neo4j_session,
1005
+ transformed_policy_data,
1006
+ aws_update_tag,
1007
+ current_aws_account_id,
1008
+ )
1009
+
1010
+
1011
+ def load_external_aws_accounts(
1012
+ neo4j_session: neo4j.Session,
1013
+ external_aws_accounts: list[dict[str, Any]],
1014
+ aws_update_tag: int,
1015
+ ) -> None:
1016
+ load(
1017
+ neo4j_session,
1018
+ AWSAccountAWSRoleSchema(),
1019
+ external_aws_accounts,
1020
+ lastupdated=aws_update_tag,
1021
+ )
1022
+ # Ensure that the root principal exists for each external account.
1023
+ for account in external_aws_accounts:
1024
+ sync_root_principal(
1025
+ neo4j_session,
1026
+ account["id"],
1027
+ aws_update_tag,
1028
+ )
1029
+
1030
+
1031
+ @timeit
1032
+ def load_service_principals(
1033
+ neo4j_session: neo4j.Session,
1034
+ service_principals: list[dict[str, Any]],
1035
+ aws_update_tag: int,
1036
+ ) -> None:
1037
+ load(
1038
+ neo4j_session,
1039
+ AWSServicePrincipalSchema(),
1040
+ service_principals,
1041
+ lastupdated=aws_update_tag,
1042
+ )
1043
+
1044
+
1045
+ @timeit
1046
+ def load_role_data(
1047
+ neo4j_session: neo4j.Session,
1048
+ role_list: list[dict[str, Any]],
1049
+ current_aws_account_id: str,
1050
+ aws_update_tag: int,
1051
+ ) -> None:
1052
+ # Note that the account_id is set in the transform_roles function instead of from the `AWS_ID` kwarg like in other modules
1053
+ # because this can create root principals from other accounts based on data from the assume role policy document.
1054
+ load(
1055
+ neo4j_session,
1056
+ AWSRoleSchema(),
1057
+ role_list,
1058
+ lastupdated=aws_update_tag,
1059
+ AWS_ID=current_aws_account_id,
1060
+ )
1061
+
1062
+
1063
+ @timeit
1064
+ def load_federated_principals(
1065
+ neo4j_session: neo4j.Session,
1066
+ federated_principals: list[dict[str, Any]],
1067
+ current_aws_account_id: str,
1068
+ aws_update_tag: int,
1069
+ ) -> None:
1070
+ load(
1071
+ neo4j_session,
1072
+ AWSFederatedPrincipalSchema(),
1073
+ federated_principals,
1074
+ lastupdated=aws_update_tag,
1075
+ AWS_ID=current_aws_account_id,
1076
+ )
1077
+
1078
+
1079
+ @timeit
1080
+ def sync_role_assumptions(
1081
+ neo4j_session: neo4j.Session,
1082
+ data: dict[str, Any],
1083
+ current_aws_account_id: str,
1084
+ aws_update_tag: int,
1085
+ ) -> None:
1086
+ transformed = transform_role_trust_policies(data["Roles"], current_aws_account_id)
1087
+
1088
+ # Order matters here.
1089
+ # External accounts come first because they need to be created before the roles that trust them.
1090
+ load_external_aws_accounts(
1091
+ neo4j_session, transformed.external_aws_accounts, aws_update_tag
1092
+ )
1093
+ # Service principals e.g. arn = "ec2.amazonaws.com" come next because they're global
1094
+ load_service_principals(
1095
+ neo4j_session, transformed.service_principals, aws_update_tag
1096
+ )
1097
+ load_federated_principals(
1098
+ neo4j_session,
1099
+ transformed.federated_principals,
1100
+ current_aws_account_id,
1101
+ aws_update_tag,
1102
+ )
1103
+ load_role_data(
1104
+ neo4j_session, transformed.role_data, current_aws_account_id, aws_update_tag
1105
+ )
731
1106
 
732
1107
 
733
1108
  @timeit
734
1109
  def sync_roles(
735
- neo4j_session: neo4j.Session, boto3_session: boto3.session.Session, current_aws_account_id: str,
736
- aws_update_tag: int, common_job_parameters: Dict,
1110
+ neo4j_session: neo4j.Session,
1111
+ boto3_session: boto3.Session,
1112
+ current_aws_account_id: str,
1113
+ aws_update_tag: int,
1114
+ common_job_parameters: Dict,
737
1115
  ) -> None:
738
1116
  logger.info("Syncing IAM roles for account '%s'.", current_aws_account_id)
739
1117
  data = get_role_list_data(boto3_session)
740
- load_roles(neo4j_session, data['Roles'], current_aws_account_id, aws_update_tag)
741
1118
 
742
- sync_role_inline_policies(current_aws_account_id, boto3_session, data, neo4j_session, aws_update_tag)
1119
+ sync_role_assumptions(neo4j_session, data, current_aws_account_id, aws_update_tag)
743
1120
 
744
- sync_role_managed_policies(current_aws_account_id, boto3_session, data, neo4j_session, aws_update_tag)
1121
+ sync_role_inline_policies(
1122
+ current_aws_account_id,
1123
+ boto3_session,
1124
+ data,
1125
+ neo4j_session,
1126
+ aws_update_tag,
1127
+ )
745
1128
 
746
- run_cleanup_job('aws_import_roles_cleanup.json', neo4j_session, common_job_parameters)
1129
+ sync_role_managed_policies(
1130
+ current_aws_account_id,
1131
+ boto3_session,
1132
+ data,
1133
+ neo4j_session,
1134
+ aws_update_tag,
1135
+ )
747
1136
 
748
1137
 
749
1138
  def sync_role_managed_policies(
750
- current_aws_account_id: str, boto3_session: boto3.session.Session, data: Dict,
751
- neo4j_session: neo4j.Session, aws_update_tag: int,
1139
+ current_aws_account_id: str,
1140
+ boto3_session: boto3.Session,
1141
+ data: Dict,
1142
+ neo4j_session: neo4j.Session,
1143
+ aws_update_tag: int,
752
1144
  ) -> None:
753
- logger.info("Syncing IAM role managed policies for account '%s'.", current_aws_account_id)
1145
+ logger.info(
1146
+ "Syncing IAM role managed policies for account '%s'.",
1147
+ current_aws_account_id,
1148
+ )
754
1149
  managed_policy_data = get_role_managed_policy_data(boto3_session, data["Roles"])
755
- transform_policy_data(managed_policy_data, PolicyType.managed.value)
756
- load_policy_data(neo4j_session, managed_policy_data, PolicyType.managed.value, aws_update_tag)
1150
+ transformed_policy_data = transform_policy_data(
1151
+ managed_policy_data, PolicyType.managed.value
1152
+ )
1153
+ load_policy_data(
1154
+ neo4j_session,
1155
+ transformed_policy_data,
1156
+ aws_update_tag,
1157
+ current_aws_account_id,
1158
+ )
757
1159
 
758
1160
 
759
1161
  def sync_role_inline_policies(
760
- current_aws_account_id: str, boto3_session: boto3.session.Session, data: Dict,
761
- neo4j_session: neo4j.Session, aws_update_tag: int,
1162
+ current_aws_account_id: str,
1163
+ boto3_session: boto3.Session,
1164
+ data: Dict,
1165
+ neo4j_session: neo4j.Session,
1166
+ aws_update_tag: int,
762
1167
  ) -> None:
763
- logger.info("Syncing IAM role inline policies for account '%s'.", current_aws_account_id)
1168
+ logger.info(
1169
+ "Syncing IAM role inline policies for account '%s'.",
1170
+ current_aws_account_id,
1171
+ )
764
1172
  inline_policy_data = get_role_policy_data(boto3_session, data["Roles"])
765
- transform_policy_data(inline_policy_data, PolicyType.inline.value)
766
- load_policy_data(neo4j_session, inline_policy_data, PolicyType.inline.value, aws_update_tag)
1173
+ transformed_policy_data = transform_policy_data(
1174
+ inline_policy_data, PolicyType.inline.value
1175
+ )
1176
+ load_policy_data(
1177
+ neo4j_session,
1178
+ transformed_policy_data,
1179
+ aws_update_tag,
1180
+ current_aws_account_id,
1181
+ )
1182
+
1183
+
1184
+ def _get_policies_in_current_account(
1185
+ neo4j_session: neo4j.Session, current_aws_account_id: str
1186
+ ) -> list[str]:
1187
+ query = """
1188
+ MATCH (:AWSAccount{id: $AWS_ID})-[:RESOURCE]->(p:AWSPolicy)
1189
+ RETURN p.id
1190
+ """
1191
+ return [
1192
+ str(policy_id)
1193
+ for policy_id in neo4j_session.execute_read(
1194
+ read_list_of_values_tx,
1195
+ query,
1196
+ AWS_ID=current_aws_account_id,
1197
+ )
1198
+ ]
1199
+
1200
+
1201
+ def _get_principals_with_pols_in_current_account(
1202
+ neo4j_session: neo4j.Session, current_aws_account_id: str
1203
+ ) -> list[str]:
1204
+ query = """
1205
+ MATCH (:AWSAccount{id: $AWS_ID})-[:RESOURCE]->(p:AWSPrincipal)
1206
+ WHERE (p)-[:POLICY]->(:AWSPolicy)
1207
+ RETURN p.id
1208
+ """
1209
+ return [
1210
+ str(principal_id)
1211
+ for principal_id in neo4j_session.execute_read(
1212
+ read_list_of_values_tx,
1213
+ query,
1214
+ AWS_ID=current_aws_account_id,
1215
+ )
1216
+ ]
767
1217
 
768
1218
 
769
1219
  @timeit
770
- def sync_group_memberships(
771
- neo4j_session: neo4j.Session, boto3_session: boto3.session.Session,
772
- current_aws_account_id: str, aws_update_tag: int, common_job_parameters: Dict,
773
- ) -> None:
774
- logger.info("Syncing IAM group membership for account '%s'.", current_aws_account_id)
775
- query = "MATCH (group:AWSGroup)<-[:RESOURCE]-(:AWSAccount{id: $AWS_ACCOUNT_ID}) " \
776
- "return group.name as name, group.arn as arn;"
777
- groups = neo4j_session.run(query, AWS_ACCOUNT_ID=current_aws_account_id)
778
- groups_membership = {group["arn"]: get_group_membership_data(boto3_session, group["name"]) for group in groups}
779
- load_group_memberships(neo4j_session, groups_membership, aws_update_tag)
780
- run_cleanup_job(
781
- 'aws_import_groups_membership_cleanup.json',
782
- neo4j_session,
783
- common_job_parameters,
1220
+ def cleanup_iam(neo4j_session: neo4j.Session, common_job_parameters: Dict) -> None:
1221
+ # List all policies in the current account
1222
+ policy_ids = _get_policies_in_current_account(
1223
+ neo4j_session, common_job_parameters["AWS_ID"]
784
1224
  )
785
1225
 
1226
+ # for each policy id, run the cleanup job for the policy statements, passing the policy id as a kwarg.
1227
+ for policy_id in policy_ids:
1228
+ GraphJob.from_node_schema(
1229
+ AWSPolicyStatementSchema(),
1230
+ {**common_job_parameters, "POLICY_ID": policy_id},
1231
+ ).run(
1232
+ neo4j_session,
1233
+ )
786
1234
 
787
- @timeit
788
- def sync_user_access_keys(
789
- neo4j_session: neo4j.Session, boto3_session: boto3.session.Session,
790
- current_aws_account_id: str, aws_update_tag: int, common_job_parameters: Dict,
1235
+ # Next, clean up the policies
1236
+ # Note that managed policies don't have a sub resource relationship. This means that we will only clean up
1237
+ # stale relationships and not stale AWSManagedPolicy nodes. This is because AWSManagedPolicy nodes are global
1238
+ # to AWS and it is possible for them to be shared across accounts, so if we cleaned up an AWSManagedPolicy node
1239
+ # for one account, it would be erroneously deleted for all accounts. Instead, we just clean up the relationships.
1240
+ GraphJob.from_node_schema(AWSManagedPolicySchema(), common_job_parameters).run(
1241
+ neo4j_session
1242
+ )
1243
+
1244
+ # Inline policies are simpler in that they are scoped to a single principal and therefore attached to that
1245
+ # principal's account. This means that this operation will clean up stale AWSInlinePolicy nodes.
1246
+ GraphJob.from_node_schema(AWSInlinePolicySchema(), common_job_parameters).run(
1247
+ neo4j_session
1248
+ )
1249
+
1250
+ # Clean up roles before federated and service principals
1251
+ GraphJob.from_node_schema(AWSRoleSchema(), common_job_parameters).run(neo4j_session)
1252
+ GraphJob.from_node_schema(AWSFederatedPrincipalSchema(), common_job_parameters).run(
1253
+ neo4j_session
1254
+ )
1255
+ GraphJob.from_node_schema(AWSServicePrincipalSchema(), common_job_parameters).run(
1256
+ neo4j_session
1257
+ )
1258
+ GraphJob.from_node_schema(AWSUserSchema(), common_job_parameters).run(neo4j_session)
1259
+ GraphJob.from_node_schema(AWSGroupSchema(), common_job_parameters).run(
1260
+ neo4j_session
1261
+ )
1262
+
1263
+
1264
+ def sync_root_principal(
1265
+ neo4j_session: neo4j.Session, current_aws_account_id: str, aws_update_tag: int
791
1266
  ) -> None:
792
- logger.info("Syncing IAM user access keys for account '%s'.", current_aws_account_id)
793
- query = "MATCH (user:AWSUser)<-[:RESOURCE]-(:AWSAccount{id: $AWS_ACCOUNT_ID}) " \
794
- "RETURN user.name as name, user.arn as arn"
795
- for user in neo4j_session.run(query, AWS_ACCOUNT_ID=current_aws_account_id):
796
- access_keys = get_account_access_key_data(boto3_session, user["name"])
797
- if access_keys:
798
- account_access_keys = {user["arn"]: access_keys}
799
- load_user_access_keys(neo4j_session, account_access_keys, aws_update_tag)
800
- run_cleanup_job(
801
- 'aws_import_account_access_key_cleanup.json',
1267
+ """
1268
+ In the current account, create a node for the AWS root principal "arn:aws:iam::<account_id>:root".
1269
+
1270
+ If a role X trusts the root principal in an account A, then any other role Y in A can assume X.
1271
+
1272
+ Note that this is _not_ the same as the AWS root user. The root principal doesn't show up in any
1273
+ APIs except for assumerole trust policies.
1274
+ """
1275
+ load(
802
1276
  neo4j_session,
803
- common_job_parameters,
1277
+ AWSRootPrincipalSchema(),
1278
+ [{"arn": f"arn:aws:iam::{current_aws_account_id}:root"}],
1279
+ lastupdated=aws_update_tag,
1280
+ AWS_ID=current_aws_account_id,
804
1281
  )
805
1282
 
806
1283
 
807
1284
  @timeit
808
1285
  def sync(
809
- neo4j_session: neo4j.Session, boto3_session: boto3.session.Session, regions: List[str], current_aws_account_id: str,
810
- update_tag: int, common_job_parameters: Dict,
1286
+ neo4j_session: neo4j.Session,
1287
+ boto3_session: boto3.Session,
1288
+ regions: List[str],
1289
+ current_aws_account_id: str,
1290
+ update_tag: int,
1291
+ common_job_parameters: Dict,
811
1292
  ) -> None:
812
1293
  logger.info("Syncing IAM for account '%s'.", current_aws_account_id)
813
1294
  # This module only syncs IAM information that is in use.
814
1295
  # As such only policies that are attached to a user, role or group are synced
815
- sync_users(neo4j_session, boto3_session, current_aws_account_id, update_tag, common_job_parameters)
816
- sync_groups(neo4j_session, boto3_session, current_aws_account_id, update_tag, common_job_parameters)
817
- sync_roles(neo4j_session, boto3_session, current_aws_account_id, update_tag, common_job_parameters)
818
- sync_group_memberships(neo4j_session, boto3_session, current_aws_account_id, update_tag, common_job_parameters)
819
- sync_assumerole_relationships(neo4j_session, current_aws_account_id, update_tag, common_job_parameters)
820
- sync_user_access_keys(neo4j_session, boto3_session, current_aws_account_id, update_tag, common_job_parameters)
821
- run_cleanup_job('aws_import_principals_cleanup.json', neo4j_session, common_job_parameters)
1296
+ sync_root_principal(
1297
+ neo4j_session,
1298
+ current_aws_account_id,
1299
+ update_tag,
1300
+ )
1301
+ sync_users(
1302
+ neo4j_session,
1303
+ boto3_session,
1304
+ current_aws_account_id,
1305
+ update_tag,
1306
+ common_job_parameters,
1307
+ )
1308
+ sync_groups(
1309
+ neo4j_session,
1310
+ boto3_session,
1311
+ current_aws_account_id,
1312
+ update_tag,
1313
+ common_job_parameters,
1314
+ )
1315
+ sync_roles(
1316
+ neo4j_session,
1317
+ boto3_session,
1318
+ current_aws_account_id,
1319
+ update_tag,
1320
+ common_job_parameters,
1321
+ )
1322
+ sync_assumerole_relationships(
1323
+ neo4j_session,
1324
+ current_aws_account_id,
1325
+ update_tag,
1326
+ common_job_parameters,
1327
+ )
1328
+ sync_user_access_keys(
1329
+ neo4j_session,
1330
+ boto3_session,
1331
+ current_aws_account_id,
1332
+ update_tag,
1333
+ common_job_parameters,
1334
+ )
1335
+ cleanup_iam(neo4j_session, common_job_parameters)
822
1336
  merge_module_sync_metadata(
823
1337
  neo4j_session,
824
- group_type='AWSAccount',
1338
+ group_type="AWSAccount",
825
1339
  group_id=current_aws_account_id,
826
- synced_type='AWSPrincipal',
1340
+ synced_type="AWSPrincipal",
827
1341
  update_tag=update_tag,
828
1342
  stat_handler=stat_handler,
829
1343
  )