cartography 0.102.0rc2__py3-none-any.whl → 0.103.0rc1__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.

Potentially problematic release.


This version of cartography might be problematic. Click here for more details.

Files changed (251) hide show
  1. cartography/__main__.py +1 -2
  2. cartography/_version.py +2 -2
  3. cartography/cli.py +302 -253
  4. cartography/client/core/tx.py +39 -18
  5. cartography/config.py +4 -0
  6. cartography/driftdetect/__main__.py +1 -2
  7. cartography/driftdetect/add_shortcut.py +10 -2
  8. cartography/driftdetect/cli.py +71 -75
  9. cartography/driftdetect/detect_deviations.py +7 -3
  10. cartography/driftdetect/get_states.py +20 -8
  11. cartography/driftdetect/model.py +5 -5
  12. cartography/driftdetect/serializers.py +8 -6
  13. cartography/driftdetect/storage.py +2 -2
  14. cartography/graph/cleanupbuilder.py +35 -15
  15. cartography/graph/job.py +46 -17
  16. cartography/graph/querybuilder.py +165 -80
  17. cartography/graph/statement.py +35 -26
  18. cartography/intel/analysis.py +4 -1
  19. cartography/intel/aws/__init__.py +114 -55
  20. cartography/intel/aws/apigateway.py +134 -63
  21. cartography/intel/aws/cloudtrail.py +127 -0
  22. cartography/intel/aws/config.py +56 -20
  23. cartography/intel/aws/dynamodb.py +108 -40
  24. cartography/intel/aws/ec2/__init__.py +2 -2
  25. cartography/intel/aws/ec2/auto_scaling_groups.py +181 -78
  26. cartography/intel/aws/ec2/elastic_ip_addresses.py +41 -13
  27. cartography/intel/aws/ec2/images.py +49 -20
  28. cartography/intel/aws/ec2/instances.py +234 -136
  29. cartography/intel/aws/ec2/internet_gateways.py +40 -11
  30. cartography/intel/aws/ec2/key_pairs.py +44 -20
  31. cartography/intel/aws/ec2/launch_templates.py +101 -59
  32. cartography/intel/aws/ec2/load_balancer_v2s.py +104 -39
  33. cartography/intel/aws/ec2/load_balancers.py +82 -42
  34. cartography/intel/aws/ec2/network_acls.py +89 -65
  35. cartography/intel/aws/ec2/network_interfaces.py +146 -87
  36. cartography/intel/aws/ec2/reserved_instances.py +45 -16
  37. cartography/intel/aws/ec2/route_tables.py +138 -98
  38. cartography/intel/aws/ec2/security_groups.py +71 -21
  39. cartography/intel/aws/ec2/snapshots.py +61 -22
  40. cartography/intel/aws/ec2/subnets.py +54 -18
  41. cartography/intel/aws/ec2/tgw.py +100 -34
  42. cartography/intel/aws/ec2/util.py +1 -1
  43. cartography/intel/aws/ec2/volumes.py +69 -41
  44. cartography/intel/aws/ec2/vpc.py +37 -12
  45. cartography/intel/aws/ec2/vpc_peerings.py +83 -24
  46. cartography/intel/aws/ecr.py +88 -32
  47. cartography/intel/aws/ecs.py +83 -47
  48. cartography/intel/aws/eks.py +55 -29
  49. cartography/intel/aws/elasticache.py +42 -18
  50. cartography/intel/aws/elasticsearch.py +57 -20
  51. cartography/intel/aws/emr.py +61 -23
  52. cartography/intel/aws/iam.py +401 -145
  53. cartography/intel/aws/iam_instance_profiles.py +22 -22
  54. cartography/intel/aws/identitycenter.py +71 -37
  55. cartography/intel/aws/inspector.py +159 -89
  56. cartography/intel/aws/kms.py +92 -38
  57. cartography/intel/aws/lambda_function.py +103 -34
  58. cartography/intel/aws/organizations.py +30 -10
  59. cartography/intel/aws/permission_relationships.py +133 -51
  60. cartography/intel/aws/rds.py +249 -85
  61. cartography/intel/aws/redshift.py +107 -46
  62. cartography/intel/aws/resourcegroupstaggingapi.py +120 -66
  63. cartography/intel/aws/resources.py +53 -46
  64. cartography/intel/aws/route53.py +108 -61
  65. cartography/intel/aws/s3.py +168 -83
  66. cartography/intel/aws/s3accountpublicaccessblock.py +157 -0
  67. cartography/intel/aws/secretsmanager.py +24 -12
  68. cartography/intel/aws/securityhub.py +20 -9
  69. cartography/intel/aws/sns.py +166 -0
  70. cartography/intel/aws/sqs.py +60 -28
  71. cartography/intel/aws/ssm.py +70 -30
  72. cartography/intel/aws/util/arns.py +7 -7
  73. cartography/intel/aws/util/common.py +31 -4
  74. cartography/intel/azure/__init__.py +78 -19
  75. cartography/intel/azure/compute.py +101 -27
  76. cartography/intel/azure/cosmosdb.py +496 -170
  77. cartography/intel/azure/sql.py +296 -105
  78. cartography/intel/azure/storage.py +322 -113
  79. cartography/intel/azure/subscription.py +39 -23
  80. cartography/intel/azure/tenant.py +13 -4
  81. cartography/intel/azure/util/credentials.py +95 -55
  82. cartography/intel/bigfix/__init__.py +2 -2
  83. cartography/intel/bigfix/computers.py +93 -65
  84. cartography/intel/create_indexes.py +3 -2
  85. cartography/intel/crowdstrike/__init__.py +11 -9
  86. cartography/intel/crowdstrike/endpoints.py +5 -1
  87. cartography/intel/crowdstrike/spotlight.py +8 -3
  88. cartography/intel/cve/__init__.py +46 -13
  89. cartography/intel/cve/feed.py +48 -12
  90. cartography/intel/digitalocean/__init__.py +22 -13
  91. cartography/intel/digitalocean/compute.py +75 -108
  92. cartography/intel/digitalocean/management.py +44 -80
  93. cartography/intel/digitalocean/platform.py +48 -43
  94. cartography/intel/dns.py +36 -10
  95. cartography/intel/duo/__init__.py +21 -16
  96. cartography/intel/duo/api_host.py +14 -9
  97. cartography/intel/duo/endpoints.py +50 -45
  98. cartography/intel/duo/groups.py +18 -14
  99. cartography/intel/duo/phones.py +37 -34
  100. cartography/intel/duo/tokens.py +26 -23
  101. cartography/intel/duo/users.py +54 -50
  102. cartography/intel/duo/web_authn_credentials.py +30 -25
  103. cartography/intel/entra/__init__.py +25 -7
  104. cartography/intel/entra/ou.py +112 -0
  105. cartography/intel/entra/users.py +69 -63
  106. cartography/intel/gcp/__init__.py +185 -49
  107. cartography/intel/gcp/compute.py +418 -231
  108. cartography/intel/gcp/crm.py +96 -43
  109. cartography/intel/gcp/dns.py +60 -19
  110. cartography/intel/gcp/gke.py +72 -38
  111. cartography/intel/gcp/iam.py +61 -41
  112. cartography/intel/gcp/storage.py +84 -55
  113. cartography/intel/github/__init__.py +13 -11
  114. cartography/intel/github/repos.py +270 -137
  115. cartography/intel/github/teams.py +170 -88
  116. cartography/intel/github/users.py +70 -39
  117. cartography/intel/github/util.py +36 -34
  118. cartography/intel/gsuite/__init__.py +47 -26
  119. cartography/intel/gsuite/api.py +73 -30
  120. cartography/intel/jamf/__init__.py +19 -1
  121. cartography/intel/jamf/computers.py +30 -7
  122. cartography/intel/jamf/util.py +7 -2
  123. cartography/intel/kandji/__init__.py +6 -3
  124. cartography/intel/kandji/devices.py +14 -8
  125. cartography/intel/kubernetes/namespaces.py +7 -4
  126. cartography/intel/kubernetes/pods.py +7 -4
  127. cartography/intel/kubernetes/services.py +8 -4
  128. cartography/intel/lastpass/__init__.py +2 -2
  129. cartography/intel/lastpass/users.py +23 -12
  130. cartography/intel/oci/__init__.py +44 -11
  131. cartography/intel/oci/iam.py +134 -38
  132. cartography/intel/oci/organizations.py +13 -6
  133. cartography/intel/oci/utils.py +43 -20
  134. cartography/intel/okta/__init__.py +66 -15
  135. cartography/intel/okta/applications.py +42 -20
  136. cartography/intel/okta/awssaml.py +93 -33
  137. cartography/intel/okta/factors.py +16 -4
  138. cartography/intel/okta/groups.py +56 -29
  139. cartography/intel/okta/organization.py +5 -1
  140. cartography/intel/okta/origins.py +6 -2
  141. cartography/intel/okta/roles.py +15 -5
  142. cartography/intel/okta/users.py +20 -8
  143. cartography/intel/okta/utils.py +6 -4
  144. cartography/intel/pagerduty/__init__.py +8 -7
  145. cartography/intel/pagerduty/escalation_policies.py +18 -6
  146. cartography/intel/pagerduty/schedules.py +12 -4
  147. cartography/intel/pagerduty/services.py +11 -4
  148. cartography/intel/pagerduty/teams.py +8 -3
  149. cartography/intel/pagerduty/users.py +3 -1
  150. cartography/intel/pagerduty/vendors.py +3 -1
  151. cartography/intel/semgrep/__init__.py +24 -6
  152. cartography/intel/semgrep/dependencies.py +50 -28
  153. cartography/intel/semgrep/deployment.py +3 -1
  154. cartography/intel/semgrep/findings.py +42 -18
  155. cartography/intel/snipeit/__init__.py +17 -3
  156. cartography/intel/snipeit/asset.py +12 -6
  157. cartography/intel/snipeit/user.py +8 -5
  158. cartography/intel/snipeit/util.py +9 -4
  159. cartography/models/aws/apigateway.py +21 -17
  160. cartography/models/aws/apigatewaycertificate.py +28 -22
  161. cartography/models/aws/apigatewayresource.py +28 -20
  162. cartography/models/aws/apigatewaystage.py +33 -25
  163. cartography/models/aws/cloudtrail/__init__.py +0 -0
  164. cartography/models/aws/cloudtrail/trail.py +61 -0
  165. cartography/models/aws/dynamodb/gsi.py +30 -22
  166. cartography/models/aws/dynamodb/tables.py +25 -17
  167. cartography/models/aws/ec2/auto_scaling_groups.py +102 -82
  168. cartography/models/aws/ec2/images.py +36 -34
  169. cartography/models/aws/ec2/instances.py +51 -45
  170. cartography/models/aws/ec2/keypair.py +21 -16
  171. cartography/models/aws/ec2/keypair_instance.py +28 -21
  172. cartography/models/aws/ec2/launch_configurations.py +30 -26
  173. cartography/models/aws/ec2/launch_template_versions.py +48 -38
  174. cartography/models/aws/ec2/launch_templates.py +21 -17
  175. cartography/models/aws/ec2/load_balancer_listeners.py +27 -23
  176. cartography/models/aws/ec2/load_balancers.py +47 -37
  177. cartography/models/aws/ec2/network_acl_rules.py +38 -30
  178. cartography/models/aws/ec2/network_acls.py +38 -29
  179. cartography/models/aws/ec2/networkinterface_instance.py +52 -39
  180. cartography/models/aws/ec2/networkinterfaces.py +53 -37
  181. cartography/models/aws/ec2/privateip_networkinterface.py +32 -22
  182. cartography/models/aws/ec2/reservations.py +18 -14
  183. cartography/models/aws/ec2/route_table_associations.py +44 -34
  184. cartography/models/aws/ec2/route_tables.py +50 -43
  185. cartography/models/aws/ec2/routes.py +45 -37
  186. cartography/models/aws/ec2/securitygroup_instance.py +29 -20
  187. cartography/models/aws/ec2/securitygroup_networkinterface.py +24 -15
  188. cartography/models/aws/ec2/subnet_instance.py +24 -19
  189. cartography/models/aws/ec2/subnet_networkinterface.py +40 -31
  190. cartography/models/aws/ec2/volumes.py +47 -40
  191. cartography/models/aws/eks/clusters.py +23 -21
  192. cartography/models/aws/emr.py +32 -30
  193. cartography/models/aws/iam/instanceprofile.py +33 -24
  194. cartography/models/aws/identitycenter/awsidentitycenter.py +18 -14
  195. cartography/models/aws/identitycenter/awspermissionset.py +37 -29
  196. cartography/models/aws/identitycenter/awsssouser.py +23 -21
  197. cartography/models/aws/inspector/findings.py +77 -65
  198. cartography/models/aws/inspector/packages.py +35 -29
  199. cartography/models/aws/s3/__init__.py +0 -0
  200. cartography/models/aws/s3/account_public_access_block.py +51 -0
  201. cartography/models/aws/sns/__init__.py +0 -0
  202. cartography/models/aws/sns/topic.py +50 -0
  203. cartography/models/aws/ssm/instance_information.py +51 -39
  204. cartography/models/aws/ssm/instance_patch.py +32 -26
  205. cartography/models/bigfix/bigfix_computer.py +42 -38
  206. cartography/models/bigfix/bigfix_root.py +3 -3
  207. cartography/models/core/common.py +12 -10
  208. cartography/models/core/nodes.py +5 -2
  209. cartography/models/core/relationships.py +14 -6
  210. cartography/models/crowdstrike/hosts.py +37 -35
  211. cartography/models/cve/cve.py +34 -32
  212. cartography/models/cve/cve_feed.py +6 -6
  213. cartography/models/digitalocean/__init__.py +0 -0
  214. cartography/models/digitalocean/account.py +21 -0
  215. cartography/models/digitalocean/droplet.py +56 -0
  216. cartography/models/digitalocean/project.py +48 -0
  217. cartography/models/duo/api_host.py +3 -3
  218. cartography/models/duo/endpoint.py +43 -41
  219. cartography/models/duo/group.py +14 -14
  220. cartography/models/duo/phone.py +27 -27
  221. cartography/models/duo/token.py +16 -16
  222. cartography/models/duo/user.py +46 -44
  223. cartography/models/duo/web_authn_credential.py +27 -19
  224. cartography/models/entra/ou.py +48 -0
  225. cartography/models/entra/tenant.py +24 -18
  226. cartography/models/entra/user.py +64 -48
  227. cartography/models/gcp/iam.py +23 -23
  228. cartography/models/github/orgs.py +5 -4
  229. cartography/models/github/teams.py +37 -31
  230. cartography/models/github/users.py +34 -23
  231. cartography/models/kandji/device.py +22 -16
  232. cartography/models/kandji/tenant.py +6 -4
  233. cartography/models/lastpass/tenant.py +3 -3
  234. cartography/models/lastpass/user.py +32 -28
  235. cartography/models/semgrep/dependencies.py +36 -24
  236. cartography/models/semgrep/deployment.py +5 -5
  237. cartography/models/semgrep/findings.py +58 -42
  238. cartography/models/semgrep/locations.py +27 -21
  239. cartography/models/snipeit/asset.py +30 -21
  240. cartography/models/snipeit/tenant.py +6 -4
  241. cartography/models/snipeit/user.py +19 -12
  242. cartography/stats.py +3 -3
  243. cartography/sync.py +107 -31
  244. cartography/util.py +84 -62
  245. {cartography-0.102.0rc2.dist-info → cartography-0.103.0rc1.dist-info}/METADATA +3 -14
  246. cartography-0.103.0rc1.dist-info/RECORD +396 -0
  247. {cartography-0.102.0rc2.dist-info → cartography-0.103.0rc1.dist-info}/WHEEL +1 -1
  248. cartography-0.102.0rc2.dist-info/RECORD +0 -381
  249. {cartography-0.102.0rc2.dist-info → cartography-0.103.0rc1.dist-info}/entry_points.txt +0 -0
  250. {cartography-0.102.0rc2.dist-info → cartography-0.103.0rc1.dist-info}/licenses/LICENSE +0 -0
  251. {cartography-0.102.0rc2.dist-info → cartography-0.103.0rc1.dist-info}/top_level.txt +0 -0
