praetorian-cli 2.4.1__tar.gz → 2.4.2__tar.gz

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 (147) hide show
  1. {praetorian_cli-2.4.1/praetorian_cli.egg-info → praetorian_cli-2.4.2}/PKG-INFO +1 -1
  2. praetorian_cli-2.4.2/praetorian_cli/handlers/access.py +209 -0
  3. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/configure.py +1 -1
  4. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/engagement.py +7 -4
  5. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/list.py +21 -2
  6. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/main.py +1 -0
  7. praetorian_cli-2.4.2/praetorian_cli/sdk/entities/account_discovery.py +443 -0
  8. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/assets.py +25 -0
  9. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/attributes.py +15 -0
  10. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/credentials.py +20 -3
  11. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/jobs.py +171 -0
  12. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/risks.py +24 -0
  13. praetorian_cli-2.4.2/praetorian_cli/sdk/test/test_access_aws.py +198 -0
  14. praetorian_cli-2.4.2/praetorian_cli/sdk/test/test_bulk.py +315 -0
  15. praetorian_cli-2.4.2/praetorian_cli/sdk/test/test_credential_process.py +63 -0
  16. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_job.py +44 -0
  17. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_z_cli.py +2 -1
  18. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/ui_mocks.py +5 -0
  19. praetorian_cli-2.4.2/praetorian_cli/ui/aegis/account_selector.py +250 -0
  20. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/aegis/commands/job.py +2 -1
  21. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/aegis/commands/job_helpers.py +10 -0
  22. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/aegis/commands/list.py +1 -3
  23. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/aegis/commands/schedule.py +262 -166
  24. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/aegis/commands/set.py +27 -1
  25. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/aegis/menu.py +120 -19
  26. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/console/commands/__init__.py +1 -0
  27. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/console/commands/accounts.py +6 -2
  28. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/console/commands/context.py +70 -2
  29. praetorian_cli-2.4.2/praetorian_cli/ui/console/commands/data.py +162 -0
  30. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/console/commands/marcus.py +109 -14
  31. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/console/console.py +28 -0
  32. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/console/context.py +1 -0
  33. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2/praetorian_cli.egg-info}/PKG-INFO +1 -1
  34. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli.egg-info/SOURCES.txt +7 -0
  35. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/setup.cfg +1 -1
  36. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/LICENSE +0 -0
  37. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/MANIFEST.in +0 -0
  38. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/README.md +0 -0
  39. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/__init__.py +0 -0
  40. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/__init__.py +0 -0
  41. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/add.py +0 -0
  42. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/aegis.py +0 -0
  43. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/agent.py +0 -0
  44. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/chariot.py +0 -0
  45. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/cli_decorators.py +0 -0
  46. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/critfinder.py +0 -0
  47. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/delete.py +0 -0
  48. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/enrich.py +0 -0
  49. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/export.py +0 -0
  50. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/find.py +0 -0
  51. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/get.py +0 -0
  52. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/imports.py +0 -0
  53. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/link.py +0 -0
  54. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/report.py +0 -0
  55. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/run.py +0 -0
  56. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/script.py +0 -0
  57. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/search.py +0 -0
  58. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/ssh_utils.py +0 -0
  59. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/test.py +0 -0
  60. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/unlink.py +0 -0
  61. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/update.py +0 -0
  62. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/handlers/utils.py +0 -0
  63. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/runners/__init__.py +0 -0
  64. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/runners/local.py +0 -0
  65. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/scripts/__init__.py +0 -0
  66. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/scripts/commands/__init__.py +0 -0
  67. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/scripts/commands/nmap-example.py +0 -0
  68. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/scripts/utils.py +0 -0
  69. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/__init__.py +0 -0
  70. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/chariot.py +0 -0
  71. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/__init__.py +0 -0
  72. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/accounts.py +0 -0
  73. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/ad.py +0 -0
  74. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/aegis.py +0 -0
  75. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/agents.py +0 -0
  76. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/capabilities.py +0 -0
  77. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/configurations.py +0 -0
  78. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/definitions.py +0 -0
  79. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/files.py +0 -0
  80. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/integrations.py +0 -0
  81. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/keys.py +0 -0
  82. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/preseeds.py +0 -0
  83. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/reports.py +0 -0
  84. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/scanners.py +0 -0
  85. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/schedules.py +0 -0
  86. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/schema.py +0 -0
  87. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/search.py +0 -0
  88. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/seeds.py +0 -0
  89. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/settings.py +0 -0
  90. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/statistics.py +0 -0
  91. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/webhook.py +0 -0
  92. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/entities/webpage.py +0 -0
  93. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/guard.py +0 -0
  94. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/keychain.py +0 -0
  95. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/mcp_server.py +0 -0
  96. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/model/__init__.py +0 -0
  97. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/model/aegis.py +0 -0
  98. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/model/globals.py +0 -0
  99. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/model/query.py +0 -0
  100. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/model/utils.py +0 -0
  101. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/__init__.py +0 -0
  102. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/pytest.ini +0 -0
  103. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_account.py +0 -0
  104. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_agent.py +0 -0
  105. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_asset.py +0 -0
  106. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_attribute.py +0 -0
  107. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_capabilities.py +0 -0
  108. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_configuration.py +0 -0
  109. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_conversation.py +0 -0
  110. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_definition.py +0 -0
  111. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_extend.py +0 -0
  112. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_file.py +0 -0
  113. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_key.py +0 -0
  114. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_mcp.py +0 -0
  115. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_preseed.py +0 -0
  116. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_report.py +0 -0
  117. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_risk.py +0 -0
  118. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_search.py +0 -0
  119. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_seed.py +0 -0
  120. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_setting.py +0 -0
  121. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_webhook.py +0 -0
  122. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/test_webpage.py +0 -0
  123. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/sdk/test/utils.py +0 -0
  124. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/__init__.py +0 -0
  125. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/aegis/__init__.py +0 -0
  126. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/aegis/commands/__init__.py +0 -0
  127. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/aegis/commands/cp.py +0 -0
  128. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/aegis/commands/help.py +0 -0
  129. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/aegis/commands/info.py +0 -0
  130. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/aegis/commands/proxy.py +0 -0
  131. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/aegis/commands/schedule_helpers.py +0 -0
  132. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/aegis/commands/ssh.py +0 -0
  133. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/aegis/constants.py +0 -0
  134. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/aegis/theme.py +0 -0
  135. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/aegis/utils.py +0 -0
  136. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/console/__init__.py +0 -0
  137. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/console/commands/reporting.py +0 -0
  138. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/console/commands/search.py +0 -0
  139. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/console/commands/tools.py +0 -0
  140. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/console/renderer.py +0 -0
  141. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/conversation/__init__.py +0 -0
  142. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli/ui/conversation/textual_chat.py +0 -0
  143. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli.egg-info/dependency_links.txt +0 -0
  144. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli.egg-info/entry_points.txt +0 -0
  145. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli.egg-info/requires.txt +0 -0
  146. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/praetorian_cli.egg-info/top_level.txt +0 -0
  147. {praetorian_cli-2.4.1 → praetorian_cli-2.4.2}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: praetorian-cli
