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
cartography/cli.py CHANGED
@@ -8,10 +8,10 @@ from typing import Optional
8
8
  import cartography.config
9
9
  import cartography.sync
10
10
  import cartography.util
11
+ from cartography.intel.aws.util.common import parse_and_validate_aws_regions
11
12
  from cartography.intel.aws.util.common import parse_and_validate_aws_requested_syncs
12
13
  from cartography.intel.semgrep.dependencies import parse_and_validate_semgrep_ecosystems
13
14
 
14
-
15
15
  logger = logging.getLogger(__name__)
16
16
 
17
17
 
@@ -23,7 +23,11 @@ class CLI:
23
23
  :param prog: The name of the command line program. This will be displayed in usage and help output.
24
24
  """
25
25
 
26
- def __init__(self, sync: Optional[cartography.sync.Sync] = None, prog: Optional[str] = None):
26
+ def __init__(
27
+ self,
28
+ sync: Optional[cartography.sync.Sync] = None,
29
+ prog: Optional[str] = None,
30
+ ):
27
31
  self.sync = sync if sync else cartography.sync.build_default_sync()
28
32
  self.prog = prog
29
33
  self.parser = self._build_parser()
@@ -47,540 +51,531 @@ class CLI:
47
51
  "instances, use auth when communicating with Neo4j, sync data from multiple AWS accounts, and execute "
48
52
  "arbitrary analysis jobs after the conclusion of the sync."
49
53
  ),
50
- epilog='For more documentation please visit: https://github.com/lyft/cartography',
54
+ epilog="For more documentation please visit: https://github.com/cartography-cncf/cartography",
51
55
  )
52
56
  parser.add_argument(
53
- '-v',
54
- '--verbose',
55
- action='store_true',
56
- help='Enable verbose logging for cartography.',
57
+ "-v",
58
+ "--verbose",
59
+ action="store_true",
60
+ help="Enable verbose logging for cartography.",
57
61
  )
58
62
  parser.add_argument(
59
- '-q',
60
- '--quiet',
61
- action='store_true',
62
- help='Restrict cartography logging to warnings and errors only.',
63
+ "-q",
64
+ "--quiet",
65
+ action="store_true",
66
+ help="Restrict cartography logging to warnings and errors only.",
63
67
  )
64
68
  parser.add_argument(
65
- '--neo4j-uri',
69
+ "--neo4j-uri",
66
70
  type=str,
67
- default='bolt://localhost:7687',
71
+ default="bolt://localhost:7687",
68
72
  help=(
69
- 'A valid Neo4j URI to sync against. See '
70
- 'https://neo4j.com/docs/api/python-driver/current/driver.html#uri for complete documentation on the '
71
- 'structure of a Neo4j URI.'
73
+ "A valid Neo4j URI to sync against. See "
74
+ "https://neo4j.com/docs/api/python-driver/current/driver.html#uri for complete documentation on the "
75
+ "structure of a Neo4j URI."
72
76
  ),
73
77
  )
74
78
  parser.add_argument(
75
- '--neo4j-user',
79
+ "--neo4j-user",
76
80
  type=str,
77
81
  default=None,
78
- help='A username with which to authenticate to Neo4j.',
82
+ help="A username with which to authenticate to Neo4j.",
79
83
  )
80
84
  parser.add_argument(
81
- '--neo4j-password-env-var',
85
+ "--neo4j-password-env-var",
82
86
  type=str,
83
87
  default=None,
84
- help='The name of an environment variable containing a password with which to authenticate to Neo4j.',
88
+ help="The name of an environment variable containing a password with which to authenticate to Neo4j.",
85
89
  )
86
90
  parser.add_argument(
87
- '--neo4j-password-prompt',
88
- action='store_true',
91
+ "--neo4j-password-prompt",
92
+ action="store_true",
89
93
  help=(
90
- 'Present an interactive prompt for a password with which to authenticate to Neo4j. This parameter '
91
- 'supersedes other methods of supplying a Neo4j password.'
94
+ "Present an interactive prompt for a password with which to authenticate to Neo4j. This parameter "
95
+ "supersedes other methods of supplying a Neo4j password."
92
96
  ),
93
97
  )
94
98
  parser.add_argument(
95
- '--neo4j-max-connection-lifetime',
99
+ "--neo4j-max-connection-lifetime",
96
100
  type=int,
97
101
  default=3600,
98
102
  help=(
99
- 'Time in seconds for the Neo4j driver to consider a TCP connection alive. cartography default = 3600, '
100
- 'which is the same as the Neo4j driver default. See '
101
- 'https://neo4j.com/docs/driver-manual/1.7/client-applications/#driver-config-connection-pool-management'
102
- '.'
103
+ "Time in seconds for the Neo4j driver to consider a TCP connection alive. cartography default = 3600, "
104
+ "which is the same as the Neo4j driver default. See "
105
+ "https://neo4j.com/docs/driver-manual/1.7/client-applications/#driver-config-connection-pool-management"
106
+ "."
103
107
  ),
104
108
  )
105
109
  parser.add_argument(
106
- '--neo4j-database',
110
+ "--neo4j-database",
107
111
  type=str,
108
112
  default=None,
109
113
  help=(
110
- 'The name of the database in Neo4j to connect to. If not specified, uses the config settings of your '
111
- 'Neo4j database itself to infer which database is set to default. '
112
- 'See https://neo4j.com/docs/api/python-driver/4.4/api.html#database.'
114
+ "The name of the database in Neo4j to connect to. If not specified, uses the config settings of your "
115
+ "Neo4j database itself to infer which database is set to default. "
116
+ "See https://neo4j.com/docs/api/python-driver/4.4/api.html#database."
113
117
  ),
114
118
  )
115
119
  parser.add_argument(
116
- '--selected-modules',
120
+ "--selected-modules",
117
121
  type=str,
118
122
  default=None,
119
123
  help=(
120
124
  'Comma-separated list of cartography top-level modules to sync. Example 1: "aws,gcp" to run AWS and GCP'
121
- 'modules. See the full list available in source code at cartography.sync. '
122
- 'If not specified, cartography by default will run all modules available and log warnings when it '
123
- 'does not find credentials configured for them. '
125
+ "modules. See the full list available in source code at cartography.sync. "
126
+ "If not specified, cartography by default will run all modules available and log warnings when it "
127
+ "does not find credentials configured for them. "
124
128
  # TODO remove this mention about the create-indexes module when everything is using auto-indexes.
125
- 'We recommend that you always specify the `create-indexes` module first in this list. '
126
- 'If you specify the `analysis` module, we recommend that you include it as the LAST item of this list, '
127
- '(because it does not make sense to perform analysis on an empty/out-of-date graph).'
129
+ "We recommend that you always specify the `create-indexes` module first in this list. "
130
+ "If you specify the `analysis` module, we recommend that you include it as the LAST item of this list, "
131
+ "(because it does not make sense to perform analysis on an empty/out-of-date graph)."
128
132
  ),
129
133
  )
130
134
  # TODO add the below parameters to a 'sync' subparser
131
135
  parser.add_argument(
132
- '--update-tag',
136
+ "--update-tag",
133
137
  type=int,
134
138
  default=None,
135
139
  help=(
136
- 'A unique tag to apply to all Neo4j nodes and relationships created or updated during the sync run. '
137
- 'This tag is used by cleanup jobs to identify nodes and relationships that are stale and need to be '
138
- 'removed from the graph. By default, cartography will use a UNIX timestamp as the update tag.'
140
+ "A unique tag to apply to all Neo4j nodes and relationships created or updated during the sync run. "
141
+ "This tag is used by cleanup jobs to identify nodes and relationships that are stale and need to be "
142
+ "removed from the graph. By default, cartography will use a UNIX timestamp as the update tag."
139
143
  ),
140
144
  )
141
145
  parser.add_argument(
142
- '--aws-sync-all-profiles',
143
- action='store_true',
146
+ "--aws-sync-all-profiles",
147
+ action="store_true",
144
148
  help=(
145
- 'Enable AWS sync for all discovered named profiles. When this parameter is supplied cartography will '
146
- 'discover all configured AWS named profiles (see '
147
- 'https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) and run the AWS sync '
149
+ "Enable AWS sync for all discovered named profiles. When this parameter is supplied cartography will "
150
+ "discover all configured AWS named profiles (see "
151
+ "https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) and run the AWS sync "
148
152
  'job for each profile not named "default". If this parameter is not supplied, cartography will use the '
149
- 'default AWS credentials available in your environment to run the AWS sync once. When using this '
150
- 'parameter it is suggested that you create an AWS config file containing a named profile for each AWS '
151
- 'account you want to sync and use the AWS_CONFIG_FILE environment variable to point to that config '
152
- 'file (see https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html). cartography '
153
- 'respects the AWS CLI/SDK environment variables and does not override them.'
153
+ "default AWS credentials available in your environment to run the AWS sync once. When using this "
154
+ "parameter it is suggested that you create an AWS config file containing a named profile for each AWS "
155
+ "account you want to sync and use the AWS_CONFIG_FILE environment variable to point to that config "
156
+ "file (see https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html). cartography "
157
+ "respects the AWS CLI/SDK environment variables and does not override them."
154
158
  ),
155
159
  )
156
160
  parser.add_argument(
157
- '--aws-best-effort-mode',
158
- action='store_true',
161
+ "--aws-regions",
162
+ type=str,
163
+ default=None,
159
164
  help=(
160
- 'Enable AWS sync best effort mode when syncing AWS accounts. This will allow cartography to continue '
161
- 'syncing other accounts and delay raising an exception until the very end.'
165
+ '[EXPERIMENTAL!] Comma-separated list of AWS regions to sync. Example: specify "us-east-1,us-east-2" '
166
+ "to sync US East 1 and 2. Note that this syncs the same regions in ALL accounts and it is currently "
167
+ "not possible to specify different regions per account. "
168
+ "CAUTION: if you previously synced assets from regions that are _not_ included in your current list, "
169
+ "those assets will be _deleted_ during this sync. "
170
+ 'This is because cartography\'s cleanup process uses "lastupdated" and "account id" to determine data '
171
+ "freshness and not regions. So, if a previously synced region is missing in the current sync, "
172
+ "Cartography assumes the associated assets are stale and removes them. "
173
+ "Default behavior: If `--aws-regions` is not specified, cartography will _autodiscover_ the "
174
+ "regions supported by each account being synced."
162
175
  ),
163
176
  )
164
177
  parser.add_argument(
165
- '--oci-sync-all-profiles',
166
- action='store_true',
178
+ "--aws-best-effort-mode",
179
+ action="store_true",
167
180
  help=(
168
- 'Enable OCI sync for all discovered named profiles. When this parameter is supplied cartography will '
169
- 'discover all configured OCI named profiles (see '
170
- 'https://docs.oracle.com/en-us/iaas/Content/API/Concepts/sdkconfig.htm) and run the OCI sync '
171
- 'job for each profile not named "DEFAULT". If this parameter is not supplied, cartography will use the '
172
- 'default OCI credentials available in your environment to run the OCI sync once.'
181
+ "Enable AWS sync best effort mode when syncing AWS accounts. This will allow cartography to continue "
182
+ "syncing other accounts and delay raising an exception until the very end."
173
183
  ),
174
184
  )
175
185
  parser.add_argument(
176
- '--azure-sync-all-subscriptions',
177
- action='store_true',
186
+ "--oci-sync-all-profiles",
187
+ action="store_true",
178
188
  help=(
179
- 'Enable Azure sync for all discovered subscriptions. When this parameter is supplied cartography will '
180
- 'discover all configured Azure subscriptions.'
189
+ "Enable OCI sync for all discovered named profiles. When this parameter is supplied cartography will "
190
+ "discover all configured OCI named profiles (see "
191
+ "https://docs.oracle.com/en-us/iaas/Content/API/Concepts/sdkconfig.htm) and run the OCI sync "
192
+ 'job for each profile not named "DEFAULT". If this parameter is not supplied, cartography will use the '
193
+ "default OCI credentials available in your environment to run the OCI sync once."
181
194
  ),
182
195
  )
183
196
  parser.add_argument(
184
- '--azure-sp-auth',
185
- action='store_true',
197
+ "--azure-sync-all-subscriptions",
198
+ action="store_true",
186
199
  help=(
187
- 'Use Service Principal authentication for Azure sync.'
200
+ "Enable Azure sync for all discovered subscriptions. When this parameter is supplied cartography will "
201
+ "discover all configured Azure subscriptions."
188
202
  ),
189
203
  )
190
204
  parser.add_argument(
191
- '--azure-tenant-id',
205
+ "--azure-sp-auth",
206
+ action="store_true",
207
+ help=("Use Service Principal authentication for Azure sync."),
208
+ )
209
+ parser.add_argument(
210
+ "--azure-tenant-id",
192
211
  type=str,
193
212
  default=None,
194
- help=(
195
- 'Azure Tenant Id for Service Principal Authentication.'
196
- ),
213
+ help=("Azure Tenant Id for Service Principal Authentication."),
197
214
  )
198
215
  parser.add_argument(
199
- '--azure-client-id',
216
+ "--azure-client-id",
200
217
  type=str,
201
218
  default=None,
202
- help=(
203
- 'Azure Client Id for Service Principal Authentication.'
204
- ),
219
+ help=("Azure Client Id for Service Principal Authentication."),
205
220
  )
206
221
  parser.add_argument(
207
- '--azure-client-secret-env-var',
222
+ "--azure-client-secret-env-var",
208
223
  type=str,
209
224
  default=None,
210
225
  help=(
211
- 'The name of environment variable containing Azure Client Secret for Service Principal Authentication.'
226
+ "The name of environment variable containing Azure Client Secret for Service Principal Authentication."
212
227
  ),
213
228
  )
214
229
  parser.add_argument(
215
- '--entra-tenant-id',
230
+ "--entra-tenant-id",
216
231
  type=str,
217
232
  default=None,
218
- help=(
219
- 'Entra Tenant Id for Service Principal Authentication.'
220
- ),
233
+ help=("Entra Tenant Id for Service Principal Authentication."),
221
234
  )
222
235
  parser.add_argument(
223
- '--entra-client-id',
236
+ "--entra-client-id",
224
237
  type=str,
225
238
  default=None,
226
- help=(
227
- 'Entra Client Id for Service Principal Authentication.'
228
- ),
239
+ help=("Entra Client Id for Service Principal Authentication."),
229
240
  )
230
241
  parser.add_argument(
231
- '--entra-client-secret-env-var',
242
+ "--entra-client-secret-env-var",
232
243
  type=str,
233
244
  default=None,
234
245
  help=(
235
- 'The name of environment variable containing Entra Client Secret for Service Principal Authentication.'
246
+ "The name of environment variable containing Entra Client Secret for Service Principal Authentication."
236
247
  ),
237
248
  )
238
249
  parser.add_argument(
239
- '--aws-requested-syncs',
250
+ "--aws-requested-syncs",
240
251
  type=str,
241
252
  default=None,
242
253
  help=(
243
254
  'Comma-separated list of AWS resources to sync. Example 1: "ecr,s3,ec2:instance" for ECR, S3, and all '
244
- 'EC2 instance resources. See the full list available in source code at cartography.intel.aws.resources.'
245
- ' If not specified, cartography by default will run all AWS sync modules available.'
255
+ "EC2 instance resources. See the full list available in source code at cartography.intel.aws.resources."
256
+ " If not specified, cartography by default will run all AWS sync modules available."
246
257
  ),
247
258
  )
248
259
  parser.add_argument(
249
- '--analysis-job-directory',
260
+ "--analysis-job-directory",
250
261
  type=str,
251
262
  default=None,
252
263
  help=(
253
- 'A path to a directory containing analysis jobs to run at the conclusion of the sync. cartography will '
254
- 'discover all JSON files in the given directory (and its subdirectories) and pass them to the GraphJob '
255
- 'API to execute against the graph. This allows you to apply data transformation and augmentation at '
256
- 'the end of a sync run without writing code. cartography does not guarantee the order in which the '
257
- 'jobs are executed.'
264
+ "A path to a directory containing analysis jobs to run at the conclusion of the sync. cartography will "
265
+ "discover all JSON files in the given directory (and its subdirectories) and pass them to the GraphJob "
266
+ "API to execute against the graph. This allows you to apply data transformation and augmentation at "
267
+ "the end of a sync run without writing code. cartography does not guarantee the order in which the "
268
+ "jobs are executed."
258
269
  ),
259
270
  )
260
271
  parser.add_argument(
261
- '--okta-org-id',
272
+ "--okta-org-id",
262
273
  type=str,
263
274
  default=None,
264
275
  help=(
265
- 'Okta organizational id to sync. Required if you are using the Okta intel module. Ignored otherwise.'
276
+ "Okta organizational id to sync. Required if you are using the Okta intel module. Ignored otherwise."
266
277
  ),
267
278
  )
268
279
  parser.add_argument(
269
- '--okta-api-key-env-var',
280
+ "--okta-api-key-env-var",
270
281
  type=str,
271
282
  default=None,
272
283
  help=(
273
- 'The name of an environment variable containing a key with which to auth to the Okta API.'
274
- 'Required if you are using the Okta intel module. Ignored otherwise.'
284
+ "The name of an environment variable containing a key with which to auth to the Okta API."
285
+ "Required if you are using the Okta intel module. Ignored otherwise."
275
286
  ),
276
287
  )
277
288
  parser.add_argument(
278
- '--okta-saml-role-regex',
289
+ "--okta-saml-role-regex",
279
290
  type=str,
280
291
  default=r"^aws\#\S+\#(?{{role}}[\w\-]+)\#(?{{accountid}}\d+)$",
281
292
  help=(
282
- 'The regex used to map Okta groups to AWS roles when using okta as a SAML provider.'
283
- 'The regex is the one entered in Step 5: Enabling Group Based Role Mapping in Okta'
284
- 'https://saml-doc.okta.com/SAML_Docs/How-to-Configure-SAML-2.0-for-Amazon-Web-Service#c-step5'
285
- 'The regex must contain the {{role}} and {{accountid}} tags'
293
+ "The regex used to map Okta groups to AWS roles when using okta as a SAML provider."
294
+ "The regex is the one entered in Step 5: Enabling Group Based Role Mapping in Okta"
295
+ "https://saml-doc.okta.com/SAML_Docs/How-to-Configure-SAML-2.0-for-Amazon-Web-Service#c-step5"
296
+ "The regex must contain the {{role}} and {{accountid}} tags"
286
297
  ),
287
298
  )
288
299
  parser.add_argument(
289
- '--github-config-env-var',
300
+ "--github-config-env-var",
290
301
  type=str,
291
302
  default=None,
292
303
  help=(
293
- 'The name of an environment variable containing a Base64 encoded GitHub config object.'
294
- 'Required if you are using the GitHub intel module. Ignored otherwise.'
304
+ "The name of an environment variable containing a Base64 encoded GitHub config object."
305
+ "Required if you are using the GitHub intel module. Ignored otherwise."
295
306
  ),
296
307
  )
297
308
  parser.add_argument(
298
- '--digitalocean-token-env-var',
309
+ "--digitalocean-token-env-var",
299
310
  type=str,
300
311
  default=None,
301
312
  help=(
302
- 'The name of an environment variable containing a DigitalOcean access token.'
303
- 'Required if you are using the DigitalOcean intel module. Ignored otherwise.'
313
+ "The name of an environment variable containing a DigitalOcean access token."
314
+ "Required if you are using the DigitalOcean intel module. Ignored otherwise."
304
315
  ),
305
316
  )
306
317
  parser.add_argument(
307
- '--permission-relationships-file',
318
+ "--permission-relationships-file",
308
319
  type=str,
309
320
  default="cartography/data/permission_relationships.yaml",
310
321
  help=(
311
- 'The path to the permission relationships mapping file.'
312
- 'If omitted the default permission relationships will be created'
322
+ "The path to the permission relationships mapping file."
323
+ "If omitted the default permission relationships will be created"
313
324
  ),
314
325
  )
315
326
  parser.add_argument(
316
- '--jamf-base-uri',
327
+ "--jamf-base-uri",
317
328
  type=str,
318
329
  default=None,
319
330
  help=(
320
- 'Your Jamf base URI, e.g. https://hostname.com/JSSResource.'
321
- 'Required if you are using the Jamf intel module. Ignored otherwise.'
331
+ "Your Jamf base URI, e.g. https://hostname.com/JSSResource."
332
+ "Required if you are using the Jamf intel module. Ignored otherwise."
322
333
  ),
323
334
  )
324
335
  parser.add_argument(
325
- '--jamf-user',
336
+ "--jamf-user",
326
337
  type=str,
327
338
  default=None,
328
- help='A username with which to authenticate to Jamf.',
339
+ help="A username with which to authenticate to Jamf.",
329
340
  )
330
341
  parser.add_argument(
331
- '--jamf-password-env-var',
342
+ "--jamf-password-env-var",
332
343
  type=str,
333
344
  default=None,
334
- help='The name of an environment variable containing a password with which to authenticate to Jamf.',
345
+ help="The name of an environment variable containing a password with which to authenticate to Jamf.",
335
346
  )
336
347
  parser.add_argument(
337
- '--kandji-base-uri',
348
+ "--kandji-base-uri",
338
349
  type=str,
339
350
  default=None,
340
351
  help=(
341
- 'Your Kandji base URI, e.g. https://company.api.kandji.io.'
342
- 'Required if you are using the Kandji intel module. Ignored otherwise.'
352
+ "Your Kandji base URI, e.g. https://company.api.kandji.io."
353
+ "Required if you are using the Kandji intel module. Ignored otherwise."
343
354
  ),
344
355
  )
345
356
  parser.add_argument(
346
- '--kandji-tenant-id',
357
+ "--kandji-tenant-id",
347
358
  type=str,
348
359
  default=None,
349
360
  help=(
350
- 'Your Kandji tenant id e.g. company.'
351
- 'Required using the Kandji intel module. Ignored otherwise.'
361
+ "Your Kandji tenant id e.g. company."
362
+ "Required using the Kandji intel module. Ignored otherwise."
352
363
  ),
353
364
  )
354
365
  parser.add_argument(
355
- '--kandji-token-env-var',
366
+ "--kandji-token-env-var",
356
367
  type=str,
357
368
  default=None,
358
- help='The name of an environment variable containing token with which to authenticate to Kandji.',
369
+ help="The name of an environment variable containing token with which to authenticate to Kandji.",
359
370
  )
360
371
  parser.add_argument(
361
- '--k8s-kubeconfig',
372
+ "--k8s-kubeconfig",
362
373
  default=None,
363
374
  type=str,
364
375
  help=(
365
- 'The path to kubeconfig file specifying context to access K8s cluster(s).'
376
+ "The path to kubeconfig file specifying context to access K8s cluster(s)."
366
377
  ),
367
378
  )
368
379
  parser.add_argument(
369
- '--nist-cve-url',
380
+ "--nist-cve-url",
370
381
  type=str,
371
- default='https://services.nvd.nist.gov/rest/json/cves/2.0/',
382
+ default="https://services.nvd.nist.gov/rest/json/cves/2.0/",
372
383
  help=(
373
- 'The base url for the NIST CVE data. Default = https://services.nvd.nist.gov/rest/json/cves/2.0/'
384
+ "The base url for the NIST CVE data. Default = https://services.nvd.nist.gov/rest/json/cves/2.0/"
374
385
  ),
375
386
  )
376
387
  parser.add_argument(
377
- '--cve-enabled',
378
- action='store_true',
379
- help=(
380
- 'If set, CVE data will be synced from NIST.'
381
- ),
388
+ "--cve-enabled",
389
+ action="store_true",
390
+ help=("If set, CVE data will be synced from NIST."),
382
391
  )
383
392
  parser.add_argument(
384
- '--cve-api-key-env-var',
393
+ "--cve-api-key-env-var",
385
394
  type=str,
386
395
  default=None,
387
- help=(
388
- 'If set, uses the provided NIST NVD API v2.0 key.'
389
- ),
396
+ help=("If set, uses the provided NIST NVD API v2.0 key."),
390
397
  )
391
398
  parser.add_argument(
392
- '--statsd-enabled',
393
- action='store_true',
399
+ "--statsd-enabled",
400
+ action="store_true",
394
401
  help=(
395
- 'If set, enables sending metrics using statsd to a server of your choice.'
402
+ "If set, enables sending metrics using statsd to a server of your choice."
396
403
  ),
397
404
  )
398
405
  parser.add_argument(
399
- '--statsd-prefix',
406
+ "--statsd-prefix",
400
407
  type=str,
401
- default='',
408
+ default="",
402
409
  help=(
403
- 'The string to prefix statsd metrics with. Only used if --statsd-enabled is on. Default = empty string.'
410
+ "The string to prefix statsd metrics with. Only used if --statsd-enabled is on. Default = empty string."
404
411
  ),
405
412
  )
406
413
  parser.add_argument(
407
- '--statsd-host',
414
+ "--statsd-host",
408
415
  type=str,
409
- default='127.0.0.1',
416
+ default="127.0.0.1",
410
417
  help=(
411
- 'The IP address of your statsd server. Only used if --statsd-enabled is on. Default = 127.0.0.1.'
418
+ "The IP address of your statsd server. Only used if --statsd-enabled is on. Default = 127.0.0.1."
412
419
  ),
413
420
  )
414
421
  parser.add_argument(
415
- '--statsd-port',
422
+ "--statsd-port",
416
423
  type=int,
417
424
  default=8125,
418
425
  help=(
419
- 'The port of your statsd server. Only used if --statsd-enabled is on. Default = UDP 8125.'
426
+ "The port of your statsd server. Only used if --statsd-enabled is on. Default = UDP 8125."
420
427
  ),
421
428
  )
422
429
  parser.add_argument(
423
- '--pagerduty-api-key-env-var',
430
+ "--pagerduty-api-key-env-var",
424
431
  type=str,
425
432
  default=None,
426
433
  help=(
427
- 'The name of environment variable containing the pagerduty API key for authentication.'
434
+ "The name of environment variable containing the pagerduty API key for authentication."
428
435
  ),
429
436
  )
430
437
  parser.add_argument(
431
- '--pagerduty-request-timeout',
438
+ "--pagerduty-request-timeout",
432
439
  type=int,
433
440
  default=None,
434
- help=(
435
- 'Seconds to timeout for pagerduty API sessions.'
436
- ),
441
+ help=("Seconds to timeout for pagerduty API sessions."),
437
442
  )
438
443
  parser.add_argument(
439
- '--crowdstrike-client-id-env-var',
444
+ "--crowdstrike-client-id-env-var",
440
445
  type=str,
441
446
  default=None,
442
447
  help=(
443
- 'The name of environment variable containing the crowdstrike client id for authentication.'
448
+ "The name of environment variable containing the crowdstrike client id for authentication."
444
449
  ),
445
450
  )
446
451
  parser.add_argument(
447
- '--crowdstrike-client-secret-env-var',
452
+ "--crowdstrike-client-secret-env-var",
448
453
  type=str,
449
454
  default=None,
450
455
  help=(
451
- 'The name of environment variable containing the crowdstrike secret key for authentication.'
456
+ "The name of environment variable containing the crowdstrike secret key for authentication."
452
457
  ),
453
458
  )
454
459
  parser.add_argument(
455
- '--crowdstrike-api-url',
460
+ "--crowdstrike-api-url",
456
461
  type=str,
457
462
  default=None,
458
463
  help=(
459
- 'The crowdstrike URL, if using self-hosted. Defaults to the public crowdstrike API URL otherwise.'
464
+ "The crowdstrike URL, if using self-hosted. Defaults to the public crowdstrike API URL otherwise."
460
465
  ),
461
466
  )
462
467
  parser.add_argument(
463
- '--gsuite-auth-method',
468
+ "--gsuite-auth-method",
464
469
  type=str,
465
- default='delegated',
466
- choices=['delegated', 'oauth', 'default'],
470
+ default="delegated",
471
+ choices=["delegated", "oauth", "default"],
467
472
  help=(
468
473
  'GSuite authentication method. Can be "delegated" for service account or "oauth" for OAuth. '
469
474
  '"Default" best if using gcloud CLI.'
470
475
  ),
471
476
  )
472
477
  parser.add_argument(
473
- '--gsuite-tokens-env-var',
478
+ "--gsuite-tokens-env-var",
474
479
  type=str,
475
- default='GSUITE_GOOGLE_APPLICATION_CREDENTIALS',
480
+ default="GSUITE_GOOGLE_APPLICATION_CREDENTIALS",
476
481
  help=(
477
- 'The name of environment variable containing secrets for GSuite authentication.'
482
+ "The name of environment variable containing secrets for GSuite authentication."
478
483
  ),
479
484
  )
480
485
  parser.add_argument(
481
- '--lastpass-cid-env-var',
486
+ "--lastpass-cid-env-var",
482
487
  type=str,
483
488
  default=None,
484
489
  help=(
485
- 'The name of environment variable containing the Lastpass CID for authentication.'
490
+ "The name of environment variable containing the Lastpass CID for authentication."
486
491
  ),
487
492
  )
488
493
  parser.add_argument(
489
- '--lastpass-provhash-env-var',
494
+ "--lastpass-provhash-env-var",
490
495
  type=str,
491
496
  default=None,
492
497
  help=(
493
- 'The name of environment variable containing the Lastpass provhash for authentication.'
498
+ "The name of environment variable containing the Lastpass provhash for authentication."
494
499
  ),
495
500
  )
496
501
  parser.add_argument(
497
- '--bigfix-username',
502
+ "--bigfix-username",
498
503
  type=str,
499
504
  default=None,
500
- help=(
501
- 'The BigFix username for authentication.'
502
- ),
505
+ help=("The BigFix username for authentication."),
503
506
  )
504
507
  parser.add_argument(
505
- '--bigfix-password-env-var',
508
+ "--bigfix-password-env-var",
506
509
  type=str,
507
510
  default=None,
508
511
  help=(
509
- 'The name of environment variable containing the BigFix password for authentication.'
512
+ "The name of environment variable containing the BigFix password for authentication."
510
513
  ),
511
514
  )
512
515
  parser.add_argument(
513
- '--bigfix-root-url',
516
+ "--bigfix-root-url",
514
517
  type=str,
515
518
  default=None,
516
- help=(
517
- 'The BigFix Root URL, a.k.a the BigFix API URL'
518
- ),
519
+ help=("The BigFix Root URL, a.k.a the BigFix API URL"),
519
520
  )
520
521
  parser.add_argument(
521
- '--duo-api-key-env-var',
522
+ "--duo-api-key-env-var",
522
523
  type=str,
523
524
  default=None,
524
- help=(
525
- 'The name of environment variable containing the Duo api key'
526
- ),
525
+ help=("The name of environment variable containing the Duo api key"),
527
526
  )
528
527
  parser.add_argument(
529
- '--duo-api-secret-env-var',
528
+ "--duo-api-secret-env-var",
530
529
  type=str,
531
530
  default=None,
532
- help=(
533
- 'The name of environment variable containing the Duo api secret'
534
- ),
531
+ help=("The name of environment variable containing the Duo api secret"),
535
532
  )
536
533
  parser.add_argument(
537
- '--duo-api-hostname',
534
+ "--duo-api-hostname",
538
535
  type=str,
539
536
  default=None,
540
- help=(
541
- 'The Duo api hostname'
542
- ),
537
+ help=("The Duo api hostname"),
543
538
  )
544
539
  parser.add_argument(
545
- '--semgrep-app-token-env-var',
540
+ "--semgrep-app-token-env-var",
546
541
  type=str,
547
542
  default=None,
548
543
  help=(
549
- 'The name of environment variable containing the Semgrep app token key. '
550
- 'Required if you are using the Semgrep intel module. Ignored otherwise.'
544
+ "The name of environment variable containing the Semgrep app token key. "
545
+ "Required if you are using the Semgrep intel module. Ignored otherwise."
551
546
  ),
552
547
  )
553
548
  parser.add_argument(
554
- '--semgrep-dependency-ecosystems',
549
+ "--semgrep-dependency-ecosystems",
555
550
  type=str,
556
551
  default=None,
557
552
  help=(
558
- 'Comma-separated list of language ecosystems for which dependencies will be retrieved from Semgrep. '
553
+ "Comma-separated list of language ecosystems for which dependencies will be retrieved from Semgrep. "
559
554
  'For example, a value of "gomod,npm" will retrieve Go and NPM dependencies. '
560
- 'See the full list of supported ecosystems in source code at cartography.intel.semgrep.dependencies. '
561
- 'Required if you are using the Semgrep dependencies intel module. Ignored otherwise.'
555
+ "See the full list of supported ecosystems in source code at cartography.intel.semgrep.dependencies. "
556
+ "Required if you are using the Semgrep dependencies intel module. Ignored otherwise."
562
557
  ),
563
558
  )
564
559
  parser.add_argument(
565
- '--snipeit-base-uri',
560
+ "--snipeit-base-uri",
566
561
  type=str,
567
562
  default=None,
568
563
  help=(
569
- 'Your SnipeIT base URI'
570
- 'Required if you are using the SnipeIT intel module. Ignored otherwise.'
564
+ "Your SnipeIT base URI"
565
+ "Required if you are using the SnipeIT intel module. Ignored otherwise."
571
566
  ),
572
567
  )
573
568
  parser.add_argument(
574
- '--snipeit-token-env-var',
569
+ "--snipeit-token-env-var",
575
570
  type=str,
576
571
  default=None,
577
- help='The name of an environment variable containing token with which to authenticate to SnipeIT.',
572
+ help="The name of an environment variable containing token with which to authenticate to SnipeIT.",
578
573
  )
579
574
  parser.add_argument(
580
- '--snipeit-tenant-id',
575
+ "--snipeit-tenant-id",
581
576
  type=str,
582
577
  default=None,
583
- help='An ID for the SnipeIT tenant.',
578
+ help="An ID for the SnipeIT tenant.",
584
579
  )
585
580
 
586
581
  return parser
@@ -596,17 +591,20 @@ class CLI:
596
591
  config: argparse.Namespace = self.parser.parse_args(argv)
597
592
  # Logging config
598
593
  if config.verbose:
599
- logging.getLogger('cartography').setLevel(logging.DEBUG)
594
+ logging.getLogger("cartography").setLevel(logging.DEBUG)
600
595
  elif config.quiet:
601
- logging.getLogger('cartography').setLevel(logging.WARNING)
596
+ logging.getLogger("cartography").setLevel(logging.WARNING)
602
597
  else:
603
- logging.getLogger('cartography').setLevel(logging.INFO)
598
+ logging.getLogger("cartography").setLevel(logging.INFO)
604
599
  logger.debug("Launching cartography with CLI configuration: %r", vars(config))
605
600
  # Neo4j config
606
601
  if config.neo4j_user:
607
602
  config.neo4j_password = None
608
603
  if config.neo4j_password_prompt:
609
- logger.info("Reading password for Neo4j user '%s' interactively.", config.neo4j_user)
604
+ logger.info(
605
+ "Reading password for Neo4j user '%s' interactively.",
606
+ config.neo4j_user,
607
+ )
610
608
  config.neo4j_password = getpass.getpass()
611
609
  elif config.neo4j_password_env_var:
612
610
  logger.debug(
@@ -616,7 +614,9 @@ class CLI:
616
614
  )
617
615
  config.neo4j_password = os.environ.get(config.neo4j_password_env_var)
618
616
  if not config.neo4j_password:
619
- logger.warning("Neo4j username was provided but a password could not be found.")
617
+ logger.warning(
618
+ "Neo4j username was provided but a password could not be found.",
619
+ )
620
620
  else:
621
621
  config.neo4j_password = None
622
622
 
@@ -629,44 +629,65 @@ class CLI:
629
629
  # No need to store the returned value; we're using this for input validation.
630
630
  parse_and_validate_aws_requested_syncs(config.aws_requested_syncs)
631
631
 
632
+ # AWS regions
633
+ if config.aws_regions:
634
+ # No need to store the returned value; we're using this for input validation.
635
+ parse_and_validate_aws_regions(config.aws_regions)
636
+
632
637
  # Azure config
633
638
  if config.azure_sp_auth and config.azure_client_secret_env_var:
634
639
  logger.debug(
635
640
  "Reading Client Secret for Azure Service Principal Authentication from environment variable %s",
636
641
  config.azure_client_secret_env_var,
637
642
  )
638
- config.azure_client_secret = os.environ.get(config.azure_client_secret_env_var)
643
+ config.azure_client_secret = os.environ.get(
644
+ config.azure_client_secret_env_var,
645
+ )
639
646
  else:
640
647
  config.azure_client_secret = None
641
648
 
642
649
  # Entra config
643
- if config.entra_tenant_id and config.entra_client_id and config.entra_client_secret_env_var:
650
+ if (
651
+ config.entra_tenant_id
652
+ and config.entra_client_id
653
+ and config.entra_client_secret_env_var
654
+ ):
644
655
  logger.debug(
645
656
  "Reading Client Secret for Entra Authentication from environment variable %s",
646
657
  config.entra_client_secret_env_var,
647
658
  )
648
- config.entra_client_secret = os.environ.get(config.entra_client_secret_env_var)
659
+ config.entra_client_secret = os.environ.get(
660
+ config.entra_client_secret_env_var
661
+ )
649
662
  else:
650
663
  config.entra_client_secret = None
651
664
 
652
665
  # Okta config
653
666
  if config.okta_org_id and config.okta_api_key_env_var:
654
- logger.debug(f"Reading API key for Okta from environment variable {config.okta_api_key_env_var}")
667
+ logger.debug(
668
+ f"Reading API key for Okta from environment variable {config.okta_api_key_env_var}",
669
+ )
655
670
  config.okta_api_key = os.environ.get(config.okta_api_key_env_var)
656
671
  else:
657
672
  config.okta_api_key = None
658
673
 
659
674
  # GitHub config
660
675
  if config.github_config_env_var:
661
- logger.debug(f"Reading config string for GitHub from environment variable {config.github_config_env_var}")
676
+ logger.debug(
677
+ f"Reading config string for GitHub from environment variable {config.github_config_env_var}",
678
+ )
662
679
  config.github_config = os.environ.get(config.github_config_env_var)
663
680
  else:
664
681
  config.github_config = None
665
682
 
666
683
  # DigitalOcean config
667
684
  if config.digitalocean_token_env_var:
668
- logger.debug(f"Reading token for DigitalOcean from env variable {config.digitalocean_token_env_var}")
669
- config.digitalocean_token = os.environ.get(config.digitalocean_token_env_var)
685
+ logger.debug(
686
+ f"Reading token for DigitalOcean from env variable {config.digitalocean_token_env_var}",
687
+ )
688
+ config.digitalocean_token = os.environ.get(
689
+ config.digitalocean_token_env_var,
690
+ )
670
691
  else:
671
692
  config.digitalocean_token = None
672
693
 
@@ -697,11 +718,11 @@ class CLI:
697
718
  config.kandji_token_env_var,
698
719
  )
699
720
  config.kandji_token = os.environ.get(config.kandji_token_env_var)
700
- elif os.environ.get('KANDJI_TOKEN'):
721
+ elif os.environ.get("KANDJI_TOKEN"):
701
722
  logger.debug(
702
723
  "Reading Kandji API token from environment variable 'KANDJI_TOKEN'.",
703
724
  )
704
- config.kandji_token = os.environ.get('KANDJI_TOKEN')
725
+ config.kandji_token = os.environ.get("KANDJI_TOKEN")
705
726
  else:
706
727
  logger.warning("A Kandji base URI was provided but a token was not.")
707
728
  config.kandji_token = None
@@ -711,13 +732,15 @@ class CLI:
711
732
 
712
733
  if config.statsd_enabled:
713
734
  logger.debug(
714
- f'statsd enabled. Sending metrics to server {config.statsd_host}:{config.statsd_port}. '
735
+ f"statsd enabled. Sending metrics to server {config.statsd_host}:{config.statsd_port}. "
715
736
  f'Metrics have prefix "{config.statsd_prefix}".',
716
737
  )
717
738
 
718
739
  # Pagerduty config
719
740
  if config.pagerduty_api_key_env_var:
720
- logger.debug(f"Reading API key for PagerDuty from environment variable {config.pagerduty_api_key_env_var}")
741
+ logger.debug(
742
+ f"Reading API key for PagerDuty from environment variable {config.pagerduty_api_key_env_var}",
743
+ )
721
744
  config.pagerduty_api_key = os.environ.get(config.pagerduty_api_key_env_var)
722
745
  else:
723
746
  config.pagerduty_api_key = None
@@ -727,7 +750,9 @@ class CLI:
727
750
  logger.debug(
728
751
  f"Reading API key for Crowdstrike from environment variable {config.crowdstrike_client_id_env_var}",
729
752
  )
730
- config.crowdstrike_client_id = os.environ.get(config.crowdstrike_client_id_env_var)
753
+ config.crowdstrike_client_id = os.environ.get(
754
+ config.crowdstrike_client_id_env_var,
755
+ )
731
756
  else:
732
757
  config.crowdstrike_client_id = None
733
758
 
@@ -735,36 +760,54 @@ class CLI:
735
760
  logger.debug(
736
761
  f"Reading API key for Crowdstrike from environment variable {config.crowdstrike_client_secret_env_var}",
737
762
  )
738
- config.crowdstrike_client_secret = os.environ.get(config.crowdstrike_client_secret_env_var)
763
+ config.crowdstrike_client_secret = os.environ.get(
764
+ config.crowdstrike_client_secret_env_var,
765
+ )
739
766
  else:
740
767
  config.crowdstrike_client_secret = None
741
768
 
742
769
  # GSuite config
743
770
  if config.gsuite_tokens_env_var:
744
- logger.debug(f"Reading config string for GSuite from environment variable {config.gsuite_tokens_env_var}")
771
+ logger.debug(
772
+ f"Reading config string for GSuite from environment variable {config.gsuite_tokens_env_var}",
773
+ )
745
774
  config.gsuite_config = os.environ.get(config.gsuite_tokens_env_var)
746
775
  else:
747
776
  config.gsuite_tokens_env_var = None
748
777
 
749
778
  # Lastpass config
750
779
  if config.lastpass_cid_env_var:
751
- logger.debug(f"Reading CID for Lastpass from environment variable {config.lastpass_cid_env_var}")
780
+ logger.debug(
781
+ f"Reading CID for Lastpass from environment variable {config.lastpass_cid_env_var}",
782
+ )
752
783
  config.lastpass_cid = os.environ.get(config.lastpass_cid_env_var)
753
784
  else:
754
785
  config.lastpass_cid = None
755
786
  if config.lastpass_provhash_env_var:
756
- logger.debug(f"Reading provhash for Lastpass from environment variable {config.lastpass_provhash_env_var}")
787
+ logger.debug(
788
+ f"Reading provhash for Lastpass from environment variable {config.lastpass_provhash_env_var}",
789
+ )
757
790
  config.lastpass_provhash = os.environ.get(config.lastpass_provhash_env_var)
758
791
  else:
759
792
  config.lastpass_provhash = None
760
793
 
761
794
  # BigFix config
762
- if config.bigfix_username and config.bigfix_password_env_var and config.bigfix_root_url:
763
- logger.debug(f"Reading BigFix password from environment variable {config.bigfix_password_env_var}")
795
+ if (
796
+ config.bigfix_username
797
+ and config.bigfix_password_env_var
798
+ and config.bigfix_root_url
799
+ ):
800
+ logger.debug(
801
+ f"Reading BigFix password from environment variable {config.bigfix_password_env_var}",
802
+ )
764
803
  config.bigfix_password = os.environ.get(config.bigfix_password_env_var)
765
804
 
766
805
  # Duo config
767
- if config.duo_api_key_env_var and config.duo_api_secret_env_var and config.duo_api_hostname:
806
+ if (
807
+ config.duo_api_key_env_var
808
+ and config.duo_api_secret_env_var
809
+ and config.duo_api_hostname
810
+ ):
768
811
  logger.debug(
769
812
  f"Reading Duo api key and secret from environment variables {config.duo_api_key_env_var}"
770
813
  f", {config.duo_api_secret_env_var}",
@@ -777,7 +820,9 @@ class CLI:
777
820
 
778
821
  # Semgrep config
779
822
  if config.semgrep_app_token_env_var:
780
- logger.debug(f"Reading Semgrep App Token from environment variable {config.semgrep_app_token_env_var}")
823
+ logger.debug(
824
+ f"Reading Semgrep App Token from environment variable {config.semgrep_app_token_env_var}",
825
+ )
781
826
  config.semgrep_app_token = os.environ.get(config.semgrep_app_token_env_var)
782
827
  else:
783
828
  config.semgrep_app_token = None
@@ -787,7 +832,9 @@ class CLI:
787
832
 
788
833
  # CVE feed config
789
834
  if config.cve_api_key_env_var:
790
- logger.debug(f"Reading NVD CVE API key environment variable {config.cve_api_key_env_var}")
835
+ logger.debug(
836
+ f"Reading NVD CVE API key environment variable {config.cve_api_key_env_var}",
837
+ )
791
838
  config.cve_api_key = os.environ.get(config.cve_api_key_env_var)
792
839
  else:
793
840
  config.cve_api_key = None
@@ -800,11 +847,11 @@ class CLI:
800
847
  config.snipeit_token_env_var,
801
848
  )
802
849
  config.snipeit_token = os.environ.get(config.snipeit_token_env_var)
803
- elif os.environ.get('SNIPEIT_TOKEN'):
850
+ elif os.environ.get("SNIPEIT_TOKEN"):
804
851
  logger.debug(
805
852
  "Reading SnipeIT API token from environment variable 'SNIPEIT_TOKEN'.",
806
853
  )
807
- config.snipeit_token = os.environ.get('SNIPEIT_TOKEN')
854
+ config.snipeit_token = os.environ.get("SNIPEIT_TOKEN")
808
855
  else:
809
856
  logger.warning("A SnipeIT base URI was provided but a token was not.")
810
857
  config.kandji_token = None
@@ -829,12 +876,14 @@ def main(argv=None):
829
876
  :return: The return code.
830
877
  """
