praetorian-cli 2.4.2__tar.gz → 2.4.3__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 (151) hide show
  1. {praetorian_cli-2.4.2/praetorian_cli.egg-info → praetorian_cli-2.4.3}/PKG-INFO +1 -1
  2. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/add.py +50 -19
  3. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/delete.py +58 -1
  4. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/export.py +7 -1
  5. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/run.py +36 -10
  6. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/update.py +6 -3
  7. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/utils.py +16 -14
  8. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/runners/local.py +107 -22
  9. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/assets.py +7 -2
  10. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/credentials.py +26 -3
  11. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/reports.py +13 -1
  12. praetorian_cli-2.4.3/praetorian_cli/sdk/test/test_credentials.py +266 -0
  13. praetorian_cli-2.4.3/praetorian_cli/sdk/test/test_export_cli.py +100 -0
  14. praetorian_cli-2.4.3/praetorian_cli/sdk/test/test_local_runner.py +230 -0
  15. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_report.py +11 -1
  16. praetorian_cli-2.4.3/praetorian_cli/sdk/test/test_run_cli.py +128 -0
  17. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/aegis/commands/job_helpers.py +10 -5
  18. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/console/commands/tools.py +156 -31
  19. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3/praetorian_cli.egg-info}/PKG-INFO +1 -1
  20. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli.egg-info/SOURCES.txt +4 -0
  21. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/setup.cfg +1 -1
  22. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/LICENSE +0 -0
  23. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/MANIFEST.in +0 -0
  24. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/README.md +0 -0
  25. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/__init__.py +0 -0
  26. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/__init__.py +0 -0
  27. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/access.py +0 -0
  28. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/aegis.py +0 -0
  29. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/agent.py +0 -0
  30. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/chariot.py +0 -0
  31. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/cli_decorators.py +0 -0
  32. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/configure.py +0 -0
  33. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/critfinder.py +0 -0
  34. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/engagement.py +0 -0
  35. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/enrich.py +0 -0
  36. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/find.py +0 -0
  37. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/get.py +0 -0
  38. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/imports.py +0 -0
  39. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/link.py +0 -0
  40. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/list.py +0 -0
  41. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/report.py +0 -0
  42. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/script.py +0 -0
  43. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/search.py +0 -0
  44. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/ssh_utils.py +0 -0
  45. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/test.py +0 -0
  46. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/handlers/unlink.py +0 -0
  47. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/main.py +0 -0
  48. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/runners/__init__.py +0 -0
  49. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/scripts/__init__.py +0 -0
  50. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/scripts/commands/__init__.py +0 -0
  51. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/scripts/commands/nmap-example.py +0 -0
  52. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/scripts/utils.py +0 -0
  53. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/__init__.py +0 -0
  54. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/chariot.py +0 -0
  55. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/__init__.py +0 -0
  56. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/account_discovery.py +0 -0
  57. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/accounts.py +0 -0
  58. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/ad.py +0 -0
  59. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/aegis.py +0 -0
  60. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/agents.py +0 -0
  61. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/attributes.py +0 -0
  62. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/capabilities.py +0 -0
  63. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/configurations.py +0 -0
  64. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/definitions.py +0 -0
  65. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/files.py +0 -0
  66. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/integrations.py +0 -0
  67. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/jobs.py +0 -0
  68. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/keys.py +0 -0
  69. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/preseeds.py +0 -0
  70. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/risks.py +0 -0
  71. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/scanners.py +0 -0
  72. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/schedules.py +0 -0
  73. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/schema.py +0 -0
  74. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/search.py +0 -0
  75. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/seeds.py +0 -0
  76. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/settings.py +0 -0
  77. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/statistics.py +0 -0
  78. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/webhook.py +0 -0
  79. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/entities/webpage.py +0 -0
  80. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/guard.py +0 -0
  81. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/keychain.py +0 -0
  82. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/mcp_server.py +0 -0
  83. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/model/__init__.py +0 -0
  84. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/model/aegis.py +0 -0
  85. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/model/globals.py +0 -0
  86. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/model/query.py +0 -0
  87. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/model/utils.py +0 -0
  88. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/__init__.py +0 -0
  89. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/pytest.ini +0 -0
  90. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_access_aws.py +0 -0
  91. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_account.py +0 -0
  92. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_agent.py +0 -0
  93. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_asset.py +0 -0
  94. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_attribute.py +0 -0
  95. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_bulk.py +0 -0
  96. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_capabilities.py +0 -0
  97. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_configuration.py +0 -0
  98. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_conversation.py +0 -0
  99. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_credential_process.py +0 -0
  100. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_definition.py +0 -0
  101. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_extend.py +0 -0
  102. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_file.py +0 -0
  103. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_job.py +0 -0
  104. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_key.py +0 -0
  105. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_mcp.py +0 -0
  106. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_preseed.py +0 -0
  107. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_risk.py +0 -0
  108. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_search.py +0 -0
  109. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_seed.py +0 -0
  110. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_setting.py +0 -0
  111. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_webhook.py +0 -0
  112. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_webpage.py +0 -0
  113. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/test_z_cli.py +0 -0
  114. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/ui_mocks.py +0 -0
  115. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/sdk/test/utils.py +0 -0
  116. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/__init__.py +0 -0
  117. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/aegis/__init__.py +0 -0
  118. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/aegis/account_selector.py +0 -0
  119. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/aegis/commands/__init__.py +0 -0
  120. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/aegis/commands/cp.py +0 -0
  121. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/aegis/commands/help.py +0 -0
  122. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/aegis/commands/info.py +0 -0
  123. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/aegis/commands/job.py +0 -0
  124. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/aegis/commands/list.py +0 -0
  125. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/aegis/commands/proxy.py +0 -0
  126. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/aegis/commands/schedule.py +0 -0
  127. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/aegis/commands/schedule_helpers.py +0 -0
  128. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/aegis/commands/set.py +0 -0
  129. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/aegis/commands/ssh.py +0 -0
  130. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/aegis/constants.py +0 -0
  131. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/aegis/menu.py +0 -0
  132. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/aegis/theme.py +0 -0
  133. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/aegis/utils.py +0 -0
  134. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/console/__init__.py +0 -0
  135. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/console/commands/__init__.py +0 -0
  136. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/console/commands/accounts.py +0 -0
  137. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/console/commands/context.py +0 -0
  138. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/console/commands/data.py +0 -0
  139. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/console/commands/marcus.py +0 -0
  140. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/console/commands/reporting.py +0 -0
  141. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/console/commands/search.py +0 -0
  142. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/console/console.py +0 -0
  143. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/console/context.py +0 -0
  144. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/console/renderer.py +0 -0
  145. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/conversation/__init__.py +0 -0
  146. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli/ui/conversation/textual_chat.py +0 -0
  147. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli.egg-info/dependency_links.txt +0 -0
  148. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli.egg-info/entry_points.txt +0 -0
  149. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli.egg-info/requires.txt +0 -0
  150. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/praetorian_cli.egg-info/top_level.txt +0 -0
  151. {praetorian_cli-2.4.2 → praetorian_cli-2.4.3}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: praetorian-cli