3
- Version: 2.4.1
3
+ Version: 2.4.2
4
4
  Summary: For interacting with the Guard API
5
5
  Home-page: https://github.com/praetorian-inc/praetorian-cli
6
6
  Author: Praetorian
@@ -0,0 +1,209 @@
1
+ import os
2
+
3
+ import click
4
+
5
+ from praetorian_cli.handlers.chariot import chariot
6
+ from praetorian_cli.handlers.cli_decorators import cli_handler
7
+
8
+
9
+ def extract_prefix(email):
10
+ """Extract the profile prefix from a Guard account email.
11
+
12
+ Takes the portion after '+' and before '@'.
13
+ If no '+' exists, uses the full local part (before '@').
14
+
15
+ Examples:
16
+ chariot+proceptbiorobotics@praetorian.com -> proceptbiorobotics
17
+ chariot+grant_street_group-ztw@praetorian.com -> grant_street_group-ztw
18
+ user@example.com -> user
19
+ """
20
+ local_part = email.split('@')[0]
21
+ if '+' in local_part:
22
+ return local_part.split('+', 1)[1]
23
+ return local_part
24
+
25
+
26
+ def extract_account_id(credential_key):
27
+ """Extract the AWS account ID from a credential key.
28
+
29
+ Keys look like: #account#chariot+client@praetorian.com#amazon#325281727610
30
+ The account ID is the last segment.
31
+ """
32
+ return credential_key.rstrip('#').split('#')[-1]
33
+
34
+
35
+ def build_aws_config_profiles(account_email, prefix, credential_id, root_account_id, sub_accounts, category='env-integration'):
36
+ """Build a list of (profile_name, profile_dict) tuples for AWS config.
37
+
38
+ Args:
39
+ account_email: The Guard account email (used in credential_process command)
40
+ prefix: The profile name prefix
41
+ credential_id: The credential UUID
42
+ root_account_id: The root AWS account ID
43
+ sub_accounts: List of dicts with 'Id' key from Organizations list_accounts,
44
+ or empty list if not an org.
45
+ category: The credential category (e.g., 'env-integration', 'cloud')
46
+ Returns:
47
+ List of (profile_name, dict) where dict has credential_process, region, output keys.
48
+ """
49
+ profiles = []
50
+
51
+ # Collect all account IDs (root + sub-accounts), deduplicated
52
+ all_account_ids = [root_account_id]
53
+ for acct in sub_accounts:
54
+ if acct['Id'] != root_account_id:
55
+ all_account_ids.append(acct['Id'])
56
+
57
+ for account_id in all_account_ids:
58
+ profile_name = f'{prefix}-{account_id}'
59
+ credential_process = (
60
+ f'guard --account {account_email} get credential {credential_id} '
61
+ f'--category {category} --type aws --format credential-process '
62
+ f'--parameters accountId {account_id}'
63
+ )
64
+ profiles.append((profile_name, {
65
+ 'credential_process': credential_process,
66
+ 'region': 'us-east-1',
67
+ 'output': 'json',
68
+ }))
69
+
70
+ return profiles
71
+
72
+
73
+ def write_aws_config(profiles, config_path=None):
74
+ """Write AWS config profiles, preserving existing unrelated profiles.
75
+
76
+ Args:
77
+ profiles: List of (profile_name, profile_dict) tuples.
78
+ config_path: Path to AWS config file. Defaults to ~/.aws/config.
79
+ """
80
+ from configparser import ConfigParser
81
+ from pathlib import Path
82
+
83
+ if config_path is None:
84
+ config_path = os.path.join(Path.home(), '.aws', 'config')
85
+
86
+ config = ConfigParser()
87
+ if os.path.exists(config_path):
88
+ config.read(config_path)
89
+
90
+ for profile_name, profile_data in profiles:
91
+ section = f'profile {profile_name}'
92
+ if not config.has_section(section):
93
+ config.add_section(section)
94
+ for key, value in profile_data.items():
95
+ config.set(section, key, value)
96
+
97
+ parent_dir = os.path.dirname(config_path)
98
+ if parent_dir:
99
+ os.makedirs(parent_dir, exist_ok=True)
100
+ with open(config_path, 'w') as f:
101
+ config.write(f)
102
+
103
+
104
+ @chariot.group()
105
+ def access():
106
+ """Configure credential access for cloud providers."""
107
+ pass
108
+
109
+
110
+ @access.command()
111
+ @cli_handler
112
+ @click.option('--account', default=None, help='Guard account email to discover AWS credentials for (also inherited from guard --account)')
113
+ @click.option('--prefix', default=None, help='Override the profile name prefix (default: derived from email)')
114
+ def aws(sdk, account, prefix):
115
+ """Generate AWS CLI config profiles from Guard credentials.
116
+
117
+ Discovers AWS credentials for the given account, enumerates sub-accounts
118
+ via Organizations (if applicable), and writes ~/.aws/config profiles that
119
+ use guard as the credential_process.
120
+
121
+ The --account can be provided here or as the top-level guard --account flag.
122
+
123
+ \b
124
+ Example usages:
125
+ - guard --account chariot+client@praetorian.com access aws
126
+ - guard access aws --account chariot+client@praetorian.com
127
+ - guard access aws --account chariot+client@praetorian.com --prefix myclient
128
+ """
129
+ import boto3
130
+
131
+ # Resolve account: explicit --account on this command, or inherited from guard --account
132
+ if account is None:
133
+ account = sdk.keychain.account
134
+ if account is None:
135
+ raise click.ClickException('--account is required. Provide it here or as guard --account.')
136
+
137
+ sdk.keychain.assume_role(account)
138
+
139
+ if prefix is None:
140
+ prefix = extract_prefix(account)
141
+
142
+ # List credentials and filter to AWS type
143
+ creds_response = sdk.credentials.list()
144
+ credentials_list = creds_response[0] if isinstance(creds_response, tuple) else []
145
+
146
+ aws_credentials = [c for c in credentials_list if c.get('type', '') == 'aws']
147
+
148
+ if not aws_credentials:
149
+ click.echo(f'No AWS credentials found for account {account}')
150
+ return
151
+
152
+ all_profiles = []
153
+
154
+ for cred in aws_credentials:
155
+ credential_id = cred.get('credentialId', '')
156
+ account_key = cred.get('accountKey', '')
157
+ category = cred.get('category', 'env-integration')
158
+ root_account_id = extract_account_id(account_key)
159
+
160
+ # Fetch the root credential to get temp AWS creds
161
+ try:
162
+ result = sdk.credentials.get(credential_id, category, 'aws', ['token'])
163
+ except Exception as e:
164
+ click.echo(f'Warning: failed to get credential {credential_id}: {e}')
165
+ continue
166
+
167
+ access_key = result.get('credentialValue', {}).get('accessKeyId')
168
+ secret_key = result.get('credentialValue', {}).get('secretAccessKey')
169
+ session_token = result.get('credentialValue', {}).get('sessionToken')
170
+
171
+ if not access_key:
172
+ click.echo(f'Warning: credential {credential_id} returned no access key')
173
+ continue
174
+
175
+ # Try Organizations to discover sub-accounts
176
+ sub_accounts = []
177
+ try:
178
+ org_client = boto3.client(
179
+ 'organizations',
180
+ aws_access_key_id=access_key,
181
+ aws_secret_access_key=secret_key,
182
+ aws_session_token=session_token,
183
+ region_name='us-east-1'
184
+ )
185
+ paginator = org_client.get_paginator('list_accounts')
186
+ for page in paginator.paginate():
187
+ sub_accounts.extend(page['Accounts'])
188
+ except Exception:
189
+ # Not an org account — that's fine, just use the root
190
+ pass
191
+
192
+ profiles = build_aws_config_profiles(
193
+ account_email=account,
194
+ prefix=prefix,
195
+ credential_id=credential_id,
196
+ root_account_id=root_account_id,
197
+ sub_accounts=sub_accounts,
198
+ category=category,
199
+ )
200
+ all_profiles.extend(profiles)
201
+
202
+ if not all_profiles:
203
+ click.echo(f'No AWS profiles could be generated for account {account}')
204
+ return
205
+
206
+ write_aws_config(all_profiles)
207
+ click.echo(f'Wrote {len(all_profiles)} AWS profile(s) to ~/.aws/config:')
208
+ for profile_name, _ in all_profiles:
209
+ click.echo(f' {profile_name}')
@@ -6,7 +6,7 @@ from praetorian_cli.sdk.keychain import Keychain, DEFAULT_API, DEFAULT_CLIENT_ID
6
6
  @click.pass_context