@@ -19,7 +19,7 @@ logger = logging.getLogger(__name__)
19
19
 
20
20
 
21
21
  def evaluate_clause(clause: str, match: str) -> bool:
22
- """ Evaluates the a clause in IAM. Clauses can be AWS [not]actions and [not]resources
22
+ """Evaluates the a clause in IAM. Clauses can be AWS [not]actions and [not]resources
23
23
 
24
24
  Arguments:
25
25
  clause {str, re.Pattern} -- The clause you are evaluating against. Clauses can use
@@ -36,9 +36,9 @@ def evaluate_clause(clause: str, match: str) -> bool:
36
36
 
37
37
  def evaluate_notaction_for_permission(statement: Dict, permission: str) -> bool:
38
38
  """Return whether an IAM 'notaction' clause in the given statement applies to the item"""
39
- if 'notaction' not in statement:
39
+ if "notaction" not in statement:
40
40
  return False
41
- for clause in statement['notaction']:
41
+ for clause in statement["notaction"]:
42
42
  if evaluate_clause(clause, permission):
43
43
  return True
44
44
  return False
@@ -46,9 +46,9 @@ def evaluate_notaction_for_permission(statement: Dict, permission: str) -> bool:
46
46
 
47
47
  def evaluate_action_for_permission(statement: Dict, permission: str) -> bool:
48
48
  """Return whether an IAM 'action' clause in the given statement applies to the permission"""
49
- if 'action' not in statement:
49
+ if "action" not in statement:
50
50
  return True
51
- for clause in statement['action']:
51
+ for clause in statement["action"]:
52
52
  if evaluate_clause(clause, permission):
53
53
  return True
54
54
  return False
@@ -56,9 +56,9 @@ def evaluate_action_for_permission(statement: Dict, permission: str) -> bool:
56
56
 
57
57
  def evaluate_resource_for_permission(statement: Dict, resource_arn: str) -> bool:
58
58
  """Return whether the given IAM 'resource' statement applies to the resource_arn"""
59
- if 'resource' not in statement:
59
+ if "resource" not in statement:
60
60
  return False
61
- for clause in statement['resource']:
61
+ for clause in statement["resource"]:
62
62
  if evaluate_clause(clause, resource_arn):
63
63
  return True
64
64
  return False
@@ -66,16 +66,20 @@ def evaluate_resource_for_permission(statement: Dict, resource_arn: str) -> bool
66
66
 
67
67
  def evaluate_notresource_for_permission(statement: Dict, resource_arn: str) -> bool:
68
68
  """Return whether an IAM 'notresource' clause in the given statement applies to the resource_arn"""
69
- if 'notresource' not in statement:
69
+ if "notresource" not in statement:
70
70
  return False