3
- Version: 2.4.2
3
+ Version: 2.4.3
4
4
  Summary: For interacting with the Guard API
5
5
  Home-page: https://github.com/praetorian-inc/praetorian-cli
6
6
  Author: Praetorian
@@ -6,7 +6,7 @@ import click
6
6
 
7
7
  from praetorian_cli.handlers.chariot import chariot
8
8
  from praetorian_cli.handlers.cli_decorators import cli_handler, praetorian_only
9
- from praetorian_cli.handlers.utils import error, parse_configuration_value
9
+ from praetorian_cli.handlers.utils import error, parse_configuration_value, parse_kv_entries
10
10
  from praetorian_cli.sdk.model.globals import AddRisk, Asset, Seed, Kind
11
11
 
12
12
 
@@ -357,40 +357,71 @@ def webpage(sdk, url, parent):
357
357
  sdk.webpage.add(url, parent)
358
358
 
359
359
 
360
- @add.command()
360
+ @add.group(invoke_without_command=True)
361
361
  @cli_handler
362
- @click.option('-r', '--resource-key', required=True, help='The resource key for the credential (e.g., account key)')
363
- @click.option('-c', '--category', required=True,
362
+ @click.option('-r', '--resource-key', help='The resource key for the credential (e.g., account key)')
363
+ @click.option('-c', '--category',
364
364
  type=click.Choice(['integration', 'cloud', 'env-integration']),
365
365
  help='The category of the credential')
366
- @click.option('-t', '--type', 'cred_type', required=True,
367
- help='The type of credential (aws, gcp, azure, static, ssh_key, json, active-directory, default)')
368
- @click.option('-l', '--label', required=True, help='A human-readable label for the credential')
366
+ @click.option('-t', '--type', 'cred_type',
367
+ help='The type of credential (aws, gcp, azure, static-token, ssh-key, '
368
+ 'json-credential, active-directory, burp-authentication, web-auth, etc.)')
369
+ @click.option('-l', '--label', help='A human-readable label for the credential')
369
370
  @click.option('-p', '--param', 'parameters', multiple=True,
370
371
  help='Parameter in format key=value (can be specified multiple times)')
371
- def credential(sdk, resource_key, category, cred_type, label, parameters):
372
+ @click.pass_context
373
+ def credential(ctx, sdk, resource_key, category, cred_type, label, parameters):
372
374
  """ Add a credential
373
375
 
374
- This command adds a credential to the credential broker. Credentials can be used
375
- for authentication with various cloud providers, integrations, and environment services.
376
+ Without a subcommand, adds a credential generically you provide all of category,
377
+ type, label, and params. Use the typed subcommands (e.g. `webauth`) for friendlier
378
+ UX when adding a known credential type.
376
379
 
377
380
  \b
378
381
  Example usages:
379
382
  - guard add credential --resource-key "C.0c6cf7104f516b08-OGMPG" --category env-integration --type active-directory --label "Robb Stark" --param username=robb.stark --param password=sexywolfy --param domain=north.sevenkingdoms.local
380
383
  - guard add credential -r "C.example-key" -c cloud -t aws --label "AWS Production" -p region=us-east-1 -p role_arn=arn:aws:iam::123456789012:role/MyRole
381
- - guard add credential -r "C.example-key" -c integration -t static --label "API Token" -p token=abc123xyz
384
+ - guard add credential -r "C.example-key" -c integration -t static-token --label "API Token" -p token=abc123xyz
382
385
  """
383
- # Parse parameters from key=value format
384
- params = {}
385
- for param in parameters:
386
- if '=' not in param:
387
- error(f"Parameter '{param}' is not in the format key=value")
388
- return
389
- key, value = param.split('=', 1)
390
- params[key] = value
386
+ if ctx.invoked_subcommand is not None:
387
+ return
388
+
389
+ missing = [name for name, val in
390
+ [('--resource-key', resource_key), ('--category', category),
391
+ ('--type', cred_type), ('--label', label)] if not val]
392
+ if missing:
393
+ error(f"Missing required option(s): {', '.join(missing)}")
394
+
395
+ params = parse_kv_entries(parameters, label='Param')
391
396
 
392
397
  try:
393
398
  result = sdk.credentials.add(resource_key, category, cred_type, label, params)
394
399
  click.echo(json.dumps(result, indent=2))
395
400
  except Exception as e:
396
401
  error(f'Unable to add credential. Error: {e}')
402
+
403
+
404
+ @credential.command('webauth')
405
+ @cli_handler
406
+ @click.option('-k', '--resource-key', required=True,
407
+ help='Web-application key (e.g., #webapplication#https://app.example.com)')
408
+ @click.option('-l', '--label', required=True, help='A human-readable label for the credential')
409
+ @click.option('-H', '--header', 'headers', multiple=True, required=True,
410
+ help='Header in format Name=Value (can be specified multiple times)')
411
+ def credential_webauth(sdk, resource_key, label, headers):
412
+ """ Add a static-token web-auth credential to a WebApplication.
413
+
414
+ Persists a header bag (e.g. an Authorization bearer token) that Chariot
415
+ capabilities and Burp Enterprise will use when scanning the target.
416
+
417
+ \b
418
+ Example usage:
419
+ - guard add credential webauth -k "#webapplication#https://app.example.com" -l "Prod token" -H "Authorization=Bearer abc123" -H "X-Tenant=acme"
420
+ """
421
+ headers_dict = parse_kv_entries(headers, label='Header')
422
+ parameters = {'method': 'static-token', 'headers': headers_dict}
423
+ try:
424
+ result = sdk.credentials.add(resource_key, 'env-integration', 'web-auth', label, parameters)
425
+ click.echo(json.dumps(result, indent=2))
426
+ except Exception as e:
427
+ error(f'Unable to add web-auth credential. Error: {e}')
@@ -193,4 +193,61 @@ def webpage(chariot, key):
193
193
  Arguments:
194
194
  - KEY: the key of an existing webpage
195
195
  """
196
- chariot.webpage.delete(key)
196
+ chariot.webpage.delete(key)
197
+
198
+
199
+ @delete.group()
200
+ def credential():
201
+ """ Delete a credential via the broker
202
+
203
+ For web-auth credentials shared across multiple WebApplications, this only
204
+ detaches the credential from the given resource_key — the underlying
205
+ credential is fully removed once it has no remaining attachments.
206
+
207
+ Use the typed subcommands (e.g. `webauth`) for friendlier UX, or `generic`
208
+ when deleting a credential type that doesn't have a typed subcommand yet.
209
+ """
210
+ pass
211
+
212
+
213
+ @credential.command('generic')
214
+ @cli_handler
215
+ @click.argument('credential_id', required=True)
216
+ @click.option('-r', '--resource-key', required=True,
217
+ help='The resource key the credential is attached to (e.g., web-application key, account key)')
218
+ @click.option('-t', '--type', 'cred_type', required=True,
219
+ help='The credential type (e.g., active-directory, burp-authentication)')
220
+ def credential_generic(chariot, credential_id, resource_key, cred_type):
221
+ """ Delete a credential of any type (escape hatch).
222
+
223
+ \b
224
+ Arguments:
225
+ - CREDENTIAL_ID: the ID of the credential to delete
226
+
227
+ \b
228
+ Example usage:
229
+ - guard delete credential generic def-456 -r "#asset#example.com#1.2.3.4" -t active-directory
230
+ """
231
+ chariot.credentials.delete(credential_id, resource_key, cred_type)
232
+
233
+
234
+ @credential.command('webauth')
235
+ @cli_handler
236
+ @click.argument('credential_id', required=True)
237
+ @click.option('-k', '--resource-key', required=True,
238
+ help='Web-application key (e.g., #webapplication#https://app.example.com)')
239
+ def credential_webauth(chariot, credential_id, resource_key):
240
+ """ Delete a web-auth credential from a WebApplication.
241
+
242
+ Detaches the credential from this WebApplication; the underlying credential
243
+ is fully removed once it has no remaining attachments.
244
+
245
+ \b
246
+ Arguments:
247
+ - CREDENTIAL_ID: the ID of the web-auth credential to delete
248
+
249
+ \b
250
+ Example usage:
251
+ - guard delete credential webauth abc-123 -k "#webapplication#https://app.example.com"
252
+ """
253
+ chariot.credentials.delete(credential_id, resource_key, 'web-auth')
@@ -28,6 +28,9 @@ def export():
28
28
  @click.option('--retest/--no-retest', default=False,
29
29
  help='Include retest status badges and sections')
30
30
  @click.option('--version', 'report_version', default='1.0', help='Report version string')
31
+ @click.option('--sow', default='', help='Statement of Work identifier (expands %%SOW%% shortcode).')
32
+ @click.option('--footer', default='', help='Custom page-footer text (overrides the report title when set).')
33
+ @click.option('--confidential-label', 'confidential_label', default='', help='Confidentiality label shown on cover, header, and footer (defaults to "Confidential").')
31
34
  @click.option('--format', 'export_format', type=click.Choice(['pdf', 'zip']),
32
35
  default='pdf', help='Export format')
33
36
  @click.option('--group-by', type=click.Choice(['attack_surface', 'tag']),
@@ -48,7 +51,7 @@ def export():
48
51
  help='Skip downloading the file; just print the job result')
49
52
  def report(chariot, title, client_name, status_filter, risk_keys,
50
53
  target, start_date, end_date, report_date, draft, retest,
51
- report_version, export_format, group_by, shared,
54
+ report_version, sow, footer, confidential_label, export_format, group_by, shared,
52
55
  executive_summary, narratives, appendix, output,
53
56
  timeout, no_download):
54
57
  """ Generate and download a PDF or ZIP report.
@@ -84,6 +87,9 @@ def report(chariot, title, client_name, status_filter, risk_keys,
84
87
  draft=draft,
85
88
  retest=retest,
86
89
  version=report_version,
90
+ sow=sow,
91
+ footer=footer,
92
+ confidential_label=confidential_label,
87
93
  export_format=export_format,
88
94
  group_by=group_by,
89
95
  shared_output=shared,
@@ -124,27 +124,34 @@ def run():
124
124
  pass
125
125
 
126
126
 
127
- @run.command('tool')
127
+ @run.command(
128
+ 'tool',
129
+ context_settings={'ignore_unknown_options': True, 'allow_extra_args': True},
130
+ )
128
131
  @cli_handler
129
132
  @click.argument('tool_name')
130
133
  @click.argument('target')
134
+ @click.argument('tool_args', nargs=-1, type=click.UNPROCESSED)
131
135
  @click.option('-c', '--config', 'extra_config', default='', help='Extra JSON config to merge')
132
136
  @click.option('--credential', multiple=True, help='Credential ID(s) to use')
133
137
  @click.option('--wait', is_flag=True, default=False, help='Wait for job completion and show results')
134
138
  @click.option('--ask', 'use_agent', is_flag=True, default=False, help='Run via Marcus AI agent instead of direct job')
135
139
  @click.option('--local', is_flag=True, default=False, help='Run locally using installed binary')
136
140
  @click.option('--remote', is_flag=True, default=False, help='Force remote job execution on Guard backend')
137
- def tool(sdk, tool_name, target, extra_config, credential, wait, use_agent, local, remote):
141
+ def tool(sdk, tool_name, target, tool_args, extra_config, credential, wait, use_agent, local, remote):
138
142
  """ Execute a named security tool against a target
139
143
 
140
144
  By default, runs LOCALLY if the binary is installed, otherwise schedules
141
- a remote job. Use --local or --remote to force.
145
+ a remote job. Use --local or --remote to force. Any additional arguments
146
+ after the target are forwarded to the local binary. Use `--` to pass
147
+ flags that collide with our own options.
142
148
 
143
149
  \b
144
150
  Example usages:
145
- guard run tool brutus 10.0.1.5
146
- guard run tool nuclei example.com --remote
147
- guard run tool titus github.com/org/repo
151
+ guard run tool brutus 10.0.1.5:22
152
+ guard run tool brutus 10.0.1.5:22 --protocol ssh -U users.txt
153
+ guard run tool brutus 10.0.1.5:22 -- --wait
154
+ guard run tool nuclei example.com --remote -c '{"templates":"cves/"}'
148
155
  """
149
156
  from praetorian_cli.runners.local import is_installed as _is_installed
150
157
 
@@ -152,11 +159,26 @@ def tool(sdk, tool_name, target, extra_config, credential, wait, use_agent, loca
152
159
  if not cap:
153
160
  error(f'Unknown capability: {tool_name}. Use "guard run capabilities" to see available capabilities.')
154
161
 
155
- # Decide local vs remote
162
+ # --local / --remote / --ask are mutually exclusive routing flags.
163
+ mode_flags = [('--local', local), ('--remote', remote), ('--ask', use_agent)]
164
+ set_flags = [name for name, v in mode_flags if v]
165
+ if len(set_flags) > 1:
166
+ error(f'Mutually exclusive flags: {", ".join(set_flags)}. Choose one.')
167
+
168
+ tool_args = list(tool_args or [])
169
+
170
+ # Decide local vs remote first so we can validate tool_args against the resolved path.
156
171
  run_local = local or (not remote and not use_agent and _is_installed(tool_name.lower()))
157
172
 
173
+ if tool_args and (remote or use_agent or not run_local):
174
+ error(
175
+ 'Extra arguments after the target are only supported when running locally. '
176
+ 'Install the tool locally ("guard run install %s") or encode settings as JSON '
177
+ 'via -c \'{"key":"value"}\'.' % tool_name.lower()
178
+ )
179
+
158
180
  if run_local:
159
- _run_local(sdk, tool_name.lower(), target, extra_config)
181
+ _run_local(sdk, tool_name.lower(), target, extra_config, tool_args)
160
182
  elif use_agent:
161
183
  target_key, warning = resolve_target(sdk, target, cap['target_type'])
162
184
  if not target_key:
@@ -376,7 +398,7 @@ def _run_via_agent(sdk, cap, target_key):
376
398
  error(str(e))
377
399
 
378
400
 
379
- def _run_local(sdk, tool_name, target, extra_config):
401
+ def _run_local(sdk, tool_name, target, extra_config, tool_args=None):
380
402
  """Run a tool locally using the installed binary."""
381
403
  from praetorian_cli.runners.local import LocalRunner, get_tool_plugin
382
404
 
@@ -392,7 +414,7 @@ def _run_local(sdk, tool_name, target, extra_config):
392
414
  raw_target = parts[-1] if len(parts) > 3 else parts[2] if len(parts) > 2 else target
393
415
 
394
416
  plugin = get_tool_plugin(tool_name)
395
- args = plugin.build_args(raw_target, extra_config)
417
+ args = plugin.build_args(raw_target, extra_config, pass_through=list(tool_args or []))
396
418
 
397
419
  click.echo(f'Running {tool_name} locally against {raw_target}...')
398
420
  click.echo(f'Command: {tool_name} {" ".join(args)}')
@@ -408,6 +430,10 @@ def _run_local(sdk, tool_name, target, extra_config):
408
430
  proc.wait(timeout=600)
409
431
  except subprocess.TimeoutExpired:
410
432
  proc.kill()
433
+ try:
434
+ proc.wait(timeout=5)
435
+ except Exception:
436
+ pass
411
437
  click.echo('\nTimed out (10 min).', err=True)
412
438
 
413
439
  stderr = proc.stderr.read() if proc.stderr else ''
@@ -16,8 +16,10 @@ def update():
16
16
  @click.argument('key', required=True)
17
17
  @click.option('-s', '--status', type=click.Choice([s.value for s in Asset]), help='The status of the asset')
18
18
  @click.option('-f', '--surface', required=False, default='', help=f'Attack surface of the asset', show_default=False)
19
- def asset(chariot, key, status, surface):
20
- """ Update the status or surface of an asset
19
+ @click.option('--secret', default=None,
20
+ help='For WebApplication assets, the credential ID to set as the default credential')
21
+ def asset(chariot, key, status, surface, secret):
22
+ """ Update the status, surface, or default credential of an asset
21
23
 
22
24
  \b
23
25
  Argument:
@@ -27,8 +29,9 @@ def asset(chariot, key, status, surface):
27
29
  Example usages:
28
30
  - guard update asset "#asset#www.example.com#1.2.3.4" -s F
29
31
  - guard update asset "#asset#www.example.com#1.2.3.4" -f internal
32
+ - guard update asset "#webapplication#https://app.example.com" --secret abc-123
30
33
  """
31
- chariot.assets.update(key, status, surface)
34
+ chariot.assets.update(key, status, surface, secret=secret)
32
35
 
33
36
 
34
37
  @update.command()
@@ -17,7 +17,7 @@ def parse_configuration_value(
17
17
  error('--entry cannot be combined with --string, --integer, or --float')
18
18
 
19
19
  if has_entries:
20
- return _parse_entry_dict(entries)
20
+ return parse_kv_entries(entries)
21
21
 
22
22
  provided = [(name, value) for name, value in typed_values.items() if value is not None]
23
23
 
@@ -30,27 +30,29 @@ def parse_configuration_value(
30
30
  return _cast_typed_value(value_type, raw_value)
31
31
 
32
32
 
33
- def _parse_entry_dict(entries):
34
- parsed = {}
33
+ def parse_kv_entries(entries, label='Entry'):
34
+ """Parse an iterable of 'key=value' strings into a dict.
35
+
36
+ Splits on the first '=' so values may themselves contain '=' (e.g.
37
+ base64-padded tokens, cookies). Calls error() (exits) on bad input.
35
38
 
39
+ :param entries: iterable of 'key=value' strings
40
+ :param label: human-readable label for the entry kind (used in error messages,
41
+ e.g. 'Header', '--param')
42
+ """
43
+ parsed = {}
36
44
  for item in entries:
37
- key, value = _split_entry(item)
45
+ if '=' not in item:
46
+ error(f"{label} '{item}' is not in the format key=value")
47
+ key, value = item.split('=', 1)
38
48
  if not key:
39
- error(f'Key cannot be empty: {item}')
49
+ error(f'{label} key cannot be empty: {item}')
40
50
  if not value:
41
- error(f'Value cannot be empty: {item}')
51
+ error(f'{label} value cannot be empty: {item}')
42
52
  parsed[key] = value
43
53
  return parsed
44
54
 
45
55
 
46
- def _split_entry(item):
47
- if '=' not in item:
48
- error(f"Entry '{item}' is not in the format key=value")
49
- if item.count('=') > 1:
50
- error(f"Entry '{item}' contains multiple '=' characters. Format should be key=value")
51
- return item.split('=', 1)
52
-
53
-
54
56
  def _cast_typed_value(value_type, raw_value):
55
57
  try:
56
58
  if value_type == 'integer':
@@ -34,6 +34,45 @@ INSTALLABLE_TOOLS = {
34
34
  }
35
35
 
36
36
 
37
+ # Well-known service ports for Brutus protocol auto-detection.
38
+ # Keep this minimal: only protocols Brutus natively supports.
39
+ _WELL_KNOWN_PORTS = {
40
+ 22: 'ssh',
41
+ 3389: 'rdp',
42
+ 21: 'ftp',
43
+ 445: 'smb',
44
+ 23: 'telnet',
45
+ 3306: 'mysql',
46
+ 5432: 'postgres',
47
+ }
48
+
49
+
50
+ def _infer_protocol(target: str):
51
+ """Infer protocol from a 'host:port' target using well-known ports.
52
+
53
+ Returns the protocol name or None if no inference is possible.
54
+ """
55
+ if not target or ':' not in target:
56
+ return None
57
+ # rsplit so 'host:port' works even if host contains ':'
58
+ _, sep, port_str = target.rpartition(':')
59
+ if not sep:
60
+ return None
61
+ try:
62
+ port = int(port_str)
63
+ except ValueError:
64
+ return None
65
+ return _WELL_KNOWN_PORTS.get(port)
66
+
67
+
68
+ def _has_flag(pass_through, *flags):
69
+ """Return True if any of the given flag names appears in pass_through."""
70
+ if not pass_through:
71
+ return False
72
+ flag_set = set(flags)
73
+ return any(arg in flag_set for arg in pass_through)
74
+
75
+
37
76
  def _detect_platform():
38
77
  """Detect OS and architecture for binary download."""
39
78
  system = platform.system().lower()
@@ -191,88 +230,134 @@ class LocalRunner:
191
230
  class ToolPlugin:
192
231
  """Base class for local tool argument builders."""
193
232
 
194
- def build_args(self, target, extra_config=''):
233
+ def build_args(self, target, extra_config='', pass_through=None):
195
234
  config = {}
196
235
  if extra_config:
197
236
  try:
198
237
  config = json.loads(extra_config) if isinstance(extra_config, str) else extra_config
199
238
  except (json.JSONDecodeError, TypeError):
200
239
  pass
201
- return self._build(target, config)
240
+ args = self._build(target, config, pass_through=pass_through)
241
+ return args
202
242
 
203
- def _build(self, target, config):
204
- return [target]
243
+ def _build(self, target, config, pass_through=None):
244
+ args = [target]
245
+ if pass_through:
246
+ args.extend(pass_through)
247
+ return args
205
248
 
206
249
 
207
250
  class BrutusPlugin(ToolPlugin):
208
- def _build(self, target, config):
209
- args = ['-t', target]
210
- if config.get('usernames'):
251
+ def _build(self, target, config, pass_through=None):
252
+ args = ['--target', target]
253
+
254
+ # Protocol precedence: caller passthrough (silent) > config['protocol'] > inferred from port
255
+ caller_has_protocol = _has_flag(pass_through, '--protocol')
256
+ if not caller_has_protocol:
257
+ proto = config.get('protocol') or _infer_protocol(target)
258
+ if proto:
259
+ args.extend(['--protocol', proto])
260
+
261
+ if config.get('usernames') and not _has_flag(pass_through, '-u', '-U'):
211
262
  args.extend(['-u', config['usernames']])
212
- if config.get('passwords'):
263
+ if config.get('passwords') and not _has_flag(pass_through, '-p', '-P'):
213
264
  args.extend(['-p', config['passwords']])
265
+
266
+ if pass_through:
267
+ args.extend(pass_through)
214
268
  return args
215
269
 
216
270
 
217
271
  class NucleiPlugin(ToolPlugin):
218
- def _build(self, target, config):
272
+ def _build(self, target, config, pass_through=None):
219
273
  args = ['-u', target, '-jsonl']
220
274
  if config.get('templates'):
221
275
  args.extend(['-t', config['templates']])
276
+ if pass_through:
277
+ args.extend(pass_through)
222
278
  return args
223
279
 
224
280
 
225
281
  class TitusPlugin(ToolPlugin):
226
- def _build(self, target, config):
282
+ def _build(self, target, config, pass_through=None):
227
283
  args = ['scan', target]
228
284
  if config.get('validation') == 'true':
229
285
  args.append('--validate')
286
+ if pass_through:
287
+ args.extend(pass_through)
230
288
  return args
231
289
 
232
290
 
233
291
  class TrajanPlugin(ToolPlugin):
234
- def _build(self, target, config):
292
+ def _build(self, target, config, pass_through=None):
235
293
  args = ['scan', target]
236
294
  if config.get('token'):
237
295
  args.extend(['--token', config['token']])
296
+ if pass_through:
297
+ args.extend(pass_through)
238
298
  return args
239
299
 
240
300
 
241
301
  class JuliusPlugin(ToolPlugin):
242
- def _build(self, target, config):
243
- return ['-t', target]
302
+ def _build(self, target, config, pass_through=None):
303
+ args = ['-t', target]
304
+ if pass_through:
305
+ args.extend(pass_through)
306
+ return args
244
307
 
245
308
 
246
309
  class AugustusPlugin(ToolPlugin):
247
- def _build(self, target, config):
248
- return ['scan', '-t', target]
310
+ def _build(self, target, config, pass_through=None):
311
+ args = ['scan', '-t', target]
312
+ if pass_through:
313
+ args.extend(pass_through)
314
+ return args
249
315
 
250
316
 
251
317
  class NervaPlugin(ToolPlugin):
252
- def _build(self, target, config):
253
- return ['-t', target]
318
+ def _build(self, target, config, pass_through=None):
319
+ args = ['-t', target]
320
+ if pass_through:
321
+ args.extend(pass_through)
322
+ return args
254
323
 
255
324
 
256
325
  class GatoPlugin(ToolPlugin):
257
- def _build(self, target, config):
326
+ def _build(self, target, config, pass_through=None):
258
327
  args = ['enumerate', '-t', target]
259
328
  if config.get('token'):
260
329
  args.extend(['--token', config['token']])
330
+ if pass_through:
331
+ args.extend(pass_through)
261
332
  return args
262
333
 
263
334
 
264
335
  class UrlTargetPlugin(ToolPlugin):
265
336
  """For tools that take scan -u <target>."""
266
- def _build(self, target, config):
267
- return ['scan', '-u', target]
337
+ def _build(self, target, config, pass_through=None):
338
+ args = ['scan', '-u', target]
339
+ if pass_through:
340
+ args.extend(pass_through)
341
+ return args
268
342
 
269
343
 
270
344
  class ScanTargetPlugin(ToolPlugin):
271
345
  """For tools that take scan <target>."""
272
- def _build(self, target, config):
273
- return ['scan', target]
346
+ def _build(self, target, config, pass_through=None):
347
+ args = ['scan', target]
348
+ if pass_through:
349
+ args.extend(pass_through)
350
+ return args
274
351
 
275
352
 
353
+ # Plugin verification status:
354
+ # - brutus: verified against brutus --help (ENG-3042)
355
+ # - nuclei: -u is the documented URL flag — OK
356
+ # - julius/nerva/nero: use -t <target>; unverified against each binary's --help
357
+ # - titus/trajan/vespasian/constantine/caligula: `scan <target>` — unverified
358
+ # - augustus/gato: `scan -t <target>` / `enumerate -t <target>` — unverified
359
+ # - cato/florian/hadrian: `scan -u <target>` — unverified
360
+ # Users can always override via `guard run tool <tool> <target> -- <raw args>`.
276
361
  TOOL_PLUGINS = {
277
362
  'brutus': BrutusPlugin(),
278
363
  'nuclei': NucleiPlugin(),
@@ -50,7 +50,7 @@ class Assets:
50
50
  asset['associated_risks'] = self.associated_risks(key)
51
51
  return asset
52
52
 
53
- def update(self, key, status=None, surface=None):
53
+ def update(self, key, status=None, surface=None, secret=None):
54
54
  """
55
55
  Update an asset.
56
56
 
@@ -60,6 +60,9 @@ class Assets:
60
60
  :type status: str or None
61
61
  :param surface: Attack surface classification (e.g., 'internal', 'external'), if None surface is not updated
62
62
  :type surface: str or None
63
+ :param secret: For WebApplication assets, the credential ID to set as the default credential.
64
+ If None, the secret is not updated.
65
+ :type secret: str or None
63
66
  :return: None
64
67
  :rtype: None
65
68
  """
@@ -68,7 +71,9 @@ class Assets:
68
71
  params = params | dict(status=status)
69
72
  if surface:
70
73
  params = params | dict(attackSurface=[surface])
71
-
74
+ if secret is not None:
75
+ params = params | dict(secret=secret)
76
+
72
77
  return self.api.upsert('asset', params)
73
78
 
74
79
  def delete(self, key):
@@ -14,15 +14,17 @@ class Credentials:
14
14
  """
15
15
  Add a new credential to the credential broker.
16
16
 
17
- :param resource_key: The resource key for the credential (e.g., account key)
17
+ :param resource_key: The resource key for the credential (e.g., account key, web-application key)
18
18
  :type resource_key: str
19
19
  :param category: The category of the credential ('integration', 'cloud', 'env-integration')
20
20
  :type category: str
21
- :param type: The type of credential ('aws', 'gcp', 'azure', 'static', 'ssh_key', 'json', 'active-directory', 'default')
21
+ :param type: The type of credential ('aws', 'gcp', 'azure', 'static-token', 'ssh-key',
22
+ 'json-credential', 'active-directory', 'burp-authentication', 'web-auth', etc.)
22
23
  :type type: str
23
24
  :param label: A human-readable label for the credential
24
25
  :type label: str
25
- :param parameters: Additional parameters for the credential (e.g., username, password, domain)
26
+ :param parameters: Additional parameters for the credential (e.g., username, password, domain,
27
+ or for web-auth: method + headers dict)
26
28
  :type parameters: dict
27
29
  :return: The response from the broker API
28
30
  :rtype: dict
@@ -36,6 +38,27 @@ class Credentials:
36
38
  }
37
39
  return self.api.post('broker', request)
38
40
 
41
+ def delete(self, credential_id, resource_key, type):
42
+ """
43
+ Delete a credential via the credential broker.
44
+
45
+ :param credential_id: The ID of the credential to delete
46
+ :type credential_id: str
47
+ :param resource_key: The resource key the credential is attached to
48
+ (e.g., account key, web-application key)
49
+ :type resource_key: str
50
+ :param type: The credential type (e.g., 'web-auth', 'active-directory', 'burp-authentication')
51
+ :type type: str
52
+ :return: The response from the broker API
53
+ :rtype: dict
54
+ """
55
+ request = {
56
+ 'CredentialID': credential_id,
57
+ 'ResourceKey': resource_key,
58
+ 'Type': type,
59
+ }
60
+ return self.api.delete('broker', request, params={})
61
+
39
62
  def list(self, offset=None, pages=100000):
40
63
  """
41
64
  List credentials available to the current principal.