7
7
  def configure(click_context):
8
8
  """Configure the CLI with API key authentication."""
9
-
9
+
10
10
  api_key_id = click.prompt("Enter your API Key ID")
11
11
  api_key_secret = click.prompt("Enter your API Key secret", hide_input=True)
12
12
 
@@ -18,16 +18,17 @@ from praetorian_cli.handlers.add import add
18
18
  @click.option('--type', 'customer_type', type=click.Choice(['ENGAGEMENT', 'MANAGED', 'SAAS', 'PILOT', 'FREEMIUM']),
19
19
  default='ENGAGEMENT', show_default=True, help='Customer type')
20
20
  @click.option('--collaborator', multiple=True, help='Additional collaborator emails (format: email:role)')
21
- def create_customer(sdk, email, display_name, scan_level, customer_type, collaborator):
21
+ @click.option('--allowed-domain', multiple=True, help='Allowed email domains for collaborators (e.g., acme.com)')
22
+ def create_customer(sdk, email, display_name, scan_level, customer_type, collaborator, allowed_domain):
22
23
  """ Create a new customer account on the Guard platform
23
24
 
24
25
  Creates a Cognito user, Guard account, and adds default collaborators.
25
26
 
26
27
  \b
27
28
  Example usages:
28
- - guard add customer --email ops@acme.com --name "ACME Corp"
29
- - guard add customer --email ops@acme.com --name "ACME Corp" --type MANAGED
30
- - guard add customer --email ops@acme.com --name "ACME Corp" --collaborator "analyst@praetorian.com:admin"
29
+ - guard add customer --email ops@acme.com --name "ACME Corp" --allowed-domain acme.com
30
+ - guard add customer --email ops@acme.com --name "ACME Corp" --type MANAGED --allowed-domain acme.com
31
+ - guard add customer --email ops@acme.com --name "ACME Corp" --allowed-domain acme.com --collaborator "analyst@praetorian.com:admin"
31
32
  """
