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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (822) hide show
  1. cartography/__main__.py +1 -2
  2. cartography/_version.py +34 -0
  3. cartography/cli.py +903 -225
  4. cartography/client/aws/__init__.py +19 -0
  5. cartography/client/aws/ecr.py +51 -0
  6. cartography/client/core/tx.py +400 -27
  7. cartography/config.py +215 -10
  8. cartography/data/azure_permission_relationships.yaml +20 -0
  9. cartography/data/gcp_permission_relationships.yaml +21 -0
  10. cartography/data/indexes.cypher +1 -200
  11. cartography/data/jobs/analysis/aws_ec2_asset_exposure.json +17 -2
  12. cartography/data/jobs/analysis/aws_ec2_keypair_analysis.json +2 -2
  13. cartography/data/jobs/analysis/gcp_compute_asset_inet_exposure.json +1 -1
  14. cartography/data/jobs/analysis/keycloak_inheritance.json +30 -0
  15. cartography/data/jobs/cleanup/crowdstrike_import_cleanup.json +0 -5
  16. cartography/data/jobs/cleanup/gcp_compute_vpc_cleanup.json +0 -12
  17. cartography/data/jobs/cleanup/github_repos_cleanup.json +27 -0
  18. cartography/data/jobs/scoped_analysis/aws_ec2_iaminstanceprofile.json +15 -0
  19. cartography/data/jobs/scoped_analysis/semgrep_sca_risk_analysis.json +13 -13
  20. cartography/driftdetect/__main__.py +1 -2
  21. cartography/driftdetect/add_shortcut.py +10 -2
  22. cartography/driftdetect/cli.py +72 -75
  23. cartography/driftdetect/detect_deviations.py +7 -3
  24. cartography/driftdetect/get_states.py +20 -8
  25. cartography/driftdetect/model.py +5 -5
  26. cartography/driftdetect/serializers.py +8 -6
  27. cartography/driftdetect/storage.py +2 -2
  28. cartography/graph/cleanupbuilder.py +255 -35
  29. cartography/graph/job.py +104 -20
  30. cartography/graph/querybuilder.py +689 -91
  31. cartography/graph/statement.py +49 -36
  32. cartography/intel/airbyte/__init__.py +105 -0
  33. cartography/intel/airbyte/connections.py +120 -0
  34. cartography/intel/airbyte/destinations.py +81 -0
  35. cartography/intel/airbyte/organizations.py +59 -0
  36. cartography/intel/airbyte/sources.py +78 -0
  37. cartography/intel/airbyte/tags.py +64 -0
  38. cartography/intel/airbyte/users.py +106 -0
  39. cartography/intel/airbyte/util.py +122 -0
  40. cartography/intel/airbyte/workspaces.py +63 -0
  41. cartography/intel/analysis.py +4 -1
  42. cartography/intel/anthropic/__init__.py +62 -0
  43. cartography/intel/anthropic/apikeys.py +72 -0
  44. cartography/intel/anthropic/users.py +75 -0
  45. cartography/intel/anthropic/util.py +51 -0
  46. cartography/intel/anthropic/workspaces.py +95 -0
  47. cartography/intel/aws/__init__.py +137 -59
  48. cartography/intel/aws/acm.py +124 -0
  49. cartography/intel/aws/apigateway.py +482 -217
  50. cartography/intel/aws/apigatewayv2.py +116 -0
  51. cartography/intel/aws/cloudtrail.py +105 -0
  52. cartography/intel/aws/cloudtrail_management_events.py +962 -0
  53. cartography/intel/aws/cloudwatch.py +239 -0
  54. cartography/intel/aws/codebuild.py +132 -0
  55. cartography/intel/aws/cognito.py +201 -0
  56. cartography/intel/aws/config.py +63 -23
  57. cartography/intel/aws/dynamodb.py +108 -40
  58. cartography/intel/aws/ec2/__init__.py +2 -2
  59. cartography/intel/aws/ec2/auto_scaling_groups.py +254 -189
  60. cartography/intel/aws/ec2/elastic_ip_addresses.py +44 -14
  61. cartography/intel/aws/ec2/images.py +74 -39
  62. cartography/intel/aws/ec2/instances.py +262 -137
  63. cartography/intel/aws/ec2/internet_gateways.py +44 -13
  64. cartography/intel/aws/ec2/key_pairs.py +72 -39
  65. cartography/intel/aws/ec2/launch_templates.py +143 -66
  66. cartography/intel/aws/ec2/load_balancer_v2s.py +119 -45
  67. cartography/intel/aws/ec2/load_balancers.py +165 -147
  68. cartography/intel/aws/ec2/network_acls.py +233 -0
  69. cartography/intel/aws/ec2/network_interfaces.py +150 -87
  70. cartography/intel/aws/ec2/reserved_instances.py +48 -17
  71. cartography/intel/aws/ec2/route_tables.py +327 -0
  72. cartography/intel/aws/ec2/security_groups.py +189 -121
  73. cartography/intel/aws/ec2/snapshots.py +93 -91
  74. cartography/intel/aws/ec2/subnets.py +70 -58
  75. cartography/intel/aws/ec2/tgw.py +111 -39
  76. cartography/intel/aws/ec2/util.py +1 -1
  77. cartography/intel/aws/ec2/volumes.py +69 -41
  78. cartography/intel/aws/ec2/vpc.py +157 -116
  79. cartography/intel/aws/ec2/vpc_peerings.py +317 -121
  80. cartography/intel/aws/ecr.py +336 -93
  81. cartography/intel/aws/ecr_image_layers.py +923 -0
  82. cartography/intel/aws/ecs.py +310 -403
  83. cartography/intel/aws/efs.py +261 -0
  84. cartography/intel/aws/eks.py +55 -29
  85. cartography/intel/aws/elasticache.py +130 -83
  86. cartography/intel/aws/elasticsearch.py +70 -24
  87. cartography/intel/aws/emr.py +61 -23
  88. cartography/intel/aws/eventbridge.py +164 -0
  89. cartography/intel/aws/glue.py +181 -0
  90. cartography/intel/aws/guardduty.py +443 -0
  91. cartography/intel/aws/iam.py +978 -464
  92. cartography/intel/aws/iam_instance_profiles.py +73 -0
  93. cartography/intel/aws/identitycenter.py +847 -0
  94. cartography/intel/aws/inspector.py +330 -133
  95. cartography/intel/aws/kms.py +235 -209
  96. cartography/intel/aws/lambda_function.py +328 -176
  97. cartography/intel/aws/organizations.py +40 -19
  98. cartography/intel/aws/permission_relationships.py +144 -68
  99. cartography/intel/aws/rds.py +467 -412
  100. cartography/intel/aws/redshift.py +116 -50
  101. cartography/intel/aws/resourcegroupstaggingapi.py +198 -82
  102. cartography/intel/aws/resources.py +80 -42
  103. cartography/intel/aws/route53.py +419 -318
  104. cartography/intel/aws/s3.py +489 -96
  105. cartography/intel/aws/s3accountpublicaccessblock.py +157 -0
  106. cartography/intel/aws/secretsmanager.py +217 -40
  107. cartography/intel/aws/securityhub.py +23 -10
  108. cartography/intel/aws/sns.py +226 -0
  109. cartography/intel/aws/sqs.py +74 -96
  110. cartography/intel/aws/ssm.py +142 -33
  111. cartography/intel/aws/util/arns.py +7 -7
  112. cartography/intel/aws/util/common.py +31 -4
  113. cartography/intel/azure/__init__.py +259 -46
  114. cartography/intel/azure/aks.py +175 -0
  115. cartography/intel/azure/app_service.py +105 -0
  116. cartography/intel/azure/compute.py +141 -120
  117. cartography/intel/azure/container_instances.py +95 -0
  118. cartography/intel/azure/cosmosdb.py +706 -519
  119. cartography/intel/azure/data_factory.py +85 -0
  120. cartography/intel/azure/data_factory_dataset.py +128 -0
  121. cartography/intel/azure/data_factory_linked_service.py +119 -0
  122. cartography/intel/azure/data_factory_pipeline.py +142 -0
  123. cartography/intel/azure/data_lake.py +124 -0
  124. cartography/intel/azure/event_grid.py +94 -0
  125. cartography/intel/azure/functions.py +124 -0
  126. cartography/intel/azure/load_balancers.py +263 -0
  127. cartography/intel/azure/logic_apps.py +101 -0
  128. cartography/intel/azure/monitor.py +105 -0
  129. cartography/intel/azure/network.py +467 -0
  130. cartography/intel/azure/permission_relationships.py +466 -0
  131. cartography/intel/azure/rbac.py +309 -0
  132. cartography/intel/azure/resource_groups.py +82 -0
  133. cartography/intel/azure/security_center.py +106 -0
  134. cartography/intel/azure/sql.py +436 -392
  135. cartography/intel/azure/storage.py +467 -335
  136. cartography/intel/azure/subscription.py +49 -55
  137. cartography/intel/azure/tenant.py +46 -28
  138. cartography/intel/azure/util/common.py +13 -0
  139. cartography/intel/azure/util/credentials.py +58 -143
  140. cartography/intel/azure/util/tag.py +41 -0
  141. cartography/intel/bigfix/__init__.py +2 -2
  142. cartography/intel/bigfix/computers.py +93 -65
  143. cartography/intel/cloudflare/__init__.py +74 -0
  144. cartography/intel/cloudflare/accounts.py +57 -0
  145. cartography/intel/cloudflare/dnsrecords.py +64 -0
  146. cartography/intel/cloudflare/members.py +75 -0
  147. cartography/intel/cloudflare/roles.py +65 -0
  148. cartography/intel/cloudflare/zones.py +64 -0
  149. cartography/intel/create_indexes.py +5 -3
  150. cartography/intel/crowdstrike/__init__.py +26 -12
  151. cartography/intel/crowdstrike/endpoints.py +17 -45
  152. cartography/intel/crowdstrike/spotlight.py +13 -5
  153. cartography/intel/cve/__init__.py +91 -26
  154. cartography/intel/cve/feed.py +77 -56
  155. cartography/intel/digitalocean/__init__.py +22 -13
  156. cartography/intel/digitalocean/compute.py +75 -108
  157. cartography/intel/digitalocean/management.py +44 -80
  158. cartography/intel/digitalocean/platform.py +48 -43
  159. cartography/intel/dns.py +41 -12
  160. cartography/intel/duo/__init__.py +21 -16
  161. cartography/intel/duo/api_host.py +14 -9
  162. cartography/intel/duo/endpoints.py +50 -45
  163. cartography/intel/duo/groups.py +18 -14
  164. cartography/intel/duo/phones.py +37 -34
  165. cartography/intel/duo/tokens.py +26 -23
  166. cartography/intel/duo/users.py +54 -50
  167. cartography/intel/duo/web_authn_credentials.py +30 -25
  168. cartography/intel/entra/__init__.py +160 -0
  169. cartography/intel/entra/app_role_assignments.py +284 -0
  170. cartography/intel/entra/applications.py +182 -0
  171. cartography/intel/entra/federation/__init__.py +0 -0
  172. cartography/intel/entra/federation/aws_identity_center.py +77 -0
  173. cartography/intel/entra/groups.py +198 -0
  174. cartography/intel/entra/ou.py +136 -0
  175. cartography/intel/entra/service_principals.py +217 -0
  176. cartography/intel/entra/users.py +259 -0
  177. cartography/intel/gcp/__init__.py +381 -175
  178. cartography/intel/gcp/bigtable_app_profile.py +101 -0
  179. cartography/intel/gcp/bigtable_backup.py +91 -0
  180. cartography/intel/gcp/bigtable_cluster.py +93 -0
  181. cartography/intel/gcp/bigtable_instance.py +86 -0
  182. cartography/intel/gcp/bigtable_table.py +87 -0
  183. cartography/intel/gcp/cai.py +292 -0
  184. cartography/intel/gcp/clients.py +112 -0
  185. cartography/intel/gcp/compute.py +521 -325
  186. cartography/intel/gcp/crm/__init__.py +0 -0
  187. cartography/intel/gcp/crm/folders.py +114 -0
  188. cartography/intel/gcp/crm/orgs.py +70 -0
  189. cartography/intel/gcp/crm/projects.py +120 -0
  190. cartography/intel/gcp/dns.py +134 -179
  191. cartography/intel/gcp/gke.py +100 -107
  192. cartography/intel/gcp/iam.py +262 -0
  193. cartography/intel/gcp/permission_relationships.py +394 -0
  194. cartography/intel/gcp/policy_bindings.py +225 -0
  195. cartography/intel/gcp/storage.py +103 -158
  196. cartography/intel/github/__init__.py +66 -27
  197. cartography/intel/github/commits.py +423 -0
  198. cartography/intel/github/repos.py +871 -160
  199. cartography/intel/github/teams.py +386 -53
  200. cartography/intel/github/users.py +214 -49
  201. cartography/intel/github/util.py +50 -35
  202. cartography/intel/googleworkspace/__init__.py +193 -0
  203. cartography/intel/googleworkspace/devices.py +254 -0
  204. cartography/intel/googleworkspace/groups.py +568 -0
  205. cartography/intel/googleworkspace/oauth_apps.py +259 -0
  206. cartography/intel/googleworkspace/tenant.py +85 -0
  207. cartography/intel/googleworkspace/users.py +138 -0
  208. cartography/intel/gsuite/__init__.py +101 -42
  209. cartography/intel/gsuite/groups.py +291 -0
  210. cartography/intel/gsuite/users.py +142 -0
  211. cartography/intel/jamf/__init__.py +19 -1
  212. cartography/intel/jamf/computers.py +37 -8
  213. cartography/intel/jamf/util.py +7 -2
  214. cartography/intel/kandji/__init__.py +6 -3
  215. cartography/intel/kandji/devices.py +40 -10
  216. cartography/intel/keycloak/__init__.py +153 -0
  217. cartography/intel/keycloak/authenticationexecutions.py +322 -0
  218. cartography/intel/keycloak/authenticationflows.py +77 -0
  219. cartography/intel/keycloak/clients.py +187 -0
  220. cartography/intel/keycloak/groups.py +126 -0
  221. cartography/intel/keycloak/identityproviders.py +94 -0
  222. cartography/intel/keycloak/organizations.py +163 -0
  223. cartography/intel/keycloak/realms.py +61 -0
  224. cartography/intel/keycloak/roles.py +202 -0
  225. cartography/intel/keycloak/scopes.py +73 -0
  226. cartography/intel/keycloak/users.py +70 -0
  227. cartography/intel/keycloak/util.py +47 -0
  228. cartography/intel/kubernetes/__init__.py +60 -14
  229. cartography/intel/kubernetes/clusters.py +86 -0
  230. cartography/intel/kubernetes/eks.py +402 -0
  231. cartography/intel/kubernetes/namespaces.py +60 -55
  232. cartography/intel/kubernetes/pods.py +171 -75
  233. cartography/intel/kubernetes/rbac.py +597 -0
  234. cartography/intel/kubernetes/secrets.py +95 -45
  235. cartography/intel/kubernetes/services.py +131 -63
  236. cartography/intel/kubernetes/util.py +142 -14
  237. cartography/intel/lastpass/__init__.py +2 -2
  238. cartography/intel/lastpass/users.py +23 -12
  239. cartography/intel/oci/__init__.py +44 -11
  240. cartography/intel/oci/iam.py +157 -47
  241. cartography/intel/oci/organizations.py +16 -7
  242. cartography/intel/oci/utils.py +71 -25
  243. cartography/intel/okta/__init__.py +66 -15
  244. cartography/intel/okta/applications.py +57 -25
  245. cartography/intel/okta/awssaml.py +105 -41
  246. cartography/intel/okta/factors.py +19 -5
  247. cartography/intel/okta/groups.py +61 -31
  248. cartography/intel/okta/organization.py +8 -2
  249. cartography/intel/okta/origins.py +9 -3
  250. cartography/intel/okta/roles.py +20 -7
  251. cartography/intel/okta/users.py +31 -10
  252. cartography/intel/okta/utils.py +6 -4
  253. cartography/intel/ontology/__init__.py +44 -0
  254. cartography/intel/ontology/devices.py +54 -0
  255. cartography/intel/ontology/users.py +54 -0
  256. cartography/intel/ontology/utils.py +176 -0
  257. cartography/intel/openai/__init__.py +86 -0
  258. cartography/intel/openai/adminapikeys.py +89 -0
  259. cartography/intel/openai/apikeys.py +96 -0
  260. cartography/intel/openai/projects.py +97 -0
  261. cartography/intel/openai/serviceaccounts.py +82 -0
  262. cartography/intel/openai/users.py +75 -0
  263. cartography/intel/openai/util.py +45 -0
  264. cartography/intel/pagerduty/__init__.py +8 -7
  265. cartography/intel/pagerduty/escalation_policies.py +31 -12
  266. cartography/intel/pagerduty/schedules.py +21 -8
  267. cartography/intel/pagerduty/services.py +18 -7
  268. cartography/intel/pagerduty/teams.py +13 -5
  269. cartography/intel/pagerduty/users.py +6 -2
  270. cartography/intel/pagerduty/vendors.py +6 -2
  271. cartography/intel/scaleway/__init__.py +127 -0
  272. cartography/intel/scaleway/iam/__init__.py +0 -0
  273. cartography/intel/scaleway/iam/apikeys.py +71 -0
  274. cartography/intel/scaleway/iam/applications.py +71 -0
  275. cartography/intel/scaleway/iam/groups.py +71 -0
  276. cartography/intel/scaleway/iam/users.py +71 -0
  277. cartography/intel/scaleway/instances/__init__.py +0 -0
  278. cartography/intel/scaleway/instances/flexibleips.py +86 -0
  279. cartography/intel/scaleway/instances/instances.py +92 -0
  280. cartography/intel/scaleway/projects.py +79 -0
  281. cartography/intel/scaleway/storage/__init__.py +0 -0
  282. cartography/intel/scaleway/storage/snapshots.py +86 -0
  283. cartography/intel/scaleway/storage/volumes.py +84 -0
  284. cartography/intel/scaleway/utils.py +37 -0
  285. cartography/intel/semgrep/__init__.py +30 -5
  286. cartography/intel/semgrep/dependencies.py +255 -0
  287. cartography/intel/semgrep/deployment.py +69 -0
  288. cartography/intel/semgrep/findings.py +157 -117
  289. cartography/intel/sentinelone/__init__.py +75 -0
  290. cartography/intel/sentinelone/account.py +140 -0
  291. cartography/intel/sentinelone/agent.py +139 -0
  292. cartography/intel/sentinelone/api.py +124 -0
  293. cartography/intel/sentinelone/application.py +248 -0
  294. cartography/intel/sentinelone/cve.py +119 -0
  295. cartography/intel/sentinelone/utils.py +28 -0
  296. cartography/intel/slack/__init__.py +78 -0
  297. cartography/intel/slack/channels.py +80 -0
  298. cartography/intel/slack/groups.py +90 -0
  299. cartography/intel/slack/teams.py +65 -0
  300. cartography/intel/slack/users.py +57 -0
  301. cartography/intel/slack/utils.py +29 -0
  302. cartography/intel/snipeit/__init__.py +44 -0
  303. cartography/intel/snipeit/asset.py +80 -0
  304. cartography/intel/snipeit/user.py +78 -0
  305. cartography/intel/snipeit/util.py +40 -0
  306. cartography/intel/spacelift/__init__.py +161 -0
  307. cartography/intel/spacelift/account.py +73 -0
  308. cartography/intel/spacelift/ec2_ownership.py +280 -0
  309. cartography/intel/spacelift/runs.py +463 -0
  310. cartography/intel/spacelift/spaces.py +112 -0
  311. cartography/intel/spacelift/stacks.py +119 -0
  312. cartography/intel/spacelift/util.py +122 -0
  313. cartography/intel/spacelift/workerpools.py +131 -0
  314. cartography/intel/spacelift/workers.py +128 -0
  315. cartography/intel/tailscale/__init__.py +77 -0
  316. cartography/intel/tailscale/acls.py +146 -0
  317. cartography/intel/tailscale/devices.py +127 -0
  318. cartography/intel/tailscale/postureintegrations.py +81 -0
  319. cartography/intel/tailscale/tailnets.py +76 -0
  320. cartography/intel/tailscale/users.py +80 -0
  321. cartography/intel/tailscale/utils.py +132 -0
  322. cartography/intel/trivy/__init__.py +272 -0
  323. cartography/intel/trivy/scanner.py +386 -0
  324. cartography/models/airbyte/__init__.py +0 -0
  325. cartography/models/airbyte/connection.py +138 -0
  326. cartography/models/airbyte/destination.py +75 -0
  327. cartography/models/airbyte/organization.py +19 -0
  328. cartography/models/airbyte/source.py +75 -0
  329. cartography/models/airbyte/stream.py +74 -0
  330. cartography/models/airbyte/tag.py +69 -0
  331. cartography/models/airbyte/user.py +115 -0
  332. cartography/models/airbyte/workspace.py +46 -0
  333. cartography/models/anthropic/__init__.py +0 -0
  334. cartography/models/anthropic/apikey.py +94 -0
  335. cartography/models/anthropic/organization.py +19 -0
  336. cartography/models/anthropic/user.py +52 -0
  337. cartography/models/anthropic/workspace.py +90 -0
  338. cartography/models/aws/acm/__init__.py +0 -0
  339. cartography/models/aws/acm/certificate.py +75 -0
  340. cartography/models/aws/apigateway/__init__.py +0 -0
  341. cartography/models/aws/apigateway/apigateway.py +51 -0
  342. cartography/models/aws/apigateway/apigatewaycertificate.py +72 -0
  343. cartography/models/aws/apigateway/apigatewaydeployment.py +74 -0
  344. cartography/models/aws/apigateway/apigatewayintegration.py +79 -0
  345. cartography/models/aws/apigateway/apigatewaymethod.py +74 -0
  346. cartography/models/aws/apigateway/apigatewayresource.py +70 -0
  347. cartography/models/aws/apigateway/apigatewaystage.py +75 -0
  348. cartography/models/aws/apigatewayv2/__init__.py +0 -0
  349. cartography/models/aws/apigatewayv2/apigatewayv2.py +53 -0
  350. cartography/models/aws/cloudtrail/__init__.py +0 -0
  351. cartography/models/aws/cloudtrail/management_events.py +153 -0
  352. cartography/models/aws/cloudtrail/trail.py +106 -0
  353. cartography/models/aws/cloudwatch/__init__.py +0 -0
  354. cartography/models/aws/cloudwatch/log_metric_filter.py +79 -0
  355. cartography/models/aws/cloudwatch/loggroup.py +52 -0
  356. cartography/models/aws/cloudwatch/metric_alarm.py +53 -0
  357. cartography/models/aws/codebuild/__init__.py +0 -0
  358. cartography/models/aws/codebuild/project.py +49 -0
  359. cartography/models/aws/cognito/__init__.py +0 -0
  360. cartography/models/aws/cognito/identity_pool.py +70 -0
  361. cartography/models/aws/cognito/user_pool.py +47 -0
  362. cartography/models/aws/dynamodb/gsi.py +30 -22
  363. cartography/models/aws/dynamodb/tables.py +27 -17
  364. cartography/models/aws/ec2/auto_scaling_groups.py +224 -0
  365. cartography/models/aws/ec2/images.py +36 -34
  366. cartography/models/aws/ec2/instances.py +85 -38
  367. cartography/models/aws/ec2/keypair.py +59 -0
  368. cartography/models/aws/ec2/keypair_instance.py +76 -0
  369. cartography/models/aws/ec2/launch_configurations.py +59 -0
  370. cartography/models/aws/ec2/launch_template_versions.py +48 -38
  371. cartography/models/aws/ec2/launch_templates.py +21 -17
  372. cartography/models/aws/ec2/load_balancer_listeners.py +72 -0
  373. cartography/models/aws/ec2/load_balancers.py +112 -0
  374. cartography/models/aws/ec2/network_acl_rules.py +106 -0
  375. cartography/models/aws/ec2/network_acls.py +95 -0
  376. cartography/models/aws/ec2/networkinterface_instance.py +52 -39
  377. cartography/models/aws/ec2/networkinterfaces.py +57 -37
  378. cartography/models/aws/ec2/privateip_networkinterface.py +32 -22
  379. cartography/models/aws/ec2/reservations.py +18 -14
  380. cartography/models/aws/ec2/route_table_associations.py +97 -0
  381. cartography/models/aws/ec2/route_tables.py +128 -0
  382. cartography/models/aws/ec2/routes.py +85 -0
  383. cartography/models/aws/ec2/security_group_rules.py +109 -0
  384. cartography/models/aws/ec2/security_groups.py +90 -0
  385. cartography/models/aws/ec2/securitygroup_instance.py +29 -20
  386. cartography/models/aws/ec2/securitygroup_networkinterface.py +24 -15
  387. cartography/models/aws/ec2/snapshots.py +58 -0
  388. cartography/models/aws/ec2/subnet_instance.py +26 -19
  389. cartography/models/aws/ec2/subnet_networkinterface.py +42 -31
  390. cartography/models/aws/ec2/subnets.py +65 -0
  391. cartography/models/aws/ec2/volumes.py +67 -40
  392. cartography/models/aws/ec2/vpc.py +46 -0
  393. cartography/models/aws/ec2/vpc_cidr.py +102 -0
  394. cartography/models/aws/ec2/vpc_peering.py +157 -0
  395. cartography/models/aws/ecr/__init__.py +0 -0
  396. cartography/models/aws/ecr/image.py +146 -0
  397. cartography/models/aws/ecr/image_layer.py +107 -0
  398. cartography/models/aws/ecr/repository.py +72 -0
  399. cartography/models/aws/ecr/repository_image.py +95 -0
  400. cartography/models/aws/ecs/__init__.py +0 -0
  401. cartography/models/aws/ecs/clusters.py +64 -0
  402. cartography/models/aws/ecs/container_definitions.py +93 -0
  403. cartography/models/aws/ecs/container_instances.py +84 -0
  404. cartography/models/aws/ecs/containers.py +101 -0
  405. cartography/models/aws/ecs/services.py +134 -0
  406. cartography/models/aws/ecs/task_definitions.py +135 -0
  407. cartography/models/aws/ecs/tasks.py +134 -0
  408. cartography/models/aws/efs/__init__.py +0 -0
  409. cartography/models/aws/efs/access_point.py +77 -0
  410. cartography/models/aws/efs/file_system.py +60 -0
  411. cartography/models/aws/efs/mount_target.py +79 -0
  412. cartography/models/aws/eks/clusters.py +23 -21
  413. cartography/models/aws/elasticache/__init__.py +0 -0
  414. cartography/models/aws/elasticache/cluster.py +65 -0
  415. cartography/models/aws/elasticache/topic.py +67 -0
  416. cartography/models/aws/emr.py +32 -30
  417. cartography/models/aws/eventbridge/__init__.py +0 -0
  418. cartography/models/aws/eventbridge/rule.py +77 -0
  419. cartography/models/aws/eventbridge/target.py +71 -0
  420. cartography/models/aws/glue/__init__.py +0 -0
  421. cartography/models/aws/glue/connection.py +51 -0
  422. cartography/models/aws/glue/job.py +69 -0
  423. cartography/models/aws/guardduty/__init__.py +1 -0
  424. cartography/models/aws/guardduty/detectors.py +50 -0
  425. cartography/models/aws/guardduty/findings.py +121 -0
  426. cartography/models/aws/iam/__init__.py +0 -0
  427. cartography/models/aws/iam/access_key.py +103 -0
  428. cartography/models/aws/iam/account_role.py +24 -0
  429. cartography/models/aws/iam/federated_principal.py +60 -0
  430. cartography/models/aws/iam/group.py +60 -0
  431. cartography/models/aws/iam/group_membership.py +27 -0
  432. cartography/models/aws/iam/inline_policy.py +78 -0
  433. cartography/models/aws/iam/instanceprofile.py +76 -0
  434. cartography/models/aws/iam/managed_policy.py +51 -0
  435. cartography/models/aws/iam/policy_statement.py +57 -0
  436. cartography/models/aws/iam/role.py +83 -0
  437. cartography/models/aws/iam/root_principal.py +52 -0
  438. cartography/models/aws/iam/service_principal.py +30 -0
  439. cartography/models/aws/iam/sts_assumerole_allow.py +38 -0
  440. cartography/models/aws/iam/user.py +59 -0
  441. cartography/models/aws/identitycenter/__init__.py +0 -0
  442. cartography/models/aws/identitycenter/awsidentitycenter.py +49 -0
  443. cartography/models/aws/identitycenter/awspermissionset.py +162 -0
  444. cartography/models/aws/identitycenter/awssogroup.py +70 -0
  445. cartography/models/aws/identitycenter/awsssouser.py +110 -0
  446. cartography/models/aws/inspector/findings.py +124 -58
  447. cartography/models/aws/inspector/packages.py +18 -42
  448. cartography/models/aws/kms/__init__.py +0 -0
  449. cartography/models/aws/kms/aliases.py +86 -0
  450. cartography/models/aws/kms/grants.py +65 -0
  451. cartography/models/aws/kms/keys.py +88 -0
  452. cartography/models/aws/lambda_function/__init__.py +0 -0
  453. cartography/models/aws/lambda_function/alias.py +74 -0
  454. cartography/models/aws/lambda_function/event_source_mapping.py +88 -0
  455. cartography/models/aws/lambda_function/lambda_function.py +91 -0
  456. cartography/models/aws/lambda_function/layer.py +72 -0
  457. cartography/models/aws/rds/__init__.py +0 -0
  458. cartography/models/aws/rds/cluster.py +91 -0
  459. cartography/models/aws/rds/event_subscription.py +146 -0
  460. cartography/models/aws/rds/instance.py +156 -0
  461. cartography/models/aws/rds/snapshot.py +108 -0
  462. cartography/models/aws/rds/subnet_group.py +101 -0
  463. cartography/models/aws/route53/__init__.py +0 -0
  464. cartography/models/aws/route53/dnsrecord.py +235 -0
  465. cartography/models/aws/route53/nameserver.py +63 -0
  466. cartography/models/aws/route53/subzone.py +40 -0
  467. cartography/models/aws/route53/zone.py +47 -0
  468. cartography/models/aws/s3/__init__.py +0 -0
  469. cartography/models/aws/s3/account_public_access_block.py +51 -0
  470. cartography/models/aws/s3/notification.py +24 -0
  471. cartography/models/aws/secretsmanager/__init__.py +0 -0
  472. cartography/models/aws/secretsmanager/secret.py +106 -0
  473. cartography/models/aws/secretsmanager/secret_version.py +114 -0
  474. cartography/models/aws/sns/__init__.py +0 -0
  475. cartography/models/aws/sns/topic.py +50 -0
  476. cartography/models/aws/sns/topic_subscription.py +74 -0
  477. cartography/models/aws/sqs/__init__.py +0 -0
  478. cartography/models/aws/sqs/queue.py +89 -0
  479. cartography/models/aws/ssm/instance_information.py +51 -39
  480. cartography/models/aws/ssm/instance_patch.py +32 -26
  481. cartography/models/aws/ssm/parameters.py +84 -0
  482. cartography/models/azure/__init__.py +0 -0
  483. cartography/models/azure/aks_cluster.py +54 -0
  484. cartography/models/azure/aks_nodepool.py +54 -0
  485. cartography/models/azure/app_service.py +59 -0
  486. cartography/models/azure/container_instance.py +57 -0
  487. cartography/models/azure/cosmosdb/__init__.py +0 -0
  488. cartography/models/azure/cosmosdb/account.py +77 -0
  489. cartography/models/azure/cosmosdb/accountfailoverpolicy.py +77 -0
  490. cartography/models/azure/cosmosdb/cassandrakeyspace.py +82 -0
  491. cartography/models/azure/cosmosdb/cassandratable.py +81 -0
  492. cartography/models/azure/cosmosdb/corspolicy.py +74 -0
  493. cartography/models/azure/cosmosdb/dblocation.py +120 -0
  494. cartography/models/azure/cosmosdb/mongodbcollection.py +82 -0
  495. cartography/models/azure/cosmosdb/mongodbdatabase.py +78 -0
  496. cartography/models/azure/cosmosdb/privateendpointconnection.py +81 -0
  497. cartography/models/azure/cosmosdb/sqlcontainer.py +88 -0
  498. cartography/models/azure/cosmosdb/sqldatabase.py +78 -0
  499. cartography/models/azure/cosmosdb/tableresource.py +76 -0
  500. cartography/models/azure/cosmosdb/virtualnetworkrule.py +78 -0
  501. cartography/models/azure/data_factory/__init__.py +0 -0
  502. cartography/models/azure/data_factory/data_factory.py +51 -0
  503. cartography/models/azure/data_factory/data_factory_dataset.py +94 -0
  504. cartography/models/azure/data_factory/data_factory_linked_service.py +78 -0
  505. cartography/models/azure/data_factory/data_factory_pipeline.py +93 -0
  506. cartography/models/azure/data_lake_filesystem.py +51 -0
  507. cartography/models/azure/event_grid_topic.py +57 -0
  508. cartography/models/azure/function_app.py +59 -0
  509. cartography/models/azure/load_balancer/__init__.py +0 -0
  510. cartography/models/azure/load_balancer/load_balancer.py +49 -0
  511. cartography/models/azure/load_balancer/load_balancer_backend_pool.py +73 -0
  512. cartography/models/azure/load_balancer/load_balancer_frontend_ip.py +75 -0
  513. cartography/models/azure/load_balancer/load_balancer_inbound_nat_rule.py +78 -0
  514. cartography/models/azure/load_balancer/load_balancer_rule.py +108 -0
  515. cartography/models/azure/logic_apps.py +56 -0
  516. cartography/models/azure/monitor.py +54 -0
  517. cartography/models/azure/network_interface.py +112 -0
  518. cartography/models/azure/network_security_group.py +50 -0
  519. cartography/models/azure/permission_relationships.py +60 -0
  520. cartography/models/azure/principal.py +41 -0
  521. cartography/models/azure/public_ip_address.py +50 -0
  522. cartography/models/azure/rbac.py +268 -0
  523. cartography/models/azure/resource_groups.py +52 -0
  524. cartography/models/azure/security_center.py +50 -0
  525. cartography/models/azure/sql/__init__.py +0 -0
  526. cartography/models/azure/sql/databasethreatdetectionpolicy.py +85 -0
  527. cartography/models/azure/sql/elasticpool.py +77 -0
  528. cartography/models/azure/sql/failovergroup.py +73 -0
  529. cartography/models/azure/sql/recoverabledatabase.py +75 -0
  530. cartography/models/azure/sql/replicationlink.py +81 -0
  531. cartography/models/azure/sql/restorabledroppeddatabase.py +82 -0
  532. cartography/models/azure/sql/restorepoint.py +74 -0
  533. cartography/models/azure/sql/serveradadministrator.py +74 -0
  534. cartography/models/azure/sql/serverdnsalias.py +71 -0
  535. cartography/models/azure/sql/sqldatabase.py +85 -0
  536. cartography/models/azure/sql/sqlserver.py +50 -0
  537. cartography/models/azure/sql/transparentdataencryption.py +76 -0
  538. cartography/models/azure/storage/__init__.py +0 -0
  539. cartography/models/azure/storage/account.py +59 -0
  540. cartography/models/azure/storage/blobcontainer.py +85 -0
  541. cartography/models/azure/storage/blobservice.py +71 -0
  542. cartography/models/azure/storage/fileservice.py +71 -0
  543. cartography/models/azure/storage/fileshare.py +82 -0
  544. cartography/models/azure/storage/queue.py +71 -0
  545. cartography/models/azure/storage/queueservice.py +73 -0
  546. cartography/models/azure/storage/table.py +72 -0
  547. cartography/models/azure/storage/tableservice.py +73 -0
  548. cartography/models/azure/subnet.py +101 -0
  549. cartography/models/azure/subscription.py +47 -0
  550. cartography/models/azure/tags/__init__.py +0 -0
  551. cartography/models/azure/tags/storage_tag.py +40 -0
  552. cartography/models/azure/tags/tag.py +37 -0
  553. cartography/models/azure/tenant.py +17 -0
  554. cartography/models/azure/virtual_network.py +49 -0
  555. cartography/models/azure/vm/__init__.py +0 -0
  556. cartography/models/azure/vm/datadisk.py +80 -0
  557. cartography/models/azure/vm/disk.py +55 -0
  558. cartography/models/azure/vm/snapshot.py +56 -0
  559. cartography/models/azure/vm/virtualmachine.py +59 -0
  560. cartography/models/bigfix/bigfix_computer.py +42 -38
  561. cartography/models/bigfix/bigfix_root.py +3 -3
  562. cartography/models/cloudflare/__init__.py +0 -0
  563. cartography/models/cloudflare/account.py +25 -0
  564. cartography/models/cloudflare/dnsrecord.py +55 -0
  565. cartography/models/cloudflare/member.py +86 -0
  566. cartography/models/cloudflare/role.py +44 -0
  567. cartography/models/cloudflare/zone.py +59 -0
  568. cartography/models/core/common.py +53 -2
  569. cartography/models/core/nodes.py +20 -4
  570. cartography/models/core/relationships.py +58 -6
  571. cartography/models/crowdstrike/__init__.py +0 -0
  572. cartography/models/crowdstrike/hosts.py +51 -0
  573. cartography/models/cve/cve.py +34 -32
  574. cartography/models/cve/cve_feed.py +6 -6
  575. cartography/models/digitalocean/__init__.py +0 -0
  576. cartography/models/digitalocean/account.py +21 -0
  577. cartography/models/digitalocean/droplet.py +58 -0
  578. cartography/models/digitalocean/project.py +48 -0
  579. cartography/models/duo/api_host.py +3 -3
  580. cartography/models/duo/endpoint.py +43 -41
  581. cartography/models/duo/group.py +14 -14
  582. cartography/models/duo/phone.py +27 -27
  583. cartography/models/duo/token.py +16 -16
  584. cartography/models/duo/user.py +50 -44
  585. cartography/models/duo/web_authn_credential.py +27 -19
  586. cartography/models/entra/__init__.py +0 -0
  587. cartography/models/entra/app_role_assignment.py +115 -0
  588. cartography/models/entra/application.py +49 -0
  589. cartography/models/entra/entra_user_to_aws_sso.py +41 -0
  590. cartography/models/entra/group.py +117 -0
  591. cartography/models/entra/ou.py +48 -0
  592. cartography/models/entra/service_principal.py +104 -0
  593. cartography/models/entra/tenant.py +39 -0
  594. cartography/models/entra/user.py +90 -0
  595. cartography/models/gcp/__init__.py +0 -0
  596. cartography/models/gcp/bigtable/__init__.py +0 -0
  597. cartography/models/gcp/bigtable/app_profile.py +94 -0
  598. cartography/models/gcp/bigtable/backup.py +91 -0
  599. cartography/models/gcp/bigtable/cluster.py +73 -0
  600. cartography/models/gcp/bigtable/instance.py +52 -0
  601. cartography/models/gcp/bigtable/table.py +69 -0
  602. cartography/models/gcp/compute/__init__.py +0 -0
  603. cartography/models/gcp/compute/subnet.py +74 -0
  604. cartography/models/gcp/compute/vpc.py +50 -0
  605. cartography/models/gcp/crm/__init__.py +0 -0
  606. cartography/models/gcp/crm/folders.py +98 -0
  607. cartography/models/gcp/crm/organizations.py +21 -0
  608. cartography/models/gcp/crm/projects.py +100 -0
  609. cartography/models/gcp/dns.py +109 -0
  610. cartography/models/gcp/gke.py +69 -0
  611. cartography/models/gcp/iam.py +73 -0
  612. cartography/models/gcp/permission_relationships.py +61 -0
  613. cartography/models/gcp/policy_bindings.py +93 -0
  614. cartography/models/gcp/storage/__init__.py +0 -0
  615. cartography/models/gcp/storage/bucket.py +119 -0
  616. cartography/models/github/commits.py +63 -0
  617. cartography/models/github/dependencies.py +73 -0
  618. cartography/models/github/manifests.py +49 -0
  619. cartography/models/github/orgs.py +27 -0
  620. cartography/models/github/teams.py +74 -22
  621. cartography/models/github/users.py +149 -0
  622. cartography/models/googleworkspace/__init__.py +0 -0
  623. cartography/models/googleworkspace/device.py +132 -0
  624. cartography/models/googleworkspace/group.py +382 -0
  625. cartography/models/googleworkspace/oauth_app.py +124 -0
  626. cartography/models/googleworkspace/tenant.py +30 -0
  627. cartography/models/googleworkspace/user.py +113 -0
  628. cartography/models/gsuite/__init__.py +0 -0
  629. cartography/models/gsuite/group.py +218 -0
  630. cartography/models/gsuite/tenant.py +29 -0
  631. cartography/models/gsuite/user.py +107 -0
  632. cartography/models/kandji/device.py +22 -17
  633. cartography/models/kandji/tenant.py +6 -4
  634. cartography/models/keycloak/__init__.py +0 -0
  635. cartography/models/keycloak/authenticationexecution.py +160 -0
  636. cartography/models/keycloak/authenticationflow.py +54 -0
  637. cartography/models/keycloak/client.py +179 -0
  638. cartography/models/keycloak/group.py +101 -0
  639. cartography/models/keycloak/identityprovider.py +89 -0
  640. cartography/models/keycloak/organization.py +116 -0
  641. cartography/models/keycloak/organizationdomain.py +73 -0
  642. cartography/models/keycloak/realm.py +173 -0
  643. cartography/models/keycloak/role.py +126 -0
  644. cartography/models/keycloak/scope.py +73 -0
  645. cartography/models/keycloak/user.py +55 -0
  646. cartography/models/kubernetes/__init__.py +0 -0
  647. cartography/models/kubernetes/clusterrolebindings.py +138 -0
  648. cartography/models/kubernetes/clusterroles.py +52 -0
  649. cartography/models/kubernetes/clusters.py +26 -0
  650. cartography/models/kubernetes/containers.py +133 -0
  651. cartography/models/kubernetes/groups.py +107 -0
  652. cartography/models/kubernetes/namespaces.py +51 -0
  653. cartography/models/kubernetes/oidc.py +51 -0
  654. cartography/models/kubernetes/pods.py +80 -0
  655. cartography/models/kubernetes/rolebindings.py +159 -0
  656. cartography/models/kubernetes/roles.py +76 -0
  657. cartography/models/kubernetes/secrets.py +79 -0
  658. cartography/models/kubernetes/serviceaccounts.py +77 -0
  659. cartography/models/kubernetes/services.py +108 -0
  660. cartography/models/kubernetes/users.py +105 -0
  661. cartography/models/lastpass/tenant.py +3 -3
  662. cartography/models/lastpass/user.py +36 -28
  663. cartography/models/ontology/__init__.py +0 -0
  664. cartography/models/ontology/device.py +137 -0
  665. cartography/models/ontology/mapping/__init__.py +76 -0
  666. cartography/models/ontology/mapping/data/__init__.py +0 -0
  667. cartography/models/ontology/mapping/data/apikeys.py +93 -0
  668. cartography/models/ontology/mapping/data/computeinstance.py +95 -0
  669. cartography/models/ontology/mapping/data/containers.py +88 -0
  670. cartography/models/ontology/mapping/data/databases.py +182 -0
  671. cartography/models/ontology/mapping/data/devices.py +194 -0
  672. cartography/models/ontology/mapping/data/thirdpartyapps.py +140 -0
  673. cartography/models/ontology/mapping/data/useraccounts.py +416 -0
  674. cartography/models/ontology/mapping/data/users.py +63 -0
  675. cartography/models/ontology/mapping/specs.py +85 -0
  676. cartography/models/ontology/user.py +51 -0
  677. cartography/models/openai/__init__.py +0 -0
  678. cartography/models/openai/adminapikey.py +94 -0
  679. cartography/models/openai/apikey.py +88 -0
  680. cartography/models/openai/organization.py +17 -0
  681. cartography/models/openai/project.py +89 -0
  682. cartography/models/openai/serviceaccount.py +50 -0
  683. cartography/models/openai/user.py +53 -0
  684. cartography/models/scaleway/__init__.py +0 -0
  685. cartography/models/scaleway/iam/__init__.py +0 -0
  686. cartography/models/scaleway/iam/apikey.py +100 -0
  687. cartography/models/scaleway/iam/application.py +52 -0
  688. cartography/models/scaleway/iam/group.py +95 -0
  689. cartography/models/scaleway/iam/user.py +64 -0
  690. cartography/models/scaleway/instance/__init__.py +0 -0
  691. cartography/models/scaleway/instance/flexibleip.py +52 -0
  692. cartography/models/scaleway/instance/instance.py +120 -0
  693. cartography/models/scaleway/organization.py +19 -0
  694. cartography/models/scaleway/project.py +48 -0
  695. cartography/models/scaleway/storage/__init__.py +0 -0
  696. cartography/models/scaleway/storage/snapshot.py +78 -0
  697. cartography/models/scaleway/storage/volume.py +51 -0
  698. cartography/models/semgrep/dependencies.py +102 -0
  699. cartography/models/semgrep/deployment.py +5 -5
  700. cartography/models/semgrep/findings.py +58 -40
  701. cartography/models/semgrep/locations.py +27 -21
  702. cartography/models/sentinelone/__init__.py +1 -0
  703. cartography/models/sentinelone/account.py +40 -0
  704. cartography/models/sentinelone/agent.py +50 -0
  705. cartography/models/sentinelone/application.py +44 -0
  706. cartography/models/sentinelone/application_version.py +96 -0
  707. cartography/models/sentinelone/cve.py +73 -0
  708. cartography/models/slack/__init__.py +0 -0
  709. cartography/models/slack/channels.py +92 -0
  710. cartography/models/slack/group.py +129 -0
  711. cartography/models/slack/team.py +22 -0
  712. cartography/models/slack/user.py +62 -0
  713. cartography/models/snipeit/__init__.py +0 -0
  714. cartography/models/snipeit/asset.py +92 -0
  715. cartography/models/snipeit/tenant.py +19 -0
  716. cartography/models/snipeit/user.py +60 -0
  717. cartography/models/spacelift/__init__.py +0 -0
  718. cartography/models/spacelift/cloudtrailevent.py +120 -0
  719. cartography/models/spacelift/run.py +162 -0
  720. cartography/models/spacelift/space.py +131 -0
  721. cartography/models/spacelift/spaceliftaccount.py +31 -0
  722. cartography/models/spacelift/spaceliftgitcommit.py +157 -0
  723. cartography/models/spacelift/stack.py +96 -0
  724. cartography/models/spacelift/user.py +63 -0
  725. cartography/models/spacelift/worker.py +97 -0
  726. cartography/models/spacelift/workerpool.py +90 -0
  727. cartography/models/tailscale/__init__.py +0 -0
  728. cartography/models/tailscale/device.py +96 -0
  729. cartography/models/tailscale/group.py +86 -0
  730. cartography/models/tailscale/postureintegration.py +58 -0
  731. cartography/models/tailscale/tag.py +102 -0
  732. cartography/models/tailscale/tailnet.py +29 -0
  733. cartography/models/tailscale/user.py +57 -0
  734. cartography/models/trivy/__init__.py +0 -0
  735. cartography/models/trivy/findings.py +66 -0
  736. cartography/models/trivy/fix.py +66 -0
  737. cartography/models/trivy/package.py +71 -0
  738. cartography/rules/README.md +1 -0
  739. cartography/rules/__init__.py +0 -0
  740. cartography/rules/cli.py +261 -0
  741. cartography/rules/data/__init__.py +0 -0
  742. cartography/rules/data/rules/__init__.py +46 -0
  743. cartography/rules/data/rules/cloud_security_product_deactivated.py +49 -0
  744. cartography/rules/data/rules/compute_instance_exposed.py +51 -0
  745. cartography/rules/data/rules/database_instance_exposed.py +53 -0
  746. cartography/rules/data/rules/delegation_boundary_modifiable.py +90 -0
  747. cartography/rules/data/rules/identity_administration_privileges.py +100 -0
  748. cartography/rules/data/rules/inactive_user_active_accounts.py +48 -0
  749. cartography/rules/data/rules/malicious_npm_dependencies_shai_hulud.py +2222 -0
  750. cartography/rules/data/rules/mfa_missing.py +46 -0
  751. cartography/rules/data/rules/object_storage_public.py +100 -0
  752. cartography/rules/data/rules/policy_administration_privileges.py +104 -0
  753. cartography/rules/data/rules/unmanaged_accounts.py +43 -0
  754. cartography/rules/data/rules/workload_identity_admin_capabilities.py +193 -0
  755. cartography/rules/formatters.py +108 -0
  756. cartography/rules/runners.py +216 -0
  757. cartography/rules/spec/__init__.py +0 -0
  758. cartography/rules/spec/model.py +267 -0
  759. cartography/rules/spec/result.py +38 -0
  760. cartography/stats.py +4 -4
  761. cartography/sync.py +137 -31
  762. cartography/util.py +187 -77
  763. cartography-0.123.0.dist-info/METADATA +230 -0
  764. cartography-0.123.0.dist-info/RECORD +856 -0
  765. {cartography-0.93.0rc1.dist-info → cartography-0.123.0.dist-info}/WHEEL +1 -1
  766. {cartography-0.93.0rc1.dist-info → cartography-0.123.0.dist-info}/entry_points.txt +1 -0
  767. {cartography-0.93.0rc1.dist-info → cartography-0.123.0.dist-info/licenses}/LICENSE +1 -1
  768. cartography/data/jobs/analysis/aws_ec2_iaminstance.json +0 -10
  769. cartography/data/jobs/analysis/aws_ec2_iaminstanceprofile.json +0 -10
  770. cartography/data/jobs/cleanup/aws_apigateway_details.json +0 -10
  771. cartography/data/jobs/cleanup/aws_dns_cleanup.json +0 -65
  772. cartography/data/jobs/cleanup/aws_import_account_access_key_cleanup.json +0 -17
  773. cartography/data/jobs/cleanup/aws_import_apigateway_cleanup.json +0 -45
  774. cartography/data/jobs/cleanup/aws_import_ec2_security_groupinfo_cleanup.json +0 -24
  775. cartography/data/jobs/cleanup/aws_import_groups_cleanup.json +0 -13
  776. cartography/data/jobs/cleanup/aws_import_lambda_cleanup.json +0 -50
  777. cartography/data/jobs/cleanup/aws_import_principals_cleanup.json +0 -30
  778. cartography/data/jobs/cleanup/aws_import_rds_clusters_cleanup.json +0 -23
  779. cartography/data/jobs/cleanup/aws_import_rds_instances_cleanup.json +0 -47
  780. cartography/data/jobs/cleanup/aws_import_rds_snapshots_cleanup.json +0 -23
  781. cartography/data/jobs/cleanup/aws_import_roles_cleanup.json +0 -13
  782. cartography/data/jobs/cleanup/aws_import_secrets_cleanup.json +0 -8
  783. cartography/data/jobs/cleanup/aws_import_snapshots_cleanup.json +0 -30
  784. cartography/data/jobs/cleanup/aws_import_users_cleanup.json +0 -8
  785. cartography/data/jobs/cleanup/aws_import_vpc_cleanup.json +0 -23
  786. cartography/data/jobs/cleanup/aws_import_vpc_peering_cleanup.json +0 -45
  787. cartography/data/jobs/cleanup/aws_kms_details.json +0 -10
  788. cartography/data/jobs/cleanup/azure_cosmosdb_cassandra_keyspace_cleanup.json +0 -25
  789. cartography/data/jobs/cleanup/azure_cosmosdb_cors_details.json +0 -15
  790. cartography/data/jobs/cleanup/azure_cosmosdb_mongodb_database_cleanup.json +0 -25
  791. cartography/data/jobs/cleanup/azure_cosmosdb_sql_database_cleanup.json +0 -25
  792. cartography/data/jobs/cleanup/azure_cosmosdb_table_resources_cleanup.json +0 -15
  793. cartography/data/jobs/cleanup/azure_database_account_cleanup.json +0 -85
  794. cartography/data/jobs/cleanup/azure_import_disks_cleanup.json +0 -15
  795. cartography/data/jobs/cleanup/azure_import_snapshots_cleanup.json +0 -15
  796. cartography/data/jobs/cleanup/azure_import_virtual_machines_cleanup.json +0 -25
  797. cartography/data/jobs/cleanup/azure_sql_server_cleanup.json +0 -125
  798. cartography/data/jobs/cleanup/azure_storage_account_cleanup.json +0 -95
  799. cartography/data/jobs/cleanup/azure_subscriptions_cleanup.json +0 -14
  800. cartography/data/jobs/cleanup/azure_tenant_cleanup.json +0 -9
  801. cartography/data/jobs/cleanup/crxcavator_import_cleanup.json +0 -18
  802. cartography/data/jobs/cleanup/gcp_compute_vpc_subnet_cleanup.json +0 -35
  803. cartography/data/jobs/cleanup/gcp_crm_folder_cleanup.json +0 -23
  804. cartography/data/jobs/cleanup/gcp_crm_organization_cleanup.json +0 -17
  805. cartography/data/jobs/cleanup/gcp_crm_project_cleanup.json +0 -23
  806. cartography/data/jobs/cleanup/gcp_dns_cleanup.json +0 -29
  807. cartography/data/jobs/cleanup/gcp_gke_cluster_cleanup.json +0 -17
  808. cartography/data/jobs/cleanup/gcp_storage_bucket_cleanup.json +0 -29
  809. cartography/data/jobs/cleanup/github_users_cleanup.json +0 -23
  810. cartography/data/jobs/cleanup/gsuite_ingest_groups_cleanup.json +0 -23
  811. cartography/data/jobs/cleanup/gsuite_ingest_users_cleanup.json +0 -11
  812. cartography/data/jobs/cleanup/kubernetes_import_cleanup.json +0 -70
  813. cartography/intel/crxcavator/__init__.py +0 -44
  814. cartography/intel/crxcavator/crxcavator.py +0 -329
  815. cartography/intel/gcp/crm.py +0 -302
  816. cartography/intel/gsuite/api.py +0 -284
  817. cartography/models/aws/ec2/keypairs.py +0 -64
  818. cartography-0.93.0rc1.dist-info/METADATA +0 -55
  819. cartography-0.93.0rc1.dist-info/NOTICE +0 -4
  820. cartography-0.93.0rc1.dist-info/RECORD +0 -341
  821. /cartography/data/jobs/{analysis → scoped_analysis}/aws_s3acl_analysis.json +0 -0
  822. {cartography-0.93.0rc1.dist-info → cartography-0.123.0.dist-info}/top_level.txt +0 -0