71
- for clause in statement['notresource']:
71
+ for clause in statement["notresource"]:
72
72
  if evaluate_clause(clause, resource_arn):
73
73
  return True
74
74
  return False
75
75
 
76
76
 
77
- def evaluate_statements_for_permission(statements: List[Dict], permission: str, resource_arn: str) -> bool:
78
- """ Evaluate an entire statement for a specific permission against a resource
77
+ def evaluate_statements_for_permission(
78
+ statements: List[Dict],
79
+ permission: str,
80
+ resource_arn: str,
81
+ ) -> bool:
82
+ """Evaluate an entire statement for a specific permission against a resource
79
83
 
80
84
  Arguments:
81
85
  statements {[dict]} -- The list of statements to be evaluated
@@ -97,9 +101,11 @@ def evaluate_statements_for_permission(statements: List[Dict], permission: str,
97
101
 
98
102
 
99
103
  def evaluate_policy_for_permissions(
100
- statements: List[Dict], permissions: List[str], resource_arn: str,
104
+ statements: List[Dict],
105
+ permissions: List[str],
106
+ resource_arn: str,
101
107
  ) -> Tuple[bool, bool]:
102
- """ Evaluates an entire policy for specific permissions to a resource.
108
+ """Evaluates an entire policy for specific permissions to a resource.
103
109
  AWS Policy evaluation reference
104
110
  https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic.html
105
111
 
@@ -118,19 +124,31 @@ def evaluate_policy_for_permissions(
118
124
  allow_statements = [s for s in statements if s["effect"] == "Allow"]
119
125
  deny_statements = [s for s in statements if s["effect"] == "Deny"]
120
126
  for permission in permissions:
121
- if evaluate_statements_for_permission(deny_statements, permission, resource_arn):
127
+ if evaluate_statements_for_permission(
128
+ deny_statements,
129
+ permission,
130
+ resource_arn,
131
+ ):
122
132
  # The action explicitly denied then no other policy can override it
123
133
  return False, True
124
134
  else:
125
- if evaluate_statements_for_permission(allow_statements, permission, resource_arn):
135
+ if evaluate_statements_for_permission(
136
+ allow_statements,
137
+ permission,
138
+ resource_arn,
139
+ ):
126
140
  # The action is allowed by this policy
127
141
  return True, False
128
142
  # The action is not allowed by this policy, but not specifically denied either
129
143
  return False, False
130
144
 
131
145
 
132
- def principal_allowed_on_resource(policies: Dict, resource_arn: str, permissions: List[str]) -> bool:
133
- """ Evaluates an enture set of policies for a specific resource for a specific permission.
146
+ def principal_allowed_on_resource(
147
+ policies: Dict,
148
+ resource_arn: str,
149
+ permissions: List[str],
150
+ ) -> bool:
151
+ """Evaluates an enture set of policies for a specific resource for a specific permission.
134
152
 
135
153
 
136
154
  Arguments:
@@ -145,7 +163,11 @@ def principal_allowed_on_resource(policies: Dict, resource_arn: str, permissions
145
163
  raise ValueError("permissions is not a list")
146
164
  granted = False
147
165
  for _, statements in policies.items():
148
- allowed, explicit_deny = evaluate_policy_for_permissions(statements, permissions, resource_arn)
166
+ allowed, explicit_deny = evaluate_policy_for_permissions(
167
+ statements,
168
+ permissions,
169
+ resource_arn,
170
+ )
149
171
 
150
172
  if explicit_deny:
151
173
 
@@ -157,9 +179,11 @@ def principal_allowed_on_resource(policies: Dict, resource_arn: str, permissions
157
179
 
158
180
 
159
181
  def calculate_permission_relationships(
160
- principals: Dict, resource_arns: List[str], permissions: List[str],
182
+ principals: Dict,
183
+ resource_arns: List[str],
184
+ permissions: List[str],
161
185
  ) -> List[Dict]:
162
- """ Evaluate principals permissions to resources
186
+ """Evaluate principals permissions to resources
163
187
  This currently only evaluates policies on IAM principals. It does not take into account
164
188
  Resource Policies - Policies attached to the resource instead of the IAM principal
165
189
  Permission Boundaries - Boundaries for an IAM principal
@@ -180,12 +204,14 @@ def calculate_permission_relationships(
180
204
  for resource_arn in resource_arns:
181
205
  for principal_arn, policies in principals.items():
182
206
  if principal_allowed_on_resource(policies, resource_arn, permissions):
183
- allowed_mappings.append({"principal_arn": principal_arn, "resource_arn": resource_arn})
207
+ allowed_mappings.append(
208
+ {"principal_arn": principal_arn, "resource_arn": resource_arn},
209
+ )
184
210
  return allowed_mappings
185
211
 
186
212
 
187
213
  def parse_statement_node(node_group: List[Any]) -> List[Any]:
188
- """ Parse a dict from group of Neo4J node
214
+ """Parse a dict from group of Neo4J node
189
215
 
190
216
  Arguments:
191
217
  node_group {[Neo4j.Node]} -- the node to parse
@@ -197,7 +223,7 @@ def parse_statement_node(node_group: List[Any]) -> List[Any]:
197
223
 
198
224
 
199
225
  def compile_regex(item: str) -> Pattern:
200
- r""" Compile a clause into a regex. Clause checking in AWS is case insensitive
226
+ r"""Compile a clause into a regex. Clause checking in AWS is case insensitive
201
227
  The following regex symbols will be replaced to make AWS * and ? matching a regex
202
228
  * -> .* (wildcard)
203
229
  ? -> .? (single character wildcard)
@@ -225,7 +251,7 @@ def compile_regex(item: str) -> Pattern:
225
251
 
226
252
 
227
253
  def compile_statement(statements: List[Any]) -> List[Any]:
228
- """ Compile a statement by precompiling the regex for the relevant clauses. This is done to boost
254
+ """Compile a statement by precompiling the regex for the relevant clauses. This is done to boost
229
255
  performance by not recompiling the regex over and over again.
230
256
 
231
257
  Arguments:
@@ -234,11 +260,13 @@ def compile_statement(statements: List[Any]) -> List[Any]:
234
260
  Returns:
235
261
  [dict] -- the compiled statement
236
262
  """
237
- properties = ['action', 'resource', 'notresource', 'notaction']
263
+ properties = ["action", "resource", "notresource", "notaction"]
238
264
  for statement in statements:
239
265
  for statement_property in properties:
240
266
  if statement_property in statement:
241
- statement[statement_property] = [compile_regex(item) for item in statement[statement_property]]
267
+ statement[statement_property] = [
268
+ compile_regex(item) for item in statement[statement_property]
269
+ ]
242
270
  return statements
243
271
 
244
272
 
@@ -263,16 +291,26 @@ def get_principals_for_account(neo4j_session: neo4j.Session, account_id: str) ->
263
291
  statements = r["statements"]
264
292
  if principal_arn not in principals:
265
293
  principals[principal_arn] = {}
266
- principals[principal_arn][policy_id] = compile_statement(parse_statement_node(statements))
294
+ principals[principal_arn][policy_id] = compile_statement(
295
+ parse_statement_node(statements),
296
+ )
267
297
  return principals
268
298
 
269
299
 
270
- def get_resource_arns(neo4j_session: neo4j.Session, account_id: str, node_label: str) -> List[Any]:
271
- get_resource_query = Template("""
300
+ def get_resource_arns(
301
+ neo4j_session: neo4j.Session,
302
+ account_id: str,
303
+ node_label: str,
304
+ ) -> List[Any]:
305
+ get_resource_query = Template(
306
+ """
272
307
  MATCH (acc:AWSAccount{id:$AccountId})-[:RESOURCE]->(resource:$node_label)
273
308
  return resource.arn as arn
274
- """)
275
- get_resource_query_template = get_resource_query.safe_substitute(node_label=node_label)
309
+ """,
310
+ )
311
+ get_resource_query_template = get_resource_query.safe_substitute(
312
+ node_label=node_label,
313
+ )
276
314
  results = neo4j_session.run(
277
315
  get_resource_query_template,
278
316
  AccountId=account_id,
@@ -282,16 +320,21 @@ def get_resource_arns(neo4j_session: neo4j.Session, account_id: str, node_label:
282
320
 
283
321
 
284
322
  def load_principal_mappings(
285
- neo4j_session: neo4j.Session, principal_mappings: List[Dict], node_label: str,
286
- relationship_name: str, update_tag: int,
323
+ neo4j_session: neo4j.Session,
324
+ principal_mappings: List[Dict],
325
+ node_label: str,
326
+ relationship_name: str,
327
+ update_tag: int,
287
328
  ) -> None:
288
- map_policy_query = Template("""
329
+ map_policy_query = Template(
330
+ """
289
331
  UNWIND $Mapping as mapping
290
332
  MATCH (principal:AWSPrincipal{arn:mapping.principal_arn})
291
333
  MATCH (resource:$node_label{arn:mapping.resource_arn})
292
334
  MERGE (principal)-[r:$relationship_name]->(resource)
293
335
  SET r.lastupdated = $aws_update_tag
294
- """)
336
+ """,
337
+ )
295
338
  if not principal_mappings:
296
339
  return
297
340
  map_policy_query_template = map_policy_query.safe_substitute(
@@ -306,16 +349,25 @@ def load_principal_mappings(
306
349
 
307
350
 
308
351
  def cleanup_rpr(
309
- neo4j_session: neo4j.Session, node_label: str, relationship_name: str, update_tag: int,
352
+ neo4j_session: neo4j.Session,
353
+ node_label: str,
354
+ relationship_name: str,
355
+ update_tag: int,
310
356
  current_aws_id: str,
311
357
  ) -> None:
312
- logger.info("Cleaning up relationship '%s' for node label '%s'", relationship_name, node_label)
313
- cleanup_rpr_query = Template("""
358
+ logger.info(
359
+ "Cleaning up relationship '%s' for node label '%s'",
360
+ relationship_name,
361
+ node_label,
362
+ )
363
+ cleanup_rpr_query = Template(
364
+ """
314
365
  MATCH (:AWSAccount{id: $AWS_ID})-[:RESOURCE]->(principal:AWSPrincipal)-[r:$relationship_name]->
315
366
  (resource:$node_label)
316
367
  WHERE r.lastupdated <> $UPDATE_TAG
317
368
  WITH r LIMIT $LIMIT_SIZE DELETE (r) return COUNT(*) as TotalCompleted
318
- """)
369
+ """,
370
+ )
319
371
  cleanup_rpr_query_template = cleanup_rpr_query.safe_substitute(
320
372
  node_label=node_label,
321
373
  relationship_name=relationship_name,
@@ -323,7 +375,7 @@ def cleanup_rpr(
323
375
 
324
376
  statement = GraphStatement(
325
377
  cleanup_rpr_query_template,
326
- {'UPDATE_TAG': update_tag, 'AWS_ID': current_aws_id},
378
+ {"UPDATE_TAG": update_tag, "AWS_ID": current_aws_id},
327
379
  True,
328
380
  1000,
329
381
  parent_job_name=f"{relationship_name}:{node_label}",
@@ -359,10 +411,17 @@ def is_valid_rpr(rpr: Dict) -> bool:
359
411
 
360
412
  @timeit
361
413
  def sync(
362
- neo4j_session: neo4j.Session, boto3_session: boto3.session.Session, regions: List[str], current_aws_account_id: str,
363
- update_tag: int, common_job_parameters: Dict,
414
+ neo4j_session: neo4j.Session,
415
+ boto3_session: boto3.session.Session,
416
+ regions: List[str],
417
+ current_aws_account_id: str,
418
+ update_tag: int,
419
+ common_job_parameters: Dict,
364
420
  ) -> None:
365
- logger.info("Syncing Permission Relationships for account '%s'.", current_aws_account_id)
421
+ logger.info(
422
+ "Syncing Permission Relationships for account '%s'.",
423
+ current_aws_account_id,
424
+ )
366
425
  principals = get_principals_for_account(neo4j_session, current_aws_account_id)
367
426
  pr_file = common_job_parameters["permission_relationships_file"]
368
427
  if not pr_file:
@@ -374,18 +433,41 @@ def sync(
374
433
  relationship_mapping = parse_permission_relationships_file(pr_file)
375
434
  for rpr in relationship_mapping:
376
435
  if not is_valid_rpr(rpr):
377
- raise ValueError("""
436
+ raise ValueError(
437
+ """
378
438
  Resource permission relationship is missing fields.
379
439
  Required fields: permissions, relationship_name, target_label"
380
- """)
440
+ """,
441
+ )
381
442
  permissions = rpr["permissions"]
382
443
  relationship_name = rpr["relationship_name"]
383
444
  target_label = rpr["target_label"]
384
- resource_arns = get_resource_arns(neo4j_session, current_aws_account_id, target_label)
385
- logger.info("Syncing relationship '%s' for node label '%s'", relationship_name, target_label)
386
- allowed_mappings = calculate_permission_relationships(principals, resource_arns, permissions)
445
+ resource_arns = get_resource_arns(
446
+ neo4j_session,
447
+ current_aws_account_id,
448
+ target_label,
449
+ )
450
+ logger.info(
451
+ "Syncing relationship '%s' for node label '%s'",
452
+ relationship_name,
453
+ target_label,
454
+ )
455
+ allowed_mappings = calculate_permission_relationships(
456
+ principals,
457
+ resource_arns,
458
+ permissions,
459
+ )
387
460
  load_principal_mappings(
388
- neo4j_session, allowed_mappings,
389
- target_label, relationship_name, update_tag,
461
+ neo4j_session,
462
+ allowed_mappings,
463
+ target_label,
464
+ relationship_name,
465
+ update_tag,
466
+ )
467
+ cleanup_rpr(
468
+ neo4j_session,
469
+ target_label,
470
+ relationship_name,
471
+ update_tag,
472
+ current_aws_account_id,
390
473
  )
391
- cleanup_rpr(neo4j_session, target_label, relationship_name, update_tag, current_aws_account_id)