831
878
  logging.basicConfig(level=logging.INFO)
832
- logging.getLogger('botocore').setLevel(logging.WARNING)
833
- logging.getLogger('googleapiclient').setLevel(logging.WARNING)
834
- logging.getLogger('neo4j').setLevel(logging.WARNING)
835
- logging.getLogger('azure.identity').setLevel(logging.WARNING)
836
- logging.getLogger('httpx').setLevel(logging.WARNING)
837
- logging.getLogger('azure.core.pipeline.policies.http_logging_policy').setLevel(logging.WARNING)
879
+ logging.getLogger("botocore").setLevel(logging.WARNING)
880
+ logging.getLogger("googleapiclient").setLevel(logging.WARNING)
881
+ logging.getLogger("neo4j").setLevel(logging.WARNING)
882
+ logging.getLogger("azure.identity").setLevel(logging.WARNING)
883
+ logging.getLogger("httpx").setLevel(logging.WARNING)
884
+ logging.getLogger("azure.core.pipeline.policies.http_logging_policy").setLevel(
885
+ logging.WARNING
886
+ )
838
887
 
839
888
  argv = argv if argv is not None else sys.argv[1:]
840
- sys.exit(CLI(prog='cartography').main(argv))
889
+ sys.exit(CLI(prog="cartography").main(argv))