cartography/cli.py CHANGED
@@ -8,8 +8,9 @@ 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
  logger = logging.getLogger(__name__)
15
16
 
@@ -22,7 +23,11 @@ class CLI:
22
23
  :param prog: The name of the command line program. This will be displayed in usage and help output.
23
24
  """
24
25
 
25
- 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
+ ):
26
31
  self.sync = sync if sync else cartography.sync.build_default_sync()
27
32
  self.prog = prog
28
33
  self.parser = self._build_parser()
@@ -46,501 +51,928 @@ class CLI:
46
51
  "instances, use auth when communicating with Neo4j, sync data from multiple AWS accounts, and execute "
47
52
  "arbitrary analysis jobs after the conclusion of the sync."
48
53
  ),
49
- epilog='For more documentation please visit: https://github.com/lyft/cartography',
54
+ epilog="For more documentation please visit: https://github.com/cartography-cncf/cartography",
50
55
  )
51
56
  parser.add_argument(
52
- '-v',
53
- '--verbose',
54
- action='store_true',
55
- help='Enable verbose logging for cartography.',
57
+ "-v",
58
+ "--verbose",
59
+ action="store_true",
60
+ help="Enable verbose logging for cartography.",
56
61
  )
57
62
  parser.add_argument(
58
- '-q',
59
- '--quiet',
60
- action='store_true',
61
- 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.",
62
67
  )
63
68
  parser.add_argument(
64
- '--neo4j-uri',
69
+ "--neo4j-uri",
65
70
  type=str,
66
- default='bolt://localhost:7687',
71
+ default="bolt://localhost:7687",
67
72
  help=(
68
- 'A valid Neo4j URI to sync against. See '
69
- 'https://neo4j.com/docs/api/python-driver/current/driver.html#uri for complete documentation on the '
70
- 'structure of a Neo4j URI.'
73
+ "A valid Neo4j URI to sync against. See "
74
+ "https://neo4j.com/docs/browser-manual/current/operations/dbms-connection/#uri-scheme for complete "
75
+ "documentation on the structure of a Neo4j URI."
71
76
  ),
72
77
  )
73
78
  parser.add_argument(
74
- '--neo4j-user',
79
+ "--neo4j-user",
75
80
  type=str,
76
81
  default=None,
77
- help='A username with which to authenticate to Neo4j.',
82
+ help="A username with which to authenticate to Neo4j.",
78
83
  )
79
84
  parser.add_argument(
80
- '--neo4j-password-env-var',
85
+ "--neo4j-password-env-var",
81
86
  type=str,
82
87
  default=None,
83
- 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.",
84
89
  )
85
90
  parser.add_argument(
86
- '--neo4j-password-prompt',
87
- action='store_true',
91
+ "--neo4j-password-prompt",
92
+ action="store_true",
88
93
  help=(
89
- 'Present an interactive prompt for a password with which to authenticate to Neo4j. This parameter '
90
- '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."
91
96
  ),
92
97
  )
93
98
  parser.add_argument(
94
- '--neo4j-max-connection-lifetime',
99
+ "--neo4j-max-connection-lifetime",
95
100
  type=int,
96
101
  default=3600,
97
102
  help=(
98
- 'Time in seconds for the Neo4j driver to consider a TCP connection alive. cartography default = 3600, '
99
- 'which is the same as the Neo4j driver default. See '
100
- 'https://neo4j.com/docs/driver-manual/1.7/client-applications/#driver-config-connection-pool-management'
101
- '.'
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
+ "."
102
107
  ),
103
108
  )
104
109
  parser.add_argument(
105
- '--neo4j-database',
110
+ "--neo4j-database",
106
111
  type=str,
107
112
  default=None,
108
113
  help=(
109
- 'The name of the database in Neo4j to connect to. If not specified, uses the config settings of your '
110
- 'Neo4j database itself to infer which database is set to default. '
111
- '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."
112
117
  ),
113
118
  )
114
119
  parser.add_argument(
115
- '--selected-modules',
120
+ "--selected-modules",
116
121
  type=str,
117
122
  default=None,
118
123
  help=(
119
124
  'Comma-separated list of cartography top-level modules to sync. Example 1: "aws,gcp" to run AWS and GCP'
120
- 'modules. See the full list available in source code at cartography.sync. '
121
- 'If not specified, cartography by default will run all modules available and log warnings when it '
122
- '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. "
123
128
  # TODO remove this mention about the create-indexes module when everything is using auto-indexes.
124
- 'We recommend that you always specify the `create-indexes` module first in this list. '
125
- 'If you specify the `analysis` module, we recommend that you include it as the LAST item of this list, '
126
- '(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)."
127
132
  ),
128
133
  )
129
134
  # TODO add the below parameters to a 'sync' subparser
130
135
  parser.add_argument(
131
- '--update-tag',
136
+ "--update-tag",
132
137
  type=int,
133
138
  default=None,
134
139
  help=(
135
- 'A unique tag to apply to all Neo4j nodes and relationships created or updated during the sync run. '
136
- 'This tag is used by cleanup jobs to identify nodes and relationships that are stale and need to be '
137
- '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."
138
143
  ),
139
144
  )
140
145
  parser.add_argument(
141
- '--aws-sync-all-profiles',
142
- action='store_true',
146
+ "--aws-sync-all-profiles",
147
+ action="store_true",
143
148
  help=(
144
- 'Enable AWS sync for all discovered named profiles. When this parameter is supplied cartography will '
145
- 'discover all configured AWS named profiles (see '
146
- '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 "
147
152
  'job for each profile not named "default". If this parameter is not supplied, cartography will use the '
148
- 'default AWS credentials available in your environment to run the AWS sync once. When using this '
149
- 'parameter it is suggested that you create an AWS config file containing a named profile for each AWS '
150
- 'account you want to sync and use the AWS_CONFIG_FILE environment variable to point to that config '
151
- 'file (see https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html). cartography '
152
- '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."
158
+ ),
159
+ )
160
+ parser.add_argument(
161
+ "--aws-regions",
162
+ type=str,
163
+ default=None,
164
+ help=(
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."
175
+ ),
176
+ )
177
+ parser.add_argument(
178
+ "--aws-best-effort-mode",
179
+ action="store_true",
180
+ help=(
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."
153
183
  ),
154
184
  )
155
185
  parser.add_argument(
156
- '--aws-best-effort-mode',
157
- action='store_true',
186
+ "--aws-cloudtrail-management-events-lookback-hours",
187
+ type=int,
188
+ default=None,
158
189
  help=(
159
- 'Enable AWS sync best effort mode when syncing AWS accounts. This will allow cartography to continue '
160
- 'syncing other accounts and delay raising an exception until the very end.'
190
+ "Number of hours back to retrieve CloudTrail management events from. If not specified, CloudTrail management events will not be retrieved."
161
191
  ),
162
192
  )
163
193
  parser.add_argument(
164
- '--oci-sync-all-profiles',
165
- action='store_true',
194
+ "--oci-sync-all-profiles",
195
+ action="store_true",
166
196
  help=(
167
- 'Enable OCI sync for all discovered named profiles. When this parameter is supplied cartography will '
168
- 'discover all configured OCI named profiles (see '
169
- 'https://docs.oracle.com/en-us/iaas/Content/API/Concepts/sdkconfig.htm) and run the OCI sync '
197
+ "Enable OCI sync for all discovered named profiles. When this parameter is supplied cartography will "
198
+ "discover all configured OCI named profiles (see "
199
+ "https://docs.oracle.com/en-us/iaas/Content/API/Concepts/sdkconfig.htm) and run the OCI sync "
170
200
  'job for each profile not named "DEFAULT". If this parameter is not supplied, cartography will use the '
171
- 'default OCI credentials available in your environment to run the OCI sync once.'
201
+ "default OCI credentials available in your environment to run the OCI sync once."
172
202
  ),
173
203
  )
174
204
  parser.add_argument(
175
- '--azure-sync-all-subscriptions',
176
- action='store_true',
205
+ "--azure-sync-all-subscriptions",
206
+ action="store_true",
177
207
  help=(
178
- 'Enable Azure sync for all discovered subscriptions. When this parameter is supplied cartography will '
179
- 'discover all configured Azure subscriptions.'
208
+ "Enable Azure sync for all discovered subscriptions. When this parameter is supplied cartography will "
209
+ "discover all configured Azure subscriptions."
180
210
  ),
181
211
  )
182
212
  parser.add_argument(
183
- '--azure-sp-auth',
184
- action='store_true',
213
+ "--azure-sp-auth",
214
+ action="store_true",
215
+ help=("Use Service Principal authentication for Azure sync."),
216
+ )
217
+ parser.add_argument(
218
+ "--azure-tenant-id",
219
+ type=str,
220
+ default=None,
221
+ help=("Azure Tenant Id for Service Principal Authentication."),
222
+ )
223
+ parser.add_argument(
224
+ "--azure-client-id",
225
+ type=str,
226
+ default=None,
227
+ help=("Azure Client Id for Service Principal Authentication."),
228
+ )
229
+ parser.add_argument(
230
+ "--azure-client-secret-env-var",
231
+ type=str,
232
+ default=None,
185
233
  help=(
186
- 'Use Service Principal authentication for Azure sync.'
234
+ "The name of environment variable containing Azure Client Secret for Service Principal Authentication."
187
235
  ),
188
236
  )
189
237
  parser.add_argument(
190
- '--azure-tenant-id',
238
+ "--azure-subscription-id",
239
+ type=str,
240
+ help="The Azure Subscription ID to sync.",
241
+ )
242
+ parser.add_argument(
243
+ "--entra-tenant-id",
244
+ type=str,
245
+ default=None,
246
+ help=("Entra Tenant Id for Service Principal Authentication."),
247
+ )
248
+ parser.add_argument(
249
+ "--entra-client-id",
250
+ type=str,
251
+ default=None,
252
+ help=("Entra Client Id for Service Principal Authentication."),
253
+ )
254
+ parser.add_argument(
255
+ "--entra-client-secret-env-var",
191
256
  type=str,
192
257
  default=None,
193
258
  help=(
194
- 'Azure Tenant Id for Service Principal Authentication.'
259
+ "The name of environment variable containing Entra Client Secret for Service Principal Authentication."
195
260
  ),
196
261
  )
197
262
  parser.add_argument(
198
- '--azure-client-id',
263
+ "--aws-requested-syncs",
199
264
  type=str,
200
265
  default=None,
201
266
  help=(
202
- 'Azure Client Id for Service Principal Authentication.'
267
+ 'Comma-separated list of AWS resources to sync. Example 1: "ecr,s3,ec2:instance" for ECR, S3, and all '
268
+ "EC2 instance resources. See the full list available in source code at cartography.intel.aws.resources."
269
+ " If not specified, cartography by default will run all AWS sync modules available."
203
270
  ),
204
271
  )
205
272
  parser.add_argument(
206
- '--azure-client-secret-env-var',
273
+ "--aws-guardduty-severity-threshold",
207
274
  type=str,
208
275
  default=None,
209
276
  help=(
210
- 'The name of environment variable containing Azure Client Secret for Service Principal Authentication.'
277
+ "GuardDuty severity threshold filter. Only findings at or above this severity level will be synced. "
278
+ "Valid values: LOW, MEDIUM, HIGH, CRITICAL. If not specified, all findings (except archived) will be synced. "
279
+ "Example: 'HIGH' will sync only HIGH and CRITICAL findings, filtering out LOW and MEDIUM severity findings."
280
+ ),
281
+ )
282
+ parser.add_argument(
283
+ "--experimental-aws-inspector-batch",
284
+ type=int,
285
+ default=1000,
286
+ help=(
287
+ "EXPERIMENTAL: This feature is experimental and may be removed in the future. "
288
+ "Batch size for AWS Inspector findings sync. Controls how many findings are fetched, processed and cleaned up at a time. "
289
+ "Default is 1000. Increase this value if you have a large number of findings and want to reduce API calls, "
290
+ "or decrease it if you're experiencing memory issues."
211
291
  ),
212
292
  )
213
293
  parser.add_argument(
214
- '--aws-requested-syncs',
294
+ "--analysis-job-directory",
215
295
  type=str,
216
296
  default=None,
217
297
  help=(
218
- 'Comma-separated list of AWS resources to sync. Example 1: "ecr,s3,ec2:instance" for ECR, S3, and all '
219
- 'EC2 instance resources. See the full list available in source code at cartography.intel.aws.resources.'
220
- ' If not specified, cartography by default will run all AWS sync modules available.'
298
+ "A path to a directory containing analysis jobs to run at the conclusion of the sync. cartography will "
299
+ "discover all JSON files in the given directory (and its subdirectories) and pass them to the GraphJob "
300
+ "API to execute against the graph. This allows you to apply data transformation and augmentation at "
301
+ "the end of a sync run without writing code. cartography does not guarantee the order in which the "
302
+ "jobs are executed."
221
303
  ),
222
304
  )
223
305
  parser.add_argument(
224
- '--crxcavator-api-base-uri',
306
+ "--okta-org-id",
225
307
  type=str,
226
- default='https://api.crxcavator.io/v1',
308
+ default=None,
227
309
  help=(
228
- 'Base URI for the CRXcavator API. Defaults to public API endpoint.'
310
+ "Okta organizational id to sync. Required if you are using the Okta intel module. Ignored otherwise."
229
311
  ),
230
312
  )
231
313
  parser.add_argument(
232
- '--crxcavator-api-key-env-var',
314
+ "--okta-api-key-env-var",
233
315
  type=str,
234
316
  default=None,
235
317
  help=(
236
- 'The name of an environment variable containing a key with which to auth to the CRXcavator API. '
237
- 'Required if you are using the CRXcavator intel module. Ignored otherwise.'
318
+ "The name of an environment variable containing a key with which to auth to the Okta API."
319
+ "Required if you are using the Okta intel module. Ignored otherwise."
238
320
  ),
239
321
  )
240
322
  parser.add_argument(
241
- '--analysis-job-directory',
323
+ "--okta-saml-role-regex",
324
+ type=str,
325
+ default=r"^aws\#\S+\#(?{{role}}[\w\-]+)\#(?{{accountid}}\d+)$",
326
+ help=(
327
+ "The regex used to map Okta groups to AWS roles when using okta as a SAML provider."
328
+ "The regex is the one entered in Step 5: Enabling Group Based Role Mapping in Okta"
329
+ "https://saml-doc.okta.com/SAML_Docs/How-to-Configure-SAML-2.0-for-Amazon-Web-Service#c-step5"
330
+ "The regex must contain the {{role}} and {{accountid}} tags"
331
+ ),
332
+ )
333
+ parser.add_argument(
334
+ "--github-config-env-var",
242
335
  type=str,
243
336
  default=None,
244
337
  help=(
245
- 'A path to a directory containing analysis jobs to run at the conclusion of the sync. cartography will '
246
- 'discover all JSON files in the given directory (and its subdirectories) and pass them to the GraphJob '
247
- 'API to execute against the graph. This allows you to apply data transformation and augmentation at '
248
- 'the end of a sync run without writing code. cartography does not guarantee the order in which the '
249
- 'jobs are executed.'
338
+ "The name of an environment variable containing a Base64 encoded GitHub config object."
339
+ "Required if you are using the GitHub intel module. Ignored otherwise."
340
+ ),
341
+ )
342
+ parser.add_argument(
343
+ "--github-commit-lookback-days",
344
+ type=int,
345
+ default=30,
346
+ help=(
347
+ "Number of days to look back for tracking GitHub users committing to repositories. Defaults to 30 days."
250
348
  ),
251
349
  )
252
350
  parser.add_argument(
253
- '--okta-org-id',
351
+ "--digitalocean-token-env-var",
254
352
  type=str,
255
353
  default=None,
256
354
  help=(
257
- 'Okta organizational id to sync. Required if you are using the Okta intel module. Ignored otherwise.'
355
+ "The name of an environment variable containing a DigitalOcean access token."
356
+ "Required if you are using the DigitalOcean intel module. Ignored otherwise."
357
+ ),
358
+ )
359
+ parser.add_argument(
360
+ "--permission-relationships-file",
361
+ type=str,
362
+ default="cartography/data/permission_relationships.yaml",
363
+ help=(
364
+ "The path to the permission relationships mapping file."
365
+ "If omitted the default permission relationships will be created"
366
+ ),
367
+ )
368
+ parser.add_argument(
369
+ "--azure-permission-relationships-file",
370
+ type=str,
371
+ default="cartography/data/azure_permission_relationships.yaml",
372
+ help=(
373
+ "The path to the Azure permission relationships mapping file."
374
+ "If omitted the default Azure permission relationships will be created"
258
375
  ),
259
376
  )
260
377
  parser.add_argument(
261
- '--okta-api-key-env-var',
378
+ "--gcp-permission-relationships-file",
379
+ type=str,
380
+ default="cartography/data/gcp_permission_relationships.yaml",
381
+ help=(
382
+ "The path to the GCP permission relationships mapping file. "
383
+ "If omitted the default GCP permission relationships will be used"
384
+ ),
385
+ )
386
+ parser.add_argument(
387
+ "--jamf-base-uri",
262
388
  type=str,
263
389
  default=None,
264
390
  help=(
265
- 'The name of an environment variable containing a key with which to auth to the Okta API.'
266
- 'Required if you are using the Okta intel module. Ignored otherwise.'
391
+ "Your Jamf base URI, e.g. https://hostname.com/JSSResource."
392
+ "Required if you are using the Jamf intel module. Ignored otherwise."
267
393
  ),
268
394
  )
269
395
  parser.add_argument(
270
- '--okta-saml-role-regex',
396
+ "--jamf-user",
271
397
  type=str,
272
- default=r"^aws\#\S+\#(?{{role}}[\w\-]+)\#(?{{accountid}}\d+)$",
398
+ default=None,
399
+ help="A username with which to authenticate to Jamf.",
400
+ )
401
+ parser.add_argument(
402
+ "--jamf-password-env-var",
403
+ type=str,
404
+ default=None,
405
+ help="The name of an environment variable containing a password with which to authenticate to Jamf.",
406
+ )
407
+ parser.add_argument(
408
+ "--kandji-base-uri",
409
+ type=str,
410
+ default=None,
273
411
  help=(
274
- 'The regex used to map Okta groups to AWS roles when using okta as a SAML provider.'
275
- 'The regex is the one entered in Step 5: Enabling Group Based Role Mapping in Okta'
276
- 'https://saml-doc.okta.com/SAML_Docs/How-to-Configure-SAML-2.0-for-Amazon-Web-Service#c-step5'
277
- 'The regex must contain the {{role}} and {{accountid}} tags'
412
+ "Your Kandji base URI, e.g. https://company.api.kandji.io."
413
+ "Required if you are using the Kandji intel module. Ignored otherwise."
278
414
  ),
279
415
  )
280
416
  parser.add_argument(
281
- '--github-config-env-var',
417
+ "--kandji-tenant-id",
282
418
  type=str,
283
419
  default=None,
284
420
  help=(
285
- 'The name of an environment variable containing a Base64 encoded GitHub config object.'
286
- 'Required if you are using the GitHub intel module. Ignored otherwise.'
421
+ "Your Kandji tenant id e.g. company."
422
+ "Required using the Kandji intel module. Ignored otherwise."
287
423
  ),
288
424
  )
289
425
  parser.add_argument(
290
- '--digitalocean-token-env-var',
426
+ "--kandji-token-env-var",
291
427
  type=str,
292
428
  default=None,
429
+ help="The name of an environment variable containing token with which to authenticate to Kandji.",
430
+ )
431
+ parser.add_argument(
432
+ "--k8s-kubeconfig",
433
+ default=None,
434
+ type=str,
293
435
  help=(
294
- 'The name of an environment variable containing a DigitalOcean access token.'
295
- 'Required if you are using the DigitalOcean intel module. Ignored otherwise.'
436
+ "The path to kubeconfig file specifying context to access K8s cluster(s)."
296
437
  ),
297
438
  )
298
439
  parser.add_argument(
299
- '--permission-relationships-file',
440
+ "--managed-kubernetes",
441
+ default=None,
300
442
  type=str,
301
- default="cartography/data/permission_relationships.yaml",
443
+ help=("Type of managed Kubernetes service (e.g., 'eks'). Optional."),
444
+ )
445
+ parser.add_argument(
446
+ "--nist-cve-url",
447
+ type=str,
448
+ default="https://services.nvd.nist.gov/rest/json/cves/2.0/",
449
+ help=(
450
+ "The base url for the NIST CVE data. Default = https://services.nvd.nist.gov/rest/json/cves/2.0/"
451
+ ),
452
+ )
453
+ parser.add_argument(
454
+ "--cve-enabled",
455
+ action="store_true",
456
+ help=("If set, CVE data will be synced from NIST."),
457
+ )
458
+ parser.add_argument(
459
+ "--cve-api-key-env-var",
460
+ type=str,
461
+ default=None,
462
+ help=("If set, uses the provided NIST NVD API v2.0 key."),
463
+ )
464
+ parser.add_argument(
465
+ "--statsd-enabled",
466
+ action="store_true",
467
+ help=(
468
+ "If set, enables sending metrics using statsd to a server of your choice."
469
+ ),
470
+ )
471
+ parser.add_argument(
472
+ "--statsd-prefix",
473
+ type=str,
474
+ default="",
475
+ help=(
476
+ "The string to prefix statsd metrics with. Only used if --statsd-enabled is on. Default = empty string."
477
+ ),
478
+ )
479
+ parser.add_argument(
480
+ "--statsd-host",
481
+ type=str,
482
+ default="127.0.0.1",
483
+ help=(
484
+ "The IP address of your statsd server. Only used if --statsd-enabled is on. Default = 127.0.0.1."
485
+ ),
486
+ )
487
+ parser.add_argument(
488
+ "--statsd-port",
489
+ type=int,
490
+ default=8125,
302
491
  help=(
303
- 'The path to the permission relationships mapping file.'
304
- 'If omitted the default permission relationships will be created'
492
+ "The port of your statsd server. Only used if --statsd-enabled is on. Default = UDP 8125."
305
493
  ),
306
494
  )
307
495
  parser.add_argument(
308
- '--jamf-base-uri',
496
+ "--pagerduty-api-key-env-var",
309
497
  type=str,
310
498
  default=None,
311
499
  help=(
312
- 'Your Jamf base URI, e.g. https://hostname.com/JSSResource.'
313
- 'Required if you are using the Jamf intel module. Ignored otherwise.'
500
+ "The name of environment variable containing the pagerduty API key for authentication."
314
501
  ),
315
502
  )
316
503
  parser.add_argument(
317
- '--jamf-user',
504
+ "--pagerduty-request-timeout",
505
+ type=int,
506
+ default=None,
507
+ help=("Seconds to timeout for pagerduty API sessions."),
508
+ )
509
+ parser.add_argument(
510
+ "--crowdstrike-client-id-env-var",
318
511
  type=str,
319
512
  default=None,
320
- help='A username with which to authenticate to Jamf.',
513
+ help=(
514
+ "The name of environment variable containing the crowdstrike client id for authentication."
515
+ ),
321
516
  )
322
517
  parser.add_argument(
323
- '--jamf-password-env-var',
518
+ "--crowdstrike-client-secret-env-var",
324
519
  type=str,
325
520
  default=None,
326
- help='The name of an environment variable containing a password with which to authenticate to Jamf.',
521
+ help=(
522
+ "The name of environment variable containing the crowdstrike secret key for authentication."
523
+ ),
327
524
  )
328
525
  parser.add_argument(
329
- '--kandji-base-uri',
526
+ "--crowdstrike-api-url",
330
527
  type=str,
331
528
  default=None,
332
529
  help=(
333
- 'Your Kandji base URI, e.g. https://company.api.kandji.io.'
334
- 'Required if you are using the Kandji intel module. Ignored otherwise.'
530
+ "The crowdstrike URL, if using self-hosted. Defaults to the public crowdstrike API URL otherwise."
531
+ ),
532
+ )
533
+ parser.add_argument(
534
+ "--gsuite-auth-method",
535
+ type=str,
536
+ default="delegated",
537
+ choices=["delegated", "oauth", "default"],
538
+ help=(
539
+ 'GSuite authentication method. Can be "delegated" for service account or "oauth" for OAuth. '
540
+ '"Default" best if using gcloud CLI.'
541
+ ),
542
+ )
543
+ parser.add_argument(
544
+ "--gsuite-tokens-env-var",
545
+ type=str,
546
+ default="GSUITE_GOOGLE_APPLICATION_CREDENTIALS",
547
+ help=(
548
+ "The name of environment variable containing secrets for GSuite authentication."
549
+ ),
550
+ )
551
+ parser.add_argument(
552
+ "--googleworkspace-auth-method",
553
+ type=str,
554
+ default="delegated",
555
+ choices=["delegated", "oauth", "default"],
556
+ help=(
557
+ 'Google Workspace authentication method. Can be "delegated" for service account or "oauth" for OAuth. '
558
+ '"Default" best if using gcloud CLI.'
335
559
  ),
336
560
  )
337
561
  parser.add_argument(
338
- '--kandji-tenant-id',
562
+ "--googleworkspace-tokens-env-var",
563
+ type=str,
564
+ default="GOOGLEWORKSPACE_GOOGLE_APPLICATION_CREDENTIALS",
565
+ help=(
566
+ "The name of environment variable containing secrets for Google Workspace authentication."
567
+ ),
568
+ )
569
+ parser.add_argument(
570
+ "--lastpass-cid-env-var",
339
571
  type=str,
340
572
  default=None,
341
573
  help=(
342
- 'Your Kandji tenant id e.g. company.'
343
- 'Required using the Kandji intel module. Ignored otherwise.'
574
+ "The name of environment variable containing the Lastpass CID for authentication."
344
575
  ),
345
576
  )
346
577
  parser.add_argument(
347
- '--kandji-token-env-var',
578
+ "--lastpass-provhash-env-var",
348
579
  type=str,
349
580
  default=None,
350
- help='The name of an environment variable containing token with which to authenticate to Kandji.',
581
+ help=(
582
+ "The name of environment variable containing the Lastpass provhash for authentication."
583
+ ),
351
584
  )
352
585
  parser.add_argument(
353
- '--k8s-kubeconfig',
586
+ "--bigfix-username",
587
+ type=str,
354
588
  default=None,
589
+ help=("The BigFix username for authentication."),
590
+ )
591
+ parser.add_argument(
592
+ "--bigfix-password-env-var",
355
593
  type=str,
594
+ default=None,
356
595
  help=(
357
- 'The path to kubeconfig file specifying context to access K8s cluster(s).'
596
+ "The name of environment variable containing the BigFix password for authentication."
358
597
  ),
359
598
  )
360
599
  parser.add_argument(
361
- '--nist-cve-url',
600
+ "--bigfix-root-url",
601
+ type=str,
602
+ default=None,
603
+ help=("The BigFix Root URL, a.k.a the BigFix API URL"),
604
+ )
605
+ parser.add_argument(
606
+ "--duo-api-key-env-var",
607
+ type=str,
608
+ default=None,
609
+ help=("The name of environment variable containing the Duo api key"),
610
+ )
611
+ parser.add_argument(
612
+ "--duo-api-secret-env-var",
613
+ type=str,
614
+ default=None,
615
+ help=("The name of environment variable containing the Duo api secret"),
616
+ )
617
+ parser.add_argument(
618
+ "--duo-api-hostname",
619
+ type=str,
620
+ default=None,
621
+ help=("The Duo api hostname"),
622
+ )
623
+ parser.add_argument(
624
+ "--semgrep-app-token-env-var",
362
625
  type=str,
363
- default='https://services.nvd.nist.gov/rest/json/cves/2.0/',
626
+ default=None,
364
627
  help=(
365
- 'The base url for the NIST CVE data. Default = https://services.nvd.nist.gov/rest/json/cves/2.0/'
628
+ "The name of environment variable containing the Semgrep app token key. "
629
+ "Required if you are using the Semgrep intel module. Ignored otherwise."
366
630
  ),
367
631
  )
368
632
  parser.add_argument(
369
- '--cve-enabled',
370
- action='store_true',
633
+ "--semgrep-dependency-ecosystems",
634
+ type=str,
635
+ default=None,
371
636
  help=(
372
- 'If set, CVE data will be synced from NIST.'
637
+ "Comma-separated list of language ecosystems for which dependencies will be retrieved from Semgrep. "
638
+ 'For example, a value of "gomod,npm" will retrieve Go and NPM dependencies. '
639
+ "See the full list of supported ecosystems in source code at cartography.intel.semgrep.dependencies. "
640
+ "Required if you are using the Semgrep dependencies intel module. Ignored otherwise."
373
641
  ),
374
642
  )
375
643
  parser.add_argument(
376
- '--cve-api-key-env-var',
644
+ "--snipeit-base-uri",
377
645
  type=str,
378
646
  default=None,
379
647
  help=(
380
- 'If set, uses the provided NIST NVD API v2.0 key.'
648
+ "Your SnipeIT base URI. "
649
+ "Required if you are using the SnipeIT intel module. Ignored otherwise."
381
650
  ),
382
651
  )
383
652
  parser.add_argument(
384
- '--statsd-enabled',
385
- action='store_true',
653
+ "--snipeit-token-env-var",
654
+ type=str,
655
+ default=None,
656
+ help="The name of an environment variable containing token with which to authenticate to SnipeIT.",
657
+ )
658
+ parser.add_argument(
659
+ "--snipeit-tenant-id",
660
+ type=str,
661
+ default=None,
662
+ help="An ID for the SnipeIT tenant.",
663
+ )
664
+ parser.add_argument(
665
+ "--cloudflare-token-env-var",
666
+ type=str,
667
+ default=None,
668
+ help="The name of an environment variable containing ApiKey with which to authenticate to Cloudflare.",
669
+ )
670
+ parser.add_argument(
671
+ "--tailscale-token-env-var",
672
+ type=str,
673
+ default=None,
386
674
  help=(
387
- 'If set, enables sending metrics using statsd to a server of your choice.'
675
+ "The name of an environment variable containing a Tailscale API token. "
676
+ "Required if you are using the Tailscale intel module. Ignored otherwise."
388
677
  ),
389
678
  )
390
679
  parser.add_argument(
391
- '--statsd-prefix',
680
+ "--tailscale-org",
392
681
  type=str,
393
- default='',
682
+ default=None,
394
683
  help=(
395
- 'The string to prefix statsd metrics with. Only used if --statsd-enabled is on. Default = empty string.'
684
+ "The name of the Tailscale organization to sync. "
685
+ "Required if you are using the Tailscale intel module. Ignored otherwise."
396
686
  ),
397
687
  )
398
688
  parser.add_argument(
399
- '--statsd-host',
689
+ "--tailscale-base-url",
400
690
  type=str,
401
- default='127.0.0.1',
691
+ default="https://api.tailscale.com/api/v2",
402
692
  help=(
403
- 'The IP address of your statsd server. Only used if --statsd-enabled is on. Default = 127.0.0.1.'
693
+ "The base URL for the Tailscale API. "
694
+ "Required if you are using the Tailscale intel module. Ignored otherwise."
404
695
  ),
405
696
  )
406
697
  parser.add_argument(
407
- '--statsd-port',
408
- type=int,
409
- default=8125,
698
+ "--openai-apikey-env-var",
699
+ type=str,
700
+ default=None,
410
701
  help=(
411
- 'The port of your statsd server. Only used if --statsd-enabled is on. Default = UDP 8125.'
702
+ "The name of an environment variable containing a OpenAI API Key. "
703
+ "Required if you are using the OpenAI intel module. Ignored otherwise."
412
704
  ),
413
705
  )
414
706
  parser.add_argument(
415
- '--pagerduty-api-key-env-var',
707
+ "--openai-org-id",
416
708
  type=str,
417
709
  default=None,
418
710
  help=(
419
- 'The name of environment variable containing the pagerduty API key for authentication.'
711
+ "The ID of the OpenAI organization to sync. "
712
+ "Required if you are using the OpenAI intel module. Ignored otherwise."
420
713
  ),
421
714
  )
422
715
  parser.add_argument(
423
- '--pagerduty-request-timeout',
424
- type=int,
716
+ "--anthropic-apikey-env-var",
717
+ type=str,
425
718
  default=None,
426
719
  help=(
427
- 'Seconds to timeout for pagerduty API sessions.'
720
+ "The name of an environment variable containing an Anthropic API Key. "
721
+ "Required if you are using the Anthropic intel module. Ignored otherwise."
428
722
  ),
429
723
  )
430
724
  parser.add_argument(
431
- '--crowdstrike-client-id-env-var',
725
+ "--airbyte-client-id",
432
726
  type=str,
433
727
  default=None,
434
728
  help=(
435
- 'The name of environment variable containing the crowdstrike client id for authentication.'
729
+ "The Airbyte client ID to use for authentication. "
730
+ "Required if you are using the Airbyte intel module. Ignored otherwise."
436
731
  ),
437
732
  )
438
733
  parser.add_argument(
439
- '--crowdstrike-client-secret-env-var',
734
+ "--airbyte-client-secret-env-var",
440
735
  type=str,
441
736
  default=None,
442
737
  help=(
443
- 'The name of environment variable containing the crowdstrike secret key for authentication.'
738
+ "The name of an environment variable containing the Airbyte client secret for authentication. "
739
+ "Required if you are using the Airbyte intel module. Ignored otherwise."
444
740
  ),
445
741
  )
446
742
  parser.add_argument(
447
- '--crowdstrike-api-url',
743
+ "--airbyte-api-url",
744
+ type=str,
745
+ default="https://api.airbyte.com/v1",
746
+ help=(
747
+ "The base URL for the Airbyte API (default is the public Airbyte Cloud API). "
748
+ "Required if you are using the Airbyte intel module. Ignored otherwise."
749
+ ),
750
+ )
751
+ parser.add_argument(
752
+ "--trivy-s3-bucket",
753
+ type=str,
754
+ default=None,
755
+ help=(
756
+ "The S3 bucket name containing Trivy scan results. "
757
+ "Required if you are using the Trivy module. Ignored otherwise."
758
+ ),
759
+ )
760
+ parser.add_argument(
761
+ "--trivy-s3-prefix",
762
+ type=str,
763
+ default=None,
764
+ help=(
765
+ "The S3 prefix path containing Trivy scan results. "
766
+ "Required if you are using the Trivy module. Ignored otherwise."
767
+ ),
768
+ )
769
+ parser.add_argument(
770
+ "--ontology-users-source",
448
771
  type=str,
449
772
  default=None,
450
773
  help=(
451
- 'The crowdstrike URL, if using self-hosted. Defaults to the public crowdstrike API URL otherwise.'
774
+ "Comma-separated list of sources of truth for user data in the ontology. "
775
+ "'User' nodes will only be created for users that exist in one of the sources. "
776
+ "Required if you are using the ontology module. Ignored otherwise."
452
777
  ),
453
778
  )
454
779
  parser.add_argument(
455
- '--gsuite-auth-method',
780
+ "--ontology-devices-source",
456
781
  type=str,
457
- default='delegated',
458
- choices=['delegated', 'oauth'],
782
+ default=None,
459
783
  help=(
460
- 'The method used by GSuite to authenticate. delegated is the legacy one.'
784
+ "Comma-separated list of sources of truth for client computer data in the ontology. "
785
+ "'Device' nodes will only be created for groups that exist in one of the sources. "
786
+ "Required if you are using the ontology module. Ignored otherwise."
461
787
  ),
462
788
  )
463
789
  parser.add_argument(
464
- '--gsuite-tokens-env-var',
790
+ "--trivy-results-dir",
465
791
  type=str,
466
- default='GSUITE_GOOGLE_APPLICATION_CREDENTIALS',
792
+ default=None,
467
793
  help=(
468
- 'The name of environment variable containing secrets for GSuite authentication.'
794
+ "Path to a directory containing Trivy JSON results on disk. "
795
+ "Required if you are using the Trivy module with local results."
469
796
  ),
470
797
  )
471
798
  parser.add_argument(
472
- '--lastpass-cid-env-var',
799
+ "--scaleway-org",
473
800
  type=str,
474
801
  default=None,
475
802
  help=(
476
- 'The name of environment variable containing the Lastpass CID for authentication.'
803
+ "The Scaleway organization ID to sync. "
804
+ "Required if you are using the Scaleway intel module. Ignored otherwise."
477
805
  ),
478
806
  )
479
807
  parser.add_argument(
480
- '--lastpass-provhash-env-var',
808
+ "--scaleway-access-key",
481
809
  type=str,
482
810
  default=None,
483
811
  help=(
484
- 'The name of environment variable containing the Lastpass provhash for authentication.'
812
+ "The Scaleway access key to use for authentication. "
813
+ "Required if you are using the Scaleway intel module. Ignored otherwise."
485
814
  ),
486
815
  )
487
816
  parser.add_argument(
488
- '--bigfix-username',
817
+ "--scaleway-secret-key-env-var",
489
818
  type=str,
490
819
  default=None,
491
820
  help=(
492
- 'The BigFix username for authentication.'
821
+ "The name of an environment variable containing the Scaleway secret key for authentication. "
822
+ "Required if you are using the Scaleway intel module. Ignored otherwise."
493
823
  ),
494
824
  )
495
825
  parser.add_argument(
496
- '--bigfix-password-env-var',
826
+ "--sentinelone-account-ids",
497
827
  type=str,
498
828
  default=None,
499
829
  help=(
500
- 'The name of environment variable containing the BigFix password for authentication.'
830
+ "Comma-separated list of SentinelOne account IDs to sync. "
831
+ "If not specified, all accessible accounts will be synced."
501
832
  ),
502
833
  )
503
834
  parser.add_argument(
504
- '--bigfix-root-url',
835
+ "--sentinelone-api-url",
505
836
  type=str,
506
837
  default=None,
507
838
  help=(
508
- 'The BigFix Root URL, a.k.a the BigFix API URL'
839
+ "SentinelOne API URL. Required if you are using the SentinelOne intel module. Ignored otherwise."
840
+ ),
841
+ )
842
+ parser.add_argument(
843
+ "--sentinelone-api-token-env-var",
844
+ type=str,
845
+ default="SENTINELONE_API_TOKEN",
846
+ help=(
847
+ "The name of an environment variable containing the SentinelOne API token. "
848
+ "Required if you are using the SentinelOne intel module. Ignored otherwise."
509
849
  ),
510
850
  )
511
851
  parser.add_argument(
512
- '--duo-api-key-env-var',
852
+ "--keycloak-client-id",
513
853
  type=str,
514
854
  default=None,
515
855
  help=(
516
- 'The name of environment variable containing the Duo api key'
856
+ "The Keycloak client ID to sync. "
857
+ "Required if you are using the Keycloak intel module. Ignored otherwise."
517
858
  ),
518
859
  )
519
860
  parser.add_argument(
520
- '--duo-api-secret-env-var',
861
+ "--keycloak-client-secret-env-var",
862
+ type=str,
863
+ default="KEYCLOAK_CLIENT_SECRET",
864
+ help=(
865
+ "The name of an environment variable containing the Keycloak client secret. "
866
+ "Required if you are using the Keycloak intel module. Ignored otherwise."
867
+ ),
868
+ )
869
+ parser.add_argument(
870
+ "--keycloak-url",
871
+ type=str,
872
+ help=(
873
+ "The base URL for the Keycloak instance. "
874
+ "Required if you are using the Keycloak intel module. Ignored otherwise. "
875
+ ),
876
+ )
877
+ parser.add_argument(
878
+ "--keycloak-realm",
879
+ type=str,
880
+ default="master",
881
+ help=(
882
+ "The Keycloak realm used for authentication (note: all available realms will be synced). "
883
+ "Should be `master` (default value) in most of the cases. "
884
+ "Required if you are using the Keycloak intel module. Ignored otherwise. "
885
+ ),
886
+ )
887
+ parser.add_argument(
888
+ "--slack-token-env-var",
521
889
  type=str,
522
890
  default=None,
523
891
  help=(
524
- 'The name of environment variable containing the Duo api secret'
892
+ "The name of environment variable containing the Slack Token. "
893
+ "Required if you are using the Slack intel module. Ignored otherwise."
525
894
  ),
526
895
  )
527
896
  parser.add_argument(
528
- '--duo-api-hostname',
897
+ "--slack-teams",
529
898
  type=str,
530
899
  default=None,
531
900
  help=(
532
- 'The Duo api hostname'
901
+ "The Slack Team ID to sync, comma separated. If not provided, all accessible teams will be synced. "
533
902
  ),
534
903
  )
535
904
  parser.add_argument(
536
- '--semgrep-app-token-env-var',
905
+ "--slack-channels-memberships",
906
+ action="store_true",
907
+ help=("Pull memberships for Slack Channels (can be time consuming)."),
908
+ )
909
+ parser.add_argument(
910
+ "--spacelift-api-endpoint",
537
911
  type=str,
538
912
  default=None,
539
913
  help=(
540
- 'The name of environment variable containing the Semgrep app token key. '
541
- 'Required if you are using the Semgrep intel module. Ignored otherwise.'
914
+ "Spacelift GraphQL API endpoint (e.g., https://yourorg.app.spacelift.io/graphql). "
915
+ "Required if you are using the Spacelift intel module. Ignored otherwise."
916
+ ),
917
+ )
918
+ parser.add_argument(
919
+ "--spacelift-api-token-env-var",
920
+ type=str,
921
+ default="SPACELIFT_API_TOKEN",
922
+ help=(
923
+ "The name of an environment variable containing the Spacelift API token. "
924
+ "Alternative to using API key ID/secret. Ignored if API key credentials are provided."
542
925
  ),
543
926
  )
927
+ parser.add_argument(
928
+ "--spacelift-api-key-id-env-var",
929
+ type=str,
930
+ default="SPACELIFT_API_KEY_ID",
931
+ help=(
932
+ "The name of an environment variable containing the Spacelift API key ID. "
933
+ "Use with --spacelift-api-key-secret-env-var for automatic token exchange. "
934
+ "Alternative to providing a pre-generated token."
935
+ ),
936
+ )
937
+ parser.add_argument(
938
+ "--spacelift-api-key-secret-env-var",
939
+ type=str,
940
+ default="SPACELIFT_API_KEY_SECRET",
941
+ help=(
942
+ "The name of an environment variable containing the Spacelift API key secret. "
943
+ "Use with --spacelift-api-key-id-env-var for automatic token exchange. "
944
+ "Alternative to providing a pre-generated token."
945
+ ),
946
+ )
947
+ parser.add_argument(
948
+ "--spacelift-ec2-ownership-aws-profile",
949
+ type=str,
950
+ default=None,
951
+ help=(
952
+ "AWS profile name to use for fetching EC2 ownership data from S3. "
953
+ "Optional. If not provided, uses default AWS credentials. "
954
+ ),
955
+ )
956
+ parser.add_argument(
957
+ "--spacelift-ec2-ownership-s3-bucket",
958
+ type=str,
959
+ default=None,
960
+ help=(
961
+ "S3 bucket name containing CloudTrail data for EC2 ownership relationships. "
962
+ "Required for EC2 ownership sync (along with --spacelift-ec2-ownership-s3-prefix)."
963
+ ),
964
+ )
965
+ parser.add_argument(
966
+ "--spacelift-ec2-ownership-s3-prefix",
967
+ type=str,
968
+ default=None,
969
+ help=(
970
+ "S3 prefix for CloudTrail data for EC2 ownership relationships. "
971
+ "All JSON files under this prefix will be processed. "
972
+ "Required for EC2 ownership sync (along with --spacelift-ec2-ownership-s3-bucket)."
973
+ ),
974
+ )
975
+
544
976
  return parser
545
977
 
546
978
  def main(self, argv: str) -> int:
@@ -554,17 +986,20 @@ class CLI:
554
986
  config: argparse.Namespace = self.parser.parse_args(argv)
555
987
  # Logging config
556
988
  if config.verbose:
557
- logging.getLogger('cartography').setLevel(logging.DEBUG)
989
+ logging.getLogger("cartography").setLevel(logging.DEBUG)
558
990
  elif config.quiet:
559
- logging.getLogger('cartography').setLevel(logging.WARNING)
991
+ logging.getLogger("cartography").setLevel(logging.WARNING)
560
992
  else:
561
- logging.getLogger('cartography').setLevel(logging.INFO)
993
+ logging.getLogger("cartography").setLevel(logging.INFO)
562
994
  logger.debug("Launching cartography with CLI configuration: %r", vars(config))
563
995
  # Neo4j config
564
996
  if config.neo4j_user:
565
997
  config.neo4j_password = None
566
998
  if config.neo4j_password_prompt:
567
- logger.info("Reading password for Neo4j user '%s' interactively.", config.neo4j_user)
999
+ logger.info(
1000
+ "Reading password for Neo4j user '%s' interactively.",
1001
+ config.neo4j_user,
1002
+ )
568
1003
  config.neo4j_password = getpass.getpass()
569
1004
  elif config.neo4j_password_env_var:
570
1005
  logger.debug(
@@ -574,7 +1009,9 @@ class CLI:
574
1009
  )
575
1010
  config.neo4j_password = os.environ.get(config.neo4j_password_env_var)
576
1011
  if not config.neo4j_password:
577
- logger.warning("Neo4j username was provided but a password could not be found.")
1012
+ logger.warning(
1013
+ "Neo4j username was provided but a password could not be found.",
1014
+ )
578
1015
  else:
579
1016
  config.neo4j_password = None
580
1017
 
@@ -587,41 +1024,65 @@ class CLI:
587
1024
  # No need to store the returned value; we're using this for input validation.
588
1025
  parse_and_validate_aws_requested_syncs(config.aws_requested_syncs)
589
1026
 
1027
+ # AWS regions
1028
+ if config.aws_regions:
1029
+ # No need to store the returned value; we're using this for input validation.
1030
+ parse_and_validate_aws_regions(config.aws_regions)
1031
+
590
1032
  # Azure config
591
1033
  if config.azure_sp_auth and config.azure_client_secret_env_var:
592
1034
  logger.debug(
593
1035
  "Reading Client Secret for Azure Service Principal Authentication from environment variable %s",
594
1036
  config.azure_client_secret_env_var,
595
1037
  )
596
- config.azure_client_secret = os.environ.get(config.azure_client_secret_env_var)
1038
+ config.azure_client_secret = os.environ.get(
1039
+ config.azure_client_secret_env_var,
1040
+ )
597
1041
  else:
598
1042
  config.azure_client_secret = None
599
1043
 
1044
+ # Entra config
1045
+ if (
1046
+ config.entra_tenant_id
1047
+ and config.entra_client_id
1048
+ and config.entra_client_secret_env_var
1049
+ ):
1050
+ logger.debug(
1051
+ "Reading Client Secret for Entra Authentication from environment variable %s",
1052
+ config.entra_client_secret_env_var,
1053
+ )
1054
+ config.entra_client_secret = os.environ.get(
1055
+ config.entra_client_secret_env_var
1056
+ )
1057
+ else:
1058
+ config.entra_client_secret = None
1059
+
600
1060
  # Okta config
601
1061
  if config.okta_org_id and config.okta_api_key_env_var:
602
- logger.debug(f"Reading API key for Okta from environment variable {config.okta_api_key_env_var}")
1062
+ logger.debug(
1063
+ f"Reading API key for Okta from environment variable {config.okta_api_key_env_var}",
1064
+ )
603
1065
  config.okta_api_key = os.environ.get(config.okta_api_key_env_var)
604
1066
  else:
605
1067
  config.okta_api_key = None
606
1068
 
607
- # CRXcavator config
608
- if config.crxcavator_api_base_uri and config.crxcavator_api_key_env_var:
609
- logger.debug(f"Reading API key for CRXcavator from env variable {config.crxcavator_api_key_env_var}.")
610
- config.crxcavator_api_key = os.environ.get(config.crxcavator_api_key_env_var)
611
- else:
612
- config.crxcavator_api_key = None
613
-
614
1069
  # GitHub config
615
1070
  if config.github_config_env_var:
616
- logger.debug(f"Reading config string for GitHub from environment variable {config.github_config_env_var}")
1071
+ logger.debug(
1072
+ f"Reading config string for GitHub from environment variable {config.github_config_env_var}",
1073
+ )
617
1074
  config.github_config = os.environ.get(config.github_config_env_var)
618
1075
  else:
619
1076
  config.github_config = None
620
1077
 
621
1078
  # DigitalOcean config
622
1079
  if config.digitalocean_token_env_var:
623
- logger.debug(f"Reading token for DigitalOcean from env variable {config.digitalocean_token_env_var}")
624
- config.digitalocean_token = os.environ.get(config.digitalocean_token_env_var)
1080
+ logger.debug(
1081
+ f"Reading token for DigitalOcean from env variable {config.digitalocean_token_env_var}",
1082
+ )
1083
+ config.digitalocean_token = os.environ.get(
1084
+ config.digitalocean_token_env_var,
1085
+ )
625
1086
  else:
626
1087
  config.digitalocean_token = None
627
1088
 
@@ -652,27 +1113,29 @@ class CLI:
652
1113
  config.kandji_token_env_var,
653
1114
  )
654
1115
  config.kandji_token = os.environ.get(config.kandji_token_env_var)
655
- elif os.environ.get('KANDJI_TOKEN'):
1116
+ elif os.environ.get("KANDJI_TOKEN"):
656
1117
  logger.debug(
657
1118
  "Reading Kandji API token from environment variable 'KANDJI_TOKEN'.",
658
1119
  )
659
- config.kandji_token = os.environ.get('KANDJI_TOKEN')
1120
+ config.kandji_token = os.environ.get("KANDJI_TOKEN")
660
1121
  else:
661
1122
  logger.warning("A Kandji base URI was provided but a token was not.")
662
1123
  config.kandji_token = None
663
1124
  else:
664
- logger.warning("A Kandji base URI was not provided.")
665
1125
  config.kandji_base_uri = None
1126
+ config.kandji_token = None
666
1127
 
667
1128
  if config.statsd_enabled:
668
1129
  logger.debug(
669
- f'statsd enabled. Sending metrics to server {config.statsd_host}:{config.statsd_port}. '
1130
+ f"statsd enabled. Sending metrics to server {config.statsd_host}:{config.statsd_port}. "
670
1131
  f'Metrics have prefix "{config.statsd_prefix}".',
671
1132
  )
672
1133
 
673
1134
  # Pagerduty config
674
1135
  if config.pagerduty_api_key_env_var:
675
- logger.debug(f"Reading API key for PagerDuty from environment variable {config.pagerduty_api_key_env_var}")
1136
+ logger.debug(
1137
+ f"Reading API key for PagerDuty from environment variable {config.pagerduty_api_key_env_var}",
1138
+ )
676
1139
  config.pagerduty_api_key = os.environ.get(config.pagerduty_api_key_env_var)
677
1140
  else:
678
1141
  config.pagerduty_api_key = None
@@ -682,7 +1145,9 @@ class CLI:
682
1145
  logger.debug(
683
1146
  f"Reading API key for Crowdstrike from environment variable {config.crowdstrike_client_id_env_var}",
684
1147
  )
685
- config.crowdstrike_client_id = os.environ.get(config.crowdstrike_client_id_env_var)
1148
+ config.crowdstrike_client_id = os.environ.get(
1149
+ config.crowdstrike_client_id_env_var,
1150
+ )
686
1151
  else:
687
1152
  config.crowdstrike_client_id = None
688
1153
 
@@ -690,36 +1155,65 @@ class CLI:
690
1155
  logger.debug(
691
1156
  f"Reading API key for Crowdstrike from environment variable {config.crowdstrike_client_secret_env_var}",
692
1157
  )
693
- config.crowdstrike_client_secret = os.environ.get(config.crowdstrike_client_secret_env_var)
1158
+ config.crowdstrike_client_secret = os.environ.get(
1159
+ config.crowdstrike_client_secret_env_var,
1160
+ )
694
1161
  else:
695
1162
  config.crowdstrike_client_secret = None
696
1163
 
697
1164
  # GSuite config
698
1165
  if config.gsuite_tokens_env_var:
699
- logger.debug(f"Reading config string for GSuite from environment variable {config.gsuite_tokens_env_var}")
1166
+ logger.debug(
1167
+ f"Reading config string for GSuite from environment variable {config.gsuite_tokens_env_var}",
1168
+ )
700
1169
  config.gsuite_config = os.environ.get(config.gsuite_tokens_env_var)
701
1170
  else:
702
1171
  config.gsuite_tokens_env_var = None
703
1172
 
1173
+ # Google Workspace config
1174
+ if config.googleworkspace_tokens_env_var:
1175
+ logger.debug(
1176
+ f"Reading config string for Google Workspace from environment variable {config.googleworkspace_tokens_env_var}",
1177
+ )
1178
+ config.googleworkspace_config = os.environ.get(
1179
+ config.googleworkspace_tokens_env_var
1180
+ )
1181
+ else:
1182
+ config.googleworkspace_tokens_env_var = None
1183
+
704
1184
  # Lastpass config
705
1185
  if config.lastpass_cid_env_var:
706
- logger.debug(f"Reading CID for Lastpass from environment variable {config.lastpass_cid_env_var}")
1186
+ logger.debug(
1187
+ f"Reading CID for Lastpass from environment variable {config.lastpass_cid_env_var}",
1188
+ )
707
1189
  config.lastpass_cid = os.environ.get(config.lastpass_cid_env_var)
708
1190
  else:
709
1191
  config.lastpass_cid = None
710
1192
  if config.lastpass_provhash_env_var:
711
- logger.debug(f"Reading provhash for Lastpass from environment variable {config.lastpass_provhash_env_var}")
1193
+ logger.debug(
1194
+ f"Reading provhash for Lastpass from environment variable {config.lastpass_provhash_env_var}",
1195
+ )
712
1196
  config.lastpass_provhash = os.environ.get(config.lastpass_provhash_env_var)
713
1197
  else:
714
1198
  config.lastpass_provhash = None
715
1199
 
716
1200
  # BigFix config
717
- if config.bigfix_username and config.bigfix_password_env_var and config.bigfix_root_url:
718
- logger.debug(f"Reading BigFix password from environment variable {config.bigfix_password_env_var}")
1201
+ if (
1202
+ config.bigfix_username
1203
+ and config.bigfix_password_env_var
1204
+ and config.bigfix_root_url
1205
+ ):
1206
+ logger.debug(
1207
+ f"Reading BigFix password from environment variable {config.bigfix_password_env_var}",
1208
+ )
719
1209
  config.bigfix_password = os.environ.get(config.bigfix_password_env_var)
720
1210
 
721
1211
  # Duo config
722
- if config.duo_api_key_env_var and config.duo_api_secret_env_var and config.duo_api_hostname:
1212
+ if (
1213
+ config.duo_api_key_env_var
1214
+ and config.duo_api_secret_env_var
1215
+ and config.duo_api_hostname
1216
+ ):
723
1217
  logger.debug(
724
1218
  f"Reading Duo api key and secret from environment variables {config.duo_api_key_env_var}"
725
1219
  f", {config.duo_api_secret_env_var}",
@@ -732,18 +1226,196 @@ class CLI:
732
1226
 
733
1227
  # Semgrep config
734
1228
  if config.semgrep_app_token_env_var:
735
- logger.debug(f"Reading Semgrep App Token from environment variable {config.semgrep_app_token_env_var}")
1229
+ logger.debug(
1230
+ f"Reading Semgrep App Token from environment variable {config.semgrep_app_token_env_var}",
1231
+ )
736
1232
  config.semgrep_app_token = os.environ.get(config.semgrep_app_token_env_var)
737
1233
  else:
738
1234
  config.semgrep_app_token = None
1235
+ if config.semgrep_dependency_ecosystems:
1236
+ # No need to store the returned value; we're using this for input validation.
1237
+ parse_and_validate_semgrep_ecosystems(config.semgrep_dependency_ecosystems)
739
1238
 
740
1239
  # CVE feed config
741
1240
  if config.cve_api_key_env_var:
742
- logger.debug(f"Reading NVD CVE API key environment variable {config.cve_api_key_env_var}")
1241
+ logger.debug(
1242
+ f"Reading NVD CVE API key environment variable {config.cve_api_key_env_var}",
1243
+ )
743
1244
  config.cve_api_key = os.environ.get(config.cve_api_key_env_var)
744
1245
  else:
745
1246
  config.cve_api_key = None
746
1247
 
1248
+ # SnipeIT config
1249
+ if config.snipeit_base_uri:
1250
+ if config.snipeit_token_env_var:
1251
+ logger.debug(
1252
+ "Reading SnipeIT API token from environment variable '%s'.",
1253
+ config.snipeit_token_env_var,
1254
+ )
1255
+ config.snipeit_token = os.environ.get(config.snipeit_token_env_var)
1256
+ elif os.environ.get("SNIPEIT_TOKEN"):
1257
+ logger.debug(
1258
+ "Reading SnipeIT API token from environment variable 'SNIPEIT_TOKEN'.",
1259
+ )
1260
+ config.snipeit_token = os.environ.get("SNIPEIT_TOKEN")
1261
+ else:
1262
+ logger.warning("A SnipeIT base URI was provided but a token was not.")
1263
+ config.snipeit_token = None
1264
+ else:
1265
+ config.snipeit_base_uri = None
1266
+ config.snipeit_token = None
1267
+
1268
+ # Tailscale config
1269
+ if config.tailscale_token_env_var:
1270
+ logger.debug(
1271
+ f"Reading Tailscale API token from environment variable {config.tailscale_token_env_var}",
1272
+ )
1273
+ config.tailscale_token = os.environ.get(config.tailscale_token_env_var)
1274
+ else:
1275
+ config.tailscale_token = None
1276
+
1277
+ # Cloudflare config
1278
+ if config.cloudflare_token_env_var:
1279
+ logger.debug(
1280
+ f"Reading Cloudflare ApiKey from environment variable {config.cloudflare_token_env_var}",
1281
+ )
1282
+ config.cloudflare_token = os.environ.get(config.cloudflare_token_env_var)
1283
+ else:
1284
+ config.cloudflare_token = None
1285
+
1286
+ # OpenAI config
1287
+ if config.openai_apikey_env_var:
1288
+ logger.debug(
1289
+ f"Reading OpenAI API key from environment variable {config.openai_apikey_env_var}",
1290
+ )
1291
+ config.openai_apikey = os.environ.get(config.openai_apikey_env_var)
1292
+ else:
1293
+ config.openai_apikey = None
1294
+
1295
+ # Anthropic config
1296
+ if config.anthropic_apikey_env_var:
1297
+ logger.debug(
1298
+ f"Reading Anthropic API key from environment variable {config.anthropic_apikey_env_var}",
1299
+ )
1300
+ config.anthropic_apikey = os.environ.get(config.anthropic_apikey_env_var)
1301
+ else:
1302
+ config.anthropic_apikey = None
1303
+
1304
+ # Airbyte config
1305
+ if config.airbyte_client_id and config.airbyte_client_secret_env_var:
1306
+ logger.debug(
1307
+ f"Reading Airbyte client secret from environment variable {config.airbyte_client_secret_env_var}",
1308
+ )
1309
+ config.airbyte_client_secret = os.environ.get(
1310
+ config.airbyte_client_secret_env_var,
1311
+ )
1312
+ else:
1313
+ config.airbyte_client_secret = None
1314
+
1315
+ # Trivy config
1316
+ if config.trivy_s3_bucket:
1317
+ logger.debug(f"Trivy S3 bucket: {config.trivy_s3_bucket}")
1318
+
1319
+ if config.trivy_s3_prefix:
1320
+ logger.debug(f"Trivy S3 prefix: {config.trivy_s3_prefix}")
1321
+
1322
+ if config.trivy_results_dir:
1323
+ logger.debug(f"Trivy results dir: {config.trivy_results_dir}")
1324
+
1325
+ # Scaleway config
1326
+ if config.scaleway_secret_key_env_var:
1327
+ logger.debug(
1328
+ f"Reading Scaleway secret key from environment variable {config.scaleway_secret_key_env_var}",
1329
+ )
1330
+ config.scaleway_secret_key = os.environ.get(
1331
+ config.scaleway_secret_key_env_var,
1332
+ )
1333
+ else:
1334
+ config.scaleway_secret_key = None
1335
+
1336
+ # SentinelOne config
1337
+ if config.sentinelone_account_ids:
1338
+ config.sentinelone_account_ids = [
1339
+ id.strip() for id in config.sentinelone_account_ids.split(",")
1340
+ ]
1341
+ logger.debug(
1342
+ f"Parsed {len(config.sentinelone_account_ids)} SentinelOne account IDs to sync"
1343
+ )
1344
+ else:
1345
+ config.sentinelone_account_ids = None
1346
+
1347
+ if config.sentinelone_api_url and config.sentinelone_api_token_env_var:
1348
+ logger.debug(
1349
+ f"Reading API token for SentinelOne from environment variable {config.sentinelone_api_token_env_var}",
1350
+ )
1351
+ config.sentinelone_api_token = os.environ.get(
1352
+ config.sentinelone_api_token_env_var
1353
+ )
1354
+ else:
1355
+ config.sentinelone_api_token = None
1356
+
1357
+ # Keycloak config
1358
+ if config.keycloak_client_secret_env_var:
1359
+ logger.debug(
1360
+ f"Reading Client Secret for Keycloak from environment variable {config.keycloak_client_secret_env_var}",
1361
+ )
1362
+ config.keycloak_client_secret = os.environ.get(
1363
+ config.keycloak_client_secret_env_var
1364
+ )
1365
+ else:
1366
+ config.keycloak_client_secret = None
1367
+
1368
+ # Slack config
1369
+ if config.slack_token_env_var:
1370
+ logger.debug(
1371
+ f"Reading Slack token from environment variable {config.slack_token_env_var}",
1372
+ )
1373
+ config.slack_token = os.environ.get(config.slack_token_env_var)
1374
+ else:
1375
+ config.slack_token = None
1376
+
1377
+ # Spacelift config
1378
+ # Read endpoint from CLI arg or env var
1379
+ if not config.spacelift_api_endpoint:
1380
+ config.spacelift_api_endpoint = os.environ.get("SPACELIFT_API_ENDPOINT")
1381
+
1382
+ if config.spacelift_api_endpoint:
1383
+ # Try to read API token
1384
+ if config.spacelift_api_token_env_var:
1385
+ logger.debug(
1386
+ f"Reading API token for Spacelift from environment variable {config.spacelift_api_token_env_var}",
1387
+ )
1388
+ config.spacelift_api_token = os.environ.get(
1389
+ config.spacelift_api_token_env_var
1390
+ )
1391
+ else:
1392
+ config.spacelift_api_token = None
1393
+
1394
+ # Try to read API key ID and secret
1395
+ if config.spacelift_api_key_id_env_var:
1396
+ logger.debug(
1397
+ f"Reading API key ID for Spacelift from environment variable {config.spacelift_api_key_id_env_var}",
1398
+ )
1399
+ config.spacelift_api_key_id = os.environ.get(
1400
+ config.spacelift_api_key_id_env_var
1401
+ )
1402
+ else:
1403
+ config.spacelift_api_key_id = None
1404
+
1405
+ if config.spacelift_api_key_secret_env_var:
1406
+ logger.debug(
1407
+ f"Reading API key secret for Spacelift from environment variable {config.spacelift_api_key_secret_env_var}",
1408
+ )
1409
+ config.spacelift_api_key_secret = os.environ.get(
1410
+ config.spacelift_api_key_secret_env_var
1411
+ )
1412
+ else:
1413
+ config.spacelift_api_key_secret = None
1414
+ else:
1415
+ config.spacelift_api_token = None
1416
+ config.spacelift_api_key_id = None
1417
+ config.spacelift_api_key_secret = None
1418
+
747
1419
  # Run cartography
748
1420
  try:
749
1421
  return cartography.sync.run_with_config(self.sync, config)
@@ -761,8 +1433,14 @@ def main(argv=None):
761
1433
  :return: The return code.
762
1434
  """
763
1435
  logging.basicConfig(level=logging.INFO)
764
- logging.getLogger('botocore').setLevel(logging.WARNING)
765
- logging.getLogger('googleapiclient').setLevel(logging.WARNING)
766
- logging.getLogger('neo4j').setLevel(logging.WARNING)
1436
+ logging.getLogger("botocore").setLevel(logging.WARNING)
1437
+ logging.getLogger("googleapiclient").setLevel(logging.WARNING)
1438
+ logging.getLogger("neo4j").setLevel(logging.WARNING)
1439
+ logging.getLogger("azure.identity").setLevel(logging.WARNING)
1440
+ logging.getLogger("httpx").setLevel(logging.WARNING)
1441
+ logging.getLogger("azure.core.pipeline.policies.http_logging_policy").setLevel(
1442
+ logging.WARNING
1443
+ )
1444
+
767
1445
  argv = argv if argv is not None else sys.argv[1:]
768
- sys.exit(CLI(prog='cartography').main(argv))
1446
+ sys.exit(CLI(prog="cartography").main(argv))