cartography 0.102.0rc1__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 +327 -0
  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 -44
  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 +97 -0
  184. cartography/models/aws/ec2/route_tables.py +128 -0
  185. cartography/models/aws/ec2/routes.py +85 -0
  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.0rc1.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.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/WHEEL +1 -1
  248. cartography-0.102.0rc1.dist-info/RECORD +0 -377
  249. {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/entry_points.txt +0 -0
  250. {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/licenses/LICENSE +0 -0
  251. {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/top_level.txt +0 -0
@@ -13,11 +13,11 @@ from cartography.util import timeit
13
13
  @timeit
14
14
  @aws_handle_regions
15
15
  def get_iam_instance_profiles(boto3_session: boto3.Session) -> list[dict[str, Any]]:
16
- client = boto3_session.client('iam', config=get_botocore_config())
17
- paginator = client.get_paginator('list_instance_profiles')
16
+ client = boto3_session.client("iam", config=get_botocore_config())
17
+ paginator = client.get_paginator("list_instance_profiles")
18
18
  instance_profiles = []
19
19
  for page in paginator.paginate():
20
- instance_profiles.extend(page['InstanceProfiles'])
20
+ instance_profiles.extend(page["InstanceProfiles"])
21
21
  return instance_profiles
22
22
 
23
23
 
@@ -25,12 +25,12 @@ def transform_instance_profiles(data: list[dict[str, Any]]) -> list[dict[str, An
25
25
  transformed = []
26
26
  for profile in data:
27
27
  transformed_profile = {
28
- 'Arn': profile['Arn'],
29
- 'CreateDate': profile['CreateDate'],
30
- 'InstanceProfileId': profile['InstanceProfileId'],
31
- 'InstanceProfileName': profile['InstanceProfileName'],
32
- 'Path': profile['Path'],
33
- 'Roles': [role['Arn'] for role in profile.get('Roles', [])],
28
+ "Arn": profile["Arn"],
29
+ "CreateDate": profile["CreateDate"],
30
+ "InstanceProfileId": profile["InstanceProfileId"],
31
+ "InstanceProfileName": profile["InstanceProfileName"],
32
+ "Path": profile["Path"],
33
+ "Roles": [role["Arn"] for role in profile.get("Roles", [])],
34
34
  }
35
35
  transformed.append(transformed_profile)
36
36
  return transformed
@@ -38,11 +38,11 @@ def transform_instance_profiles(data: list[dict[str, Any]]) -> list[dict[str, An
38
38
 
39
39
  @timeit
40
40
  def load_iam_instance_profiles(
41
- neo4j_session: neo4j.Session,
42
- data: list[dict[str, Any]],
43
- current_aws_account_id: str,
44
- update_tag: int,
45
- common_job_parameters: dict[str, Any],
41
+ neo4j_session: neo4j.Session,
42
+ data: list[dict[str, Any]],
43
+ current_aws_account_id: str,
44
+ update_tag: int,
45
+ common_job_parameters: dict[str, Any],
46
46
  ) -> None:
47
47
  load(
48
48
  neo4j_session,
@@ -55,19 +55,19 @@ def load_iam_instance_profiles(
55
55
 
56
56
  @timeit
57
57
  def sync_iam_instance_profiles(
58
- boto3_session: boto3.Session,
59
- neo4j_session: neo4j.Session,
60
- current_aws_account_id: str,
61
- update_tag: int,
62
- regions: list[str],
63
- common_job_parameters: dict[str, Any],
58
+ boto3_session: boto3.Session,
59
+ neo4j_session: neo4j.Session,
60
+ current_aws_account_id: str,
61
+ update_tag: int,
62
+ regions: list[str],
63
+ common_job_parameters: dict[str, Any],
64
64
  ) -> None:
65
65
  profiles = get_iam_instance_profiles(boto3_session)
66
66
  profiles = transform_instance_profiles(profiles)
67
67
  load_iam_instance_profiles(
68
68
  neo4j_session,
69
69
  profiles,
70
- common_job_parameters['AWS_ID'],
71
- common_job_parameters['UPDATE_TAG'],
70
+ common_job_parameters["AWS_ID"],
71
+ common_job_parameters["UPDATE_TAG"],
72
72
  common_job_parameters,
73
73
  )
@@ -8,27 +8,35 @@ import neo4j
8
8
 
9
9
  from cartography.client.core.tx import load
10
10
  from cartography.graph.job import GraphJob
11
- from cartography.models.aws.identitycenter.awsidentitycenter import AWSIdentityCenterInstanceSchema
12
- from cartography.models.aws.identitycenter.awspermissionset import AWSPermissionSetSchema
11
+ from cartography.models.aws.identitycenter.awsidentitycenter import (
12
+ AWSIdentityCenterInstanceSchema,
13
+ )
14
+ from cartography.models.aws.identitycenter.awspermissionset import (
15
+ AWSPermissionSetSchema,
16
+ )
13
17
  from cartography.models.aws.identitycenter.awsssouser import AWSSSOUserSchema
14
18
  from cartography.util import aws_handle_regions
15
19
  from cartography.util import run_cleanup_job
16
20
  from cartography.util import timeit
21
+
17
22
  logger = logging.getLogger(__name__)
18
23
 
19
24
 
20
25
  @timeit
21
26
  @aws_handle_regions
22
- def get_identity_center_instances(boto3_session: boto3.session.Session, region: str) -> List[Dict]:
27
+ def get_identity_center_instances(
28
+ boto3_session: boto3.session.Session,
29
+ region: str,
30
+ ) -> List[Dict]:
23
31
  """
24
32
  Get all AWS IAM Identity Center instances in the current region
25
33
  """
26
- client = boto3_session.client('sso-admin', region_name=region)
34
+ client = boto3_session.client("sso-admin", region_name=region)
27
35
  instances = []
28
36
 
29
- paginator = client.get_paginator('list_instances')
37
+ paginator = client.get_paginator("list_instances")
30
38
  for page in paginator.paginate():
31
- instances.extend(page.get('Instances', []))
39
+ instances.extend(page.get("Instances", []))
32
40
 
33
41
  return instances
34
42
 
@@ -44,7 +52,9 @@ def load_identity_center_instances(
44
52
  """
45
53
  Load Identity Center instances into the graph
46
54
  """
47
- logger.info(f"Loading {len(instance_data)} Identity Center instances for region {region}")
55
+ logger.info(
56
+ f"Loading {len(instance_data)} Identity Center instances for region {region}",
57
+ )
48
58
  load(
49
59
  neo4j_session,
50
60
  AWSIdentityCenterInstanceSchema(),
@@ -57,24 +67,28 @@ def load_identity_center_instances(
57
67
 
58
68
  @timeit
59
69
  @aws_handle_regions
60
- def get_permission_sets(boto3_session: boto3.session.Session, instance_arn: str, region: str) -> List[Dict]:
70
+ def get_permission_sets(
71
+ boto3_session: boto3.session.Session,
72
+ instance_arn: str,
73
+ region: str,
74
+ ) -> List[Dict]:
61
75
  """
62
76
  Get all permission sets for a given Identity Center instance
63
77
  """
64
- client = boto3_session.client('sso-admin', region_name=region)
78
+ client = boto3_session.client("sso-admin", region_name=region)
65
79
  permission_sets = []
66
80
 
67
- paginator = client.get_paginator('list_permission_sets')
81
+ paginator = client.get_paginator("list_permission_sets")
68
82
  for page in paginator.paginate(InstanceArn=instance_arn):
69
83
  # Get detailed info for each permission set
70
- for arn in page.get('PermissionSets', []):
84
+ for arn in page.get("PermissionSets", []):
71
85
  details = client.describe_permission_set(
72
86
  InstanceArn=instance_arn,
73
87
  PermissionSetArn=arn,
74
88
  )
75
- permission_set = details.get('PermissionSet', {})
89
+ permission_set = details.get("PermissionSet", {})
76
90
  if permission_set:
77
- permission_set['RoleHint'] = (
91
+ permission_set["RoleHint"] = (
78
92
  f":role/aws-reserved/sso.amazonaws.com/AWSReservedSSO_{permission_set.get('Name')}"
79
93
  )
80
94
  permission_sets.append(permission_set)
@@ -94,7 +108,9 @@ def load_permission_sets(
94
108
  """
95
109
  Load Identity Center permission sets into the graph
96
110
  """
97
- logger.info(f"Loading {len(permission_sets)} permission sets for instance {instance_arn} in region {region}")
111
+ logger.info(
112
+ f"Loading {len(permission_sets)} permission sets for instance {instance_arn} in region {region}",
113
+ )
98
114
 
99
115
  load(
100
116
  neo4j_session,
@@ -117,15 +133,15 @@ def get_sso_users(
117
133
  """
118
134
  Get all SSO users for a given Identity Store
119
135
  """
120
- client = boto3_session.client('identitystore', region_name=region)
136
+ client = boto3_session.client("identitystore", region_name=region)
121
137
  users = []
122
138
 
123
- paginator = client.get_paginator('list_users')
139
+ paginator = client.get_paginator("list_users")
124
140
  for page in paginator.paginate(IdentityStoreId=identity_store_id):
125
- user_page = page.get('Users', [])
141
+ user_page = page.get("Users", [])
126
142
  for user in user_page:
127
- if user.get('ExternalIds', None):
128
- user['ExternalId'] = user.get('ExternalIds')[0].get('Id')
143
+ if user.get("ExternalIds", None):
144
+ user["ExternalId"] = user.get("ExternalIds")[0].get("Id")
129
145
  users.append(user)
130
146
 
131
147
  return users
@@ -143,7 +159,9 @@ def load_sso_users(
143
159
  """
144
160
  Load SSO users into the graph
145
161
  """
146
- logger.info(f"Loading {len(users)} SSO users for identity store {identity_store_id} in region {region}")
162
+ logger.info(
163
+ f"Loading {len(users)} SSO users for identity store {identity_store_id} in region {region}",
164
+ )
147
165
 
148
166
  load(
149
167
  neo4j_session,
@@ -169,19 +187,25 @@ def get_role_assignments(
169
187
  """
170
188
 
171
189
  logger.info(f"Getting role assignments for {len(users)} users")
172
- client = boto3_session.client('sso-admin', region_name=region)
190
+ client = boto3_session.client("sso-admin", region_name=region)
173
191
  role_assignments = []
174
192
 
175
193
  for user in users:
176
- user_id = user['UserId']
177
- paginator = client.get_paginator('list_account_assignments_for_principal')
178
- for page in paginator.paginate(InstanceArn=instance_arn, PrincipalId=user_id, PrincipalType='USER'):
179
- for assignment in page.get('AccountAssignments', []):
180
- role_assignments.append({
181
- 'UserId': user_id,
182
- 'PermissionSetArn': assignment.get('PermissionSetArn'),
183
- 'AccountId': assignment.get('AccountId'),
184
- })
194
+ user_id = user["UserId"]
195
+ paginator = client.get_paginator("list_account_assignments_for_principal")
196
+ for page in paginator.paginate(
197
+ InstanceArn=instance_arn,
198
+ PrincipalId=user_id,
199
+ PrincipalType="USER",
200
+ ):
201
+ for assignment in page.get("AccountAssignments", []):
202
+ role_assignments.append(
203
+ {
204
+ "UserId": user_id,
205
+ "PermissionSetArn": assignment.get("PermissionSetArn"),
206
+ "AccountId": assignment.get("AccountId"),
207
+ },
208
+ )
185
209
 
186
210
  return role_assignments
187
211
 
@@ -214,12 +238,22 @@ def load_role_assignments(
214
238
 
215
239
 
216
240
  @timeit
217
- def cleanup(neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]) -> None:
218
- GraphJob.from_node_schema(AWSIdentityCenterInstanceSchema(), common_job_parameters).run(neo4j_session)
219
- GraphJob.from_node_schema(AWSPermissionSetSchema(), common_job_parameters).run(neo4j_session)
220
- GraphJob.from_node_schema(AWSSSOUserSchema(), common_job_parameters).run(neo4j_session)
241
+ def cleanup(
242
+ neo4j_session: neo4j.Session,
243
+ common_job_parameters: Dict[str, Any],
244
+ ) -> None:
245
+ GraphJob.from_node_schema(
246
+ AWSIdentityCenterInstanceSchema(),
247
+ common_job_parameters,
248
+ ).run(neo4j_session)
249
+ GraphJob.from_node_schema(AWSPermissionSetSchema(), common_job_parameters).run(
250
+ neo4j_session,
251
+ )
252
+ GraphJob.from_node_schema(AWSSSOUserSchema(), common_job_parameters).run(
253
+ neo4j_session,
254
+ )
221
255
  run_cleanup_job(
222
- 'aws_import_identity_center_cleanup.json',
256
+ "aws_import_identity_center_cleanup.json",
223
257
  neo4j_session,
224
258
  common_job_parameters,
225
259
  )
@@ -251,8 +285,8 @@ def sync_identity_center_instances(
251
285
 
252
286
  # For each instance, get and load its permission sets and SSO users
253
287
  for instance in instances:
254
- instance_arn = instance['InstanceArn']
255
- identity_store_id = instance['IdentityStoreId']
288
+ instance_arn = instance["InstanceArn"]
289
+ identity_store_id = instance["IdentityStoreId"]
256
290
 
257
291
  permission_sets = get_permission_sets(boto3_session, instance_arn, region)
258
292
 
@@ -15,15 +15,33 @@ from cartography.util import aws_handle_regions
15
15
  from cartography.util import aws_paginate
16
16
  from cartography.util import timeit
17
17
 
18
-
19
18
  logger = logging.getLogger(__name__)
20
19
 
21
20
  # As of 7/22/24, Inspector is only available in the below regions. We will need to update this hardcoded list here over
22
21
  # time. :\ https://docs.aws.amazon.com/general/latest/gr/inspector2.html
23
22
  AWS_INSPECTOR_REGIONS = {
24
- "us-east-2", "us-east-1", "us-west-1", "us-west-2", "af-south-1", "ap-east-1", "ap-southeast-3", "ap-south-1",
25
- "ap-northeast-3", "ap-northeast-2", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1", "ca-central-1",
26
- "eu-central-1", "eu-west-1", "eu-west-2", "eu-south-1", "eu-west-3", "eu-north-1", "eu-central-2", "me-south-1",
23
+ "us-east-2",
24
+ "us-east-1",
25
+ "us-west-1",
26
+ "us-west-2",
27
+ "af-south-1",
28
+ "ap-east-1",
29
+ "ap-southeast-3",
30
+ "ap-south-1",
31
+ "ap-northeast-3",
32
+ "ap-northeast-2",
33
+ "ap-southeast-1",
34
+ "ap-southeast-2",
35
+ "ap-northeast-1",
36
+ "ca-central-1",
37
+ "eu-central-1",
38
+ "eu-west-1",
39
+ "eu-west-2",
40
+ "eu-south-1",
41
+ "eu-west-3",
42
+ "eu-north-1",
43
+ "eu-central-2",
44
+ "me-south-1",
27
45
  "sa-east-1",
28
46
  }
29
47
 
@@ -31,9 +49,9 @@ AWS_INSPECTOR_REGIONS = {
31
49
  @timeit
32
50
  @aws_handle_regions
33
51
  def get_inspector_findings(
34
- session: boto3.session.Session,
35
- region: str,
36
- current_aws_account_id: str,
52
+ session: boto3.session.Session,
53
+ region: str,
54
+ current_aws_account_id: str,
37
55
  ) -> List[Dict[str, Any]]:
38
56
  """
39
57
  We must list_findings by filtering the request, otherwise the request could tiemout.
@@ -42,28 +60,31 @@ def get_inspector_findings(
42
60
  list_members will get us all the accounts that
43
61
  have delegated access to the account specified by current_aws_account_id.
44
62
  """
45
- client = session.client('inspector2', region_name=region)
63
+ client = session.client("inspector2", region_name=region)
46
64
 
47
- members = aws_paginate(client, 'list_members', 'members')
65
+ members = aws_paginate(client, "list_members", "members")
48
66
  # the current host account may not be considered a "member", but we still fetch its findings
49
- accounts = [current_aws_account_id] + [m['accountId'] for m in members]
67
+ accounts = [current_aws_account_id] + [m["accountId"] for m in members]
50
68
 
51
69
  findings = []
52
70
  for account in accounts:
53
- logger.info(f'Getting findings for member account {account} in region {region}')
71
+ logger.info(f"Getting findings for member account {account} in region {region}")
54
72
  findings.extend(
55
73
  aws_paginate(
56
- client, 'list_findings', 'findings', filterCriteria={
57
- 'awsAccountId': [
74
+ client,
75
+ "list_findings",
76
+ "findings",
77
+ filterCriteria={
78
+ "awsAccountId": [
58
79
  {
59
- 'comparison': 'EQUALS',
60
- 'value': account,
80
+ "comparison": "EQUALS",
81
+ "value": account,
61
82
  },
62
83
  ],
63
- 'findingStatus': [
84
+ "findingStatus": [
64
85
  {
65
- 'comparison': 'NOT_EQUALS',
66
- 'value': 'CLOSED',
86
+ "comparison": "NOT_EQUALS",
87
+ "value": "CLOSED",
67
88
  },
68
89
  ],
69
90
  },
@@ -72,48 +93,70 @@ def get_inspector_findings(
72
93
  return findings
73
94
 
74
95
 
75
- def transform_inspector_findings(results: List[Dict[str, Any]]) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
96
+ def transform_inspector_findings(
97
+ results: List[Dict[str, Any]],
98
+ ) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
76
99
  findings_list: List[Dict] = []
77
100
  packages: Dict[str, Any] = {}
78
101
 
79
102
  for f in results:
80
103
  finding: Dict = {}
81
104
 
82
- finding['id'] = f['findingArn']
83
- finding['arn'] = f['findingArn']
84
- finding['severity'] = f['severity']
85
- finding['name'] = f['title']
86
- finding['firstobservedat'] = f['firstObservedAt']
87
- finding['updatedat'] = f['updatedAt']
88
- finding['awsaccount'] = f['awsAccountId']
89
- finding['description'] = f['description']
90
- finding['type'] = f['type']
91
- finding['status'] = f['status']
92
- if f.get('inspectorScoreDetails'):
93
- finding['cvssscore'] = f['inspectorScoreDetails']['adjustedCvss']['score']
94
- if f['resources'][0]['type'] == "AWS_EC2_INSTANCE":
95
- finding['instanceid'] = f['resources'][0]['id']
96
- if f['resources'][0]['type'] == "AWS_ECR_CONTAINER_IMAGE":
97
- finding['ecrimageid'] = f['resources'][0]['id'].split('/')[2]
98
- if f['resources'][0]['type'] == "AWS_ECR_REPOSITORY":
99
- finding['ecrrepositoryid'] = f['resources'][0]['id']
100
- if f.get('networkReachabilityDetails'):
101
- finding['protocol'] = f['networkReachabilityDetails']['protocol']
102
- finding['portrangebegin'] = f['networkReachabilityDetails']['openPortRange']['begin']
103
- finding['portrangeend'] = f['networkReachabilityDetails']['openPortRange']['end']
104
- finding['portrange'] = _port_range_string(f['networkReachabilityDetails'])
105
- if f.get('packageVulnerabilityDetails'):
106
- finding['vulnerabilityid'] = f['packageVulnerabilityDetails']['vulnerabilityId']
107
- finding['referenceurls'] = f['packageVulnerabilityDetails'].get('referenceUrls')
108
- finding['relatedvulnerabilities'] = f['packageVulnerabilityDetails'].get('relatedVulnerabilities')
109
- finding['source'] = f['packageVulnerabilityDetails'].get('source')
110
- finding['sourceurl'] = f['packageVulnerabilityDetails'].get('sourceUrl')
111
- finding['vendorcreatedat'] = f['packageVulnerabilityDetails'].get('vendorCreatedAt')
112
- finding['vendorseverity'] = f['packageVulnerabilityDetails'].get('vendorSeverity')
113
- finding['vendorupdatedat'] = f['packageVulnerabilityDetails'].get('vendorUpdatedAt')
114
-
115
- new_packages = _process_packages(f['packageVulnerabilityDetails'], f['awsAccountId'], f['findingArn'])
116
- finding['vulnerablepackageids'] = list(new_packages.keys())
105
+ finding["id"] = f["findingArn"]
106
+ finding["arn"] = f["findingArn"]
107
+ finding["severity"] = f["severity"]
108
+ finding["name"] = f["title"]
109
+ finding["firstobservedat"] = f["firstObservedAt"]
110
+ finding["updatedat"] = f["updatedAt"]
111
+ finding["awsaccount"] = f["awsAccountId"]
112
+ finding["description"] = f["description"]
113
+ finding["type"] = f["type"]
114
+ finding["status"] = f["status"]
115
+ if f.get("inspectorScoreDetails"):
116
+ finding["cvssscore"] = f["inspectorScoreDetails"]["adjustedCvss"]["score"]
117
+ if f["resources"][0]["type"] == "AWS_EC2_INSTANCE":
118
+ finding["instanceid"] = f["resources"][0]["id"]
119
+ if f["resources"][0]["type"] == "AWS_ECR_CONTAINER_IMAGE":
120
+ finding["ecrimageid"] = f["resources"][0]["id"].split("/")[2]
121
+ if f["resources"][0]["type"] == "AWS_ECR_REPOSITORY":
122
+ finding["ecrrepositoryid"] = f["resources"][0]["id"]
123
+ if f.get("networkReachabilityDetails"):
124
+ finding["protocol"] = f["networkReachabilityDetails"]["protocol"]
125
+ finding["portrangebegin"] = f["networkReachabilityDetails"][
126
+ "openPortRange"
127
+ ]["begin"]
128
+ finding["portrangeend"] = f["networkReachabilityDetails"]["openPortRange"][
129
+ "end"
130
+ ]
131
+ finding["portrange"] = _port_range_string(f["networkReachabilityDetails"])
132
+ if f.get("packageVulnerabilityDetails"):
133
+ finding["vulnerabilityid"] = f["packageVulnerabilityDetails"][
134
+ "vulnerabilityId"
135
+ ]
136
+ finding["referenceurls"] = f["packageVulnerabilityDetails"].get(
137
+ "referenceUrls",
138
+ )
139
+ finding["relatedvulnerabilities"] = f["packageVulnerabilityDetails"].get(
140
+ "relatedVulnerabilities",
141
+ )
142
+ finding["source"] = f["packageVulnerabilityDetails"].get("source")
143
+ finding["sourceurl"] = f["packageVulnerabilityDetails"].get("sourceUrl")
144
+ finding["vendorcreatedat"] = f["packageVulnerabilityDetails"].get(
145
+ "vendorCreatedAt",
146
+ )
147
+ finding["vendorseverity"] = f["packageVulnerabilityDetails"].get(
148
+ "vendorSeverity",
149
+ )
150
+ finding["vendorupdatedat"] = f["packageVulnerabilityDetails"].get(
151
+ "vendorUpdatedAt",
152
+ )
153
+
154
+ new_packages = _process_packages(
155
+ f["packageVulnerabilityDetails"],
156
+ f["awsAccountId"],
157
+ f["findingArn"],
158
+ )
159
+ finding["vulnerablepackageids"] = list(new_packages.keys())
117
160
  packages = {**packages, **new_packages}
118
161
 
119
162
  findings_list.append(finding)
@@ -129,47 +172,51 @@ def transform_inspector_packages(packages: Dict[str, Any]) -> List[Dict[str, Any
129
172
  return packages_list
130
173
 
131
174
 
132
- def _process_packages(package_details: Dict[str, Any], aws_account_id: str, finding_arn: str) -> Dict[str, Any]:
175
+ def _process_packages(
176
+ package_details: Dict[str, Any],
177
+ aws_account_id: str,
178
+ finding_arn: str,
179
+ ) -> Dict[str, Any]:
133
180
  packages: Dict[str, Any] = {}
134
- for package in package_details['vulnerablePackages']:
181
+ for package in package_details["vulnerablePackages"]:
135
182
  new_package = {}
136
- new_package['id'] = (
183
+ new_package["id"] = (
137
184
  f"{package.get('name', '')}|"
138
185
  f"{package.get('arch', '')}|"
139
186
  f"{package.get('version', '')}|"
140
187
  f"{package.get('release', '')}|"
141
188
  f"{package.get('epoch', '')}"
142
189
  )
143
- new_package['name'] = package.get('name')
144
- new_package['arch'] = package.get('arch')
145
- new_package['version'] = package.get('version')
146
- new_package['release'] = package.get('release')
147
- new_package['epoch'] = package.get('epoch')
148
- new_package['manager'] = package.get("packageManager")
149
- new_package['filepath'] = package.get('filePath')
150
- new_package['fixedinversion'] = package.get('fixedInVersion')
151
- new_package['sourcelayerhash'] = package.get('sourceLayerHash')
152
- new_package['awsaccount'] = aws_account_id
153
- new_package['findingarn'] = finding_arn
154
-
155
- packages[new_package['id']] = new_package
190
+ new_package["name"] = package.get("name")
191
+ new_package["arch"] = package.get("arch")
192
+ new_package["version"] = package.get("version")
193
+ new_package["release"] = package.get("release")
194
+ new_package["epoch"] = package.get("epoch")
195
+ new_package["manager"] = package.get("packageManager")
196
+ new_package["filepath"] = package.get("filePath")
197
+ new_package["fixedinversion"] = package.get("fixedInVersion")
198
+ new_package["sourcelayerhash"] = package.get("sourceLayerHash")
199
+ new_package["awsaccount"] = aws_account_id
200
+ new_package["findingarn"] = finding_arn
201
+
202
+ packages[new_package["id"]] = new_package
156
203
 
157
204
  return packages
158
205
 
159
206
 
160
207
  def _port_range_string(details: Dict[str, Any]) -> str:
161
- begin = details['openPortRange']['begin']
162
- end = details['openPortRange']['end']
208
+ begin = details["openPortRange"]["begin"]
209
+ end = details["openPortRange"]["end"]
163
210
  return f"{begin}-{end}"
164
211
 
165
212
 
166
213
  @timeit
167
214
  def load_inspector_findings(
168
- neo4j_session: neo4j.Session,
169
- findings: List[Dict[str, Any]],
170
- region: str,
171
- aws_update_tag: int,
172
- current_aws_account_id: str,
215
+ neo4j_session: neo4j.Session,
216
+ findings: List[Dict[str, Any]],
217
+ region: str,
218
+ aws_update_tag: int,
219
+ current_aws_account_id: str,
173
220
  ) -> None:
174
221
  load(
175
222
  neo4j_session,
@@ -183,11 +230,11 @@ def load_inspector_findings(
183
230
 
184
231
  @timeit
185
232
  def load_inspector_packages(
186
- neo4j_session: neo4j.Session,
187
- packages: List[Dict[str, Any]],
188
- region: str,
189
- aws_update_tag: int,
190
- current_aws_account_id: str,
233
+ neo4j_session: neo4j.Session,
234
+ packages: List[Dict[str, Any]],
235
+ region: str,
236
+ aws_update_tag: int,
237
+ current_aws_account_id: str,
191
238
  ) -> None:
192
239
  load(
193
240
  neo4j_session,
@@ -200,10 +247,17 @@ def load_inspector_packages(
200
247
 
201
248
 
202
249
  @timeit
203
- def cleanup(neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]) -> None:
250
+ def cleanup(
251
+ neo4j_session: neo4j.Session,
252
+ common_job_parameters: Dict[str, Any],
253
+ ) -> None:
204
254
  logger.info("Running AWS Inspector cleanup")
205
- GraphJob.from_node_schema(AWSInspectorFindingSchema(), common_job_parameters).run(neo4j_session)
206
- GraphJob.from_node_schema(AWSInspectorPackageSchema(), common_job_parameters).run(neo4j_session)
255
+ GraphJob.from_node_schema(AWSInspectorFindingSchema(), common_job_parameters).run(
256
+ neo4j_session,
257
+ )
258
+ GraphJob.from_node_schema(AWSInspectorPackageSchema(), common_job_parameters).run(
259
+ neo4j_session,
260
+ )
207
261
 
208
262
 
209
263
  @timeit
@@ -215,14 +269,30 @@ def sync(
215
269
  update_tag: int,
216
270
  common_job_parameters: Dict[str, Any],
217
271
  ) -> None:
218
- inspector_regions = [region for region in regions if region in AWS_INSPECTOR_REGIONS]
272
+ inspector_regions = [
273
+ region for region in regions if region in AWS_INSPECTOR_REGIONS
274
+ ]
219
275
 
220
276
  for region in inspector_regions:
221
- logger.info(f"Syncing AWS Inspector findings for account {current_aws_account_id} and region {region}")
277
+ logger.info(
278
+ f"Syncing AWS Inspector findings for account {current_aws_account_id} and region {region}",
279
+ )
222
280
  findings = get_inspector_findings(boto3_session, region, current_aws_account_id)
223
281
  finding_data, package_data = transform_inspector_findings(findings)
224
282
  logger.info(f"Loading {len(finding_data)} findings")
225
- load_inspector_findings(neo4j_session, finding_data, region, update_tag, current_aws_account_id)
283
+ load_inspector_findings(
284
+ neo4j_session,
285
+ finding_data,
286
+ region,
287
+ update_tag,
288
+ current_aws_account_id,
289
+ )
226
290
  logger.info(f"Loading {len(package_data)} packages")
227
- load_inspector_packages(neo4j_session, package_data, region, update_tag, current_aws_account_id)
291
+ load_inspector_packages(
292
+ neo4j_session,
293
+ package_data,
294
+ region,
295
+ update_tag,
296
+ current_aws_account_id,
297
+ )
228
298
  cleanup(neo4j_session, common_job_parameters)