32
33
  body = {
33
34
  'username': email,
@@ -47,6 +48,8 @@ def create_customer(sdk, email, display_name, scan_level, customer_type, collabo
47
48
  collabs.append({'email': c, 'role': 'admin'})
48
49
  body['collaborators'] = collabs
49
50
 
51
+ body['allowed_domains'] = [d.lower().strip() for d in allowed_domain]
52
+
50
53
  result = sdk.post('customer/onboard', body)
51
54
  click.echo(f'Customer created: {email} ({display_name})')
52
55
  print_json(result)
@@ -99,7 +99,12 @@ def integrations(chariot, filter, details, offset, page):
99
99
 
100
100
  @list.command()
101
101
  @list_params('group of the job asset')
102
- def jobs(chariot, filter, details, offset, page):
102
+ @click.option('-c', '--capability', default=None, help='Filter by capability name (e.g., nuclei, portscan, diana-agent)')
103
+ @click.option('-s', '--status', type=click.Choice(['JQ', 'JR', 'JP', 'JF']),
104
+ default=None, help='Filter by job status (JQ=queued, JR=running, JP=passed, JF=failed)')
105
+ @click.option('-t', '--target', default=None, help='Filter by target DNS or hostname')
106
+ @click.option('--summary', is_flag=True, default=False, help='Show job count summary by capability and status')
107
+ def jobs(chariot, capability, status, target, summary, filter, details, offset, page):
103
108
  """
104
109
  List jobs
105
110
 
@@ -110,10 +115,24 @@ def jobs(chariot, filter, details, offset, page):
110
115
  Example usages:
111
116
  - guard list jobs
112
117
  - guard list jobs --filter www.example.com
118
+ - guard list jobs --capability nuclei
119
+ - guard list jobs --capability diana-agent --details
120
+ - guard list jobs --status JR
121
+ - guard list jobs --target api.example.com
122
+ - guard list jobs --summary
113
123
  - guard list jobs --details
114
124
  - guard list jobs --page all
115
125
  """
116
- render_list_results(chariot.jobs.list(filter, offset, pagination_size(page)), details)
126
+ if summary:
127
+ print_json(chariot.jobs.summary(pagination_size(page)))
128
+ elif capability:
129
+ render_list_results(chariot.jobs.list_by_capability(capability, offset, pagination_size(page)), details)
130
+ elif status:
131
+ render_list_results(chariot.jobs.list_by_status(status, offset, pagination_size(page)), details)
132
+ elif target:
133
+ render_list_results(chariot.jobs.list_by_target(target, offset, pagination_size(page)), details)
134
+ else:
135
+ render_list_results(chariot.jobs.list(filter, offset, pagination_size(page)), details)
117
136
 
118
137
 
119
138
  @list.command()
@@ -1,5 +1,6 @@
1
1
  import click
2
2
 
3
+ import praetorian_cli.handlers.access
3
4
  import praetorian_cli.handlers.add
4
5
  import praetorian_cli.handlers.aegis
5
6
  import praetorian_cli.handlers.agent