praetorian-cli 2.4.0__tar.gz → 2.4.1__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.
- {praetorian_cli-2.4.0/praetorian_cli.egg-info → praetorian_cli-2.4.1}/PKG-INFO +1 -1
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/export.py +4 -1
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/run.py +96 -12
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/runners/local.py +4 -1
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/reports.py +5 -1
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_report.py +3 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/console/commands/search.py +36 -15
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/console/console.py +13 -4
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/console/renderer.py +151 -6
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1/praetorian_cli.egg-info}/PKG-INFO +1 -1
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/setup.cfg +1 -1
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/LICENSE +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/MANIFEST.in +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/README.md +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/__init__.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/__init__.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/add.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/aegis.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/agent.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/chariot.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/cli_decorators.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/configure.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/critfinder.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/delete.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/engagement.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/enrich.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/find.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/get.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/imports.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/link.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/list.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/report.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/script.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/search.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/ssh_utils.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/test.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/unlink.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/update.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/handlers/utils.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/main.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/runners/__init__.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/scripts/__init__.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/scripts/commands/__init__.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/scripts/commands/nmap-example.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/scripts/utils.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/__init__.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/chariot.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/__init__.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/accounts.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/ad.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/aegis.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/agents.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/assets.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/attributes.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/capabilities.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/configurations.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/credentials.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/definitions.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/files.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/integrations.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/jobs.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/keys.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/preseeds.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/risks.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/scanners.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/schedules.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/schema.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/search.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/seeds.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/settings.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/statistics.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/webhook.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/entities/webpage.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/guard.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/keychain.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/mcp_server.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/model/__init__.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/model/aegis.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/model/globals.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/model/query.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/model/utils.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/__init__.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/pytest.ini +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_account.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_agent.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_asset.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_attribute.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_capabilities.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_configuration.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_conversation.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_definition.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_extend.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_file.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_job.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_key.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_mcp.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_preseed.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_risk.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_search.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_seed.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_setting.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_webhook.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_webpage.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/test_z_cli.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/ui_mocks.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/sdk/test/utils.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/__init__.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/aegis/__init__.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/aegis/commands/__init__.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/aegis/commands/cp.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/aegis/commands/help.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/aegis/commands/info.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/aegis/commands/job.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/aegis/commands/job_helpers.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/aegis/commands/list.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/aegis/commands/proxy.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/aegis/commands/schedule.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/aegis/commands/schedule_helpers.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/aegis/commands/set.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/aegis/commands/ssh.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/aegis/constants.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/aegis/menu.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/aegis/theme.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/aegis/utils.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/console/__init__.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/console/commands/__init__.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/console/commands/accounts.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/console/commands/context.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/console/commands/marcus.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/console/commands/reporting.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/console/commands/tools.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/console/context.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/conversation/__init__.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/conversation/textual_chat.py +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli.egg-info/SOURCES.txt +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli.egg-info/dependency_links.txt +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli.egg-info/entry_points.txt +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli.egg-info/requires.txt +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli.egg-info/top_level.txt +0 -0
- {praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/pyproject.toml +0 -0
|
@@ -25,6 +25,8 @@ def export():
|
|
|
25
25
|
@click.option('--end-date', default='', help='Engagement end date (ISO format)')
|
|
26
26
|
@click.option('--report-date', default='', help='Report date (ISO format). Default: today')
|
|
27
27
|
@click.option('--draft/--no-draft', default=False, help='Add DRAFT watermark')
|
|
28
|
+
@click.option('--retest/--no-retest', default=False,
|
|
29
|
+
help='Include retest status badges and sections')
|
|
28
30
|
@click.option('--version', 'report_version', default='1.0', help='Report version string')
|
|
29
31
|
@click.option('--format', 'export_format', type=click.Choice(['pdf', 'zip']),
|
|
30
32
|
default='pdf', help='Export format')
|
|
@@ -45,7 +47,7 @@ def export():
|
|
|
45
47
|
@click.option('--no-download', is_flag=True, default=False,
|
|
46
48
|
help='Skip downloading the file; just print the job result')
|
|
47
49
|
def report(chariot, title, client_name, status_filter, risk_keys,
|
|
48
|
-
target, start_date, end_date, report_date, draft,
|
|
50
|
+
target, start_date, end_date, report_date, draft, retest,
|
|
49
51
|
report_version, export_format, group_by, shared,
|
|
50
52
|
executive_summary, narratives, appendix, output,
|
|
51
53
|
timeout, no_download):
|
|
@@ -80,6 +82,7 @@ def report(chariot, title, client_name, status_filter, risk_keys,
|
|
|
80
82
|
end_date=end_date,
|
|
81
83
|
report_date=report_date,
|
|
82
84
|
draft=draft,
|
|
85
|
+
retest=retest,
|
|
83
86
|
version=report_version,
|
|
84
87
|
export_format=export_format,
|
|
85
88
|
group_by=group_by,
|
|
@@ -175,26 +175,109 @@ def tool(sdk, tool_name, target, extra_config, credential, wait, use_agent, loca
|
|
|
175
175
|
|
|
176
176
|
@run.command('list')
|
|
177
177
|
@cli_handler
|
|
178
|
-
|
|
179
|
-
|
|
178
|
+
@click.option('-s', '--surface', default='', help='Filter by surface (external, internal, cloud, repository, application)')
|
|
179
|
+
@click.option('-e', '--executor', default='', help='Filter by executor (chariot, janus, aegis, aurelian, agent)')
|
|
180
|
+
@click.option('--json', 'as_json', is_flag=True, default=False, help='Output as JSON')
|
|
181
|
+
def list_tools(sdk, surface, executor, as_json):
|
|
182
|
+
""" List all available tools and capabilities
|
|
180
183
|
|
|
181
184
|
\b
|
|
182
|
-
|
|
185
|
+
Shows all Guard capabilities with descriptions, indicating which can
|
|
186
|
+
be installed locally. Use --surface or --executor to filter.
|
|
187
|
+
|
|
188
|
+
\b
|
|
189
|
+
Example usages:
|
|
183
190
|
guard run list
|
|
191
|
+
guard run list --surface external
|
|
192
|
+
guard run list --executor agent
|
|
193
|
+
guard run list --json
|
|
184
194
|
"""
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
195
|
+
from praetorian_cli.runners.local import INSTALLABLE_TOOLS, list_installed
|
|
196
|
+
|
|
197
|
+
try:
|
|
198
|
+
result = sdk.capabilities.list()
|
|
199
|
+
caps = result[0] if isinstance(result, tuple) else result
|
|
200
|
+
if not isinstance(caps, list):
|
|
201
|
+
caps = []
|
|
202
|
+
except Exception:
|
|
203
|
+
caps = []
|
|
204
|
+
|
|
205
|
+
if not caps:
|
|
206
|
+
click.echo('Could not fetch capabilities from backend.', err=True)
|
|
207
|
+
click.echo(f'\nLocally installable tools:')
|
|
208
|
+
for name, info in sorted(INSTALLABLE_TOOLS.items()):
|
|
209
|
+
click.echo(f' {name:<18} {info["description"]}')
|
|
210
|
+
return
|
|
211
|
+
|
|
212
|
+
if as_json:
|
|
213
|
+
print_json(caps)
|
|
214
|
+
return
|
|
215
|
+
|
|
216
|
+
installed = list_installed()
|
|
217
|
+
|
|
218
|
+
# Apply filters
|
|
219
|
+
if surface:
|
|
220
|
+
caps = [c for c in caps if c.get('surface', '') == surface]
|
|
221
|
+
if executor:
|
|
222
|
+
caps = [c for c in caps if c.get('executor', '') == executor]
|
|
223
|
+
|
|
224
|
+
if not caps:
|
|
225
|
+
click.echo('No capabilities match the filters.')
|
|
226
|
+
return
|
|
227
|
+
|
|
228
|
+
# Sort: installable first, then by name
|
|
229
|
+
def sort_key(c):
|
|
230
|
+
name = c.get('name', '')
|
|
231
|
+
is_installable = name in INSTALLABLE_TOOLS
|
|
232
|
+
return (not is_installable, name)
|
|
233
|
+
|
|
234
|
+
caps.sort(key=sort_key)
|
|
235
|
+
|
|
236
|
+
# Render table
|
|
237
|
+
click.echo(f'\n{"Name":<22} {"Type":<10} {"Surface":<12} {"Description"}')
|
|
238
|
+
click.echo(f'{"─"*22} {"─"*10} {"─"*12} {"─"*48}')
|
|
239
|
+
|
|
240
|
+
for c in caps:
|
|
241
|
+
name = c.get('name', '')
|
|
242
|
+
desc = c.get('description', '') or c.get('title', '')
|
|
243
|
+
cap_surface = c.get('surface', '')
|
|
244
|
+
cap_executor = c.get('executor', '')
|
|
245
|
+
|
|
246
|
+
# Determine type label
|
|
247
|
+
if name in INSTALLABLE_TOOLS:
|
|
248
|
+
if name in installed:
|
|
249
|
+
type_label = 'local ✓'
|
|
250
|
+
else:
|
|
251
|
+
type_label = 'local'
|
|
252
|
+
elif cap_executor == 'agent':
|
|
253
|
+
type_label = 'agent'
|
|
254
|
+
else:
|
|
255
|
+
type_label = 'remote'
|
|
256
|
+
|
|
257
|
+
# Truncate description
|
|
258
|
+
max_desc = 55
|
|
259
|
+
if len(desc) > max_desc:
|
|
260
|
+
desc = desc[:max_desc - 1] + '…'
|
|
261
|
+
|
|
262
|
+
click.echo(f'{name:<22} {type_label:<10} {cap_surface:<12} {desc}')
|
|
263
|
+
|
|
264
|
+
click.echo(f'\n{len(caps)} capabilities')
|
|
265
|
+
installable_count = sum(1 for c in caps if c.get('name', '') in INSTALLABLE_TOOLS)
|
|
266
|
+
installed_count = sum(1 for c in caps if c.get('name', '') in installed)
|
|
267
|
+
if installable_count:
|
|
268
|
+
click.echo(f'{installable_count} installable locally ({installed_count} installed) — use "guard run install <name>"')
|
|
190
269
|
|
|
191
270
|
|
|
192
271
|
@run.command('capabilities')
|
|
193
272
|
@cli_handler
|
|
194
273
|
@click.option('-n', '--name', default='', help='Filter by capability name')
|
|
195
274
|
@click.option('-t', '--target', default='', help='Filter by target type')
|
|
196
|
-
|
|
197
|
-
|
|
275
|
+
@click.option('-e', '--executor', default='', help='Filter by executor')
|
|
276
|
+
def capabilities(sdk, name, target, executor):
|
|
277
|
+
""" List all capabilities as JSON (for scripting)
|
|
278
|
+
|
|
279
|
+
\b
|
|
280
|
+
Full machine-readable output. For a human-readable view, use "guard run list".
|
|
198
281
|
|
|
199
282
|
\b
|
|
200
283
|
Example usages:
|
|
@@ -202,8 +285,9 @@ def capabilities(sdk, name, target):
|
|
|
202
285
|
guard run capabilities --name nuclei
|
|
203
286
|
guard run capabilities --target asset
|
|
204
287
|
"""
|
|
205
|
-
result = sdk.capabilities.list(name=name, target=target)
|
|
206
|
-
|
|
288
|
+
result = sdk.capabilities.list(name=name, target=target, executor=executor)
|
|
289
|
+
caps = result[0] if isinstance(result, tuple) else result
|
|
290
|
+
print_json(caps)
|
|
207
291
|
|
|
208
292
|
|
|
209
293
|
@run.command('install')
|
|
@@ -71,7 +71,10 @@ def is_installed(tool_name: str) -> bool:
|
|
|
71
71
|
def install_tool(tool_name: str, force=False) -> str:
|
|
72
72
|
"""Download and install a tool from GitHub releases. Returns binary path."""
|
|
73
73
|
if tool_name not in INSTALLABLE_TOOLS:
|
|
74
|
-
|
|
74
|
+
lines = [f'Unknown tool: {tool_name}. Installable tools:']
|
|
75
|
+
for name in sorted(INSTALLABLE_TOOLS):
|
|
76
|
+
lines.append(f' {name:<18} {INSTALLABLE_TOOLS[name]["description"]}')
|
|
77
|
+
raise ValueError('\n'.join(lines))
|
|
75
78
|
|
|
76
79
|
if not force and is_installed(tool_name):
|
|
77
80
|
return get_binary_path(tool_name)
|
|
@@ -35,7 +35,8 @@ class Reports:
|
|
|
35
35
|
def build_export_body(self, title, client_name, customer_email,
|
|
36
36
|
status_filter=('O', 'T'), risk_keys=(),
|
|
37
37
|
target='', start_date='', end_date='',
|
|
38
|
-
report_date='', draft=False,
|
|
38
|
+
report_date='', draft=False, retest=False,
|
|
39
|
+
version='1.0',
|
|
39
40
|
export_format='pdf', group_by='attack_surface',
|
|
40
41
|
shared_output=False, executive_summary_path='',
|
|
41
42
|
narratives_path='', appendix_path=''):
|
|
@@ -62,6 +63,8 @@ class Reports:
|
|
|
62
63
|
:type report_date: str
|
|
63
64
|
:param draft: Whether to add DRAFT watermark
|
|
64
65
|
:type draft: bool
|
|
66
|
+
:param retest: Whether to include retest status badges and sections
|
|
67
|
+
:type retest: bool
|
|
65
68
|
:param version: Report version string
|
|
66
69
|
:type version: str
|
|
67
70
|
:param export_format: Output format ('pdf' or 'zip')
|
|
@@ -89,6 +92,7 @@ class Reports:
|
|
|
89
92
|
'client_name': client_name,
|
|
90
93
|
'report_date': report_date,
|
|
91
94
|
'draft': draft,
|
|
95
|
+
'retest': retest,
|
|
92
96
|
'version': version,
|
|
93
97
|
},
|
|
94
98
|
'shared_output': shared_output,
|
|
@@ -52,6 +52,7 @@ class TestBuildExportBody:
|
|
|
52
52
|
assert body['group_by'] == 'attack_surface'
|
|
53
53
|
assert body['shared_output'] is False
|
|
54
54
|
assert body['config']['draft'] is False
|
|
55
|
+
assert body['config']['retest'] is False
|
|
55
56
|
assert body['config']['version'] == '1.0'
|
|
56
57
|
assert 'risk_keys' not in body
|
|
57
58
|
assert 'target' not in body['config']
|
|
@@ -85,6 +86,7 @@ class TestBuildExportBody:
|
|
|
85
86
|
end_date='2026-03-01',
|
|
86
87
|
report_date='2026-03-15',
|
|
87
88
|
draft=True,
|
|
89
|
+
retest=True,
|
|
88
90
|
version='2.0',
|
|
89
91
|
export_format='zip',
|
|
90
92
|
group_by='tag',
|
|
@@ -99,6 +101,7 @@ class TestBuildExportBody:
|
|
|
99
101
|
assert body['config']['start_date'] == '2026-01-01'
|
|
100
102
|
assert body['config']['end_date'] == '2026-03-01'
|
|
101
103
|
assert body['config']['draft'] is True
|
|
104
|
+
assert body['config']['retest'] is True
|
|
102
105
|
assert body['config']['version'] == '2.0'
|
|
103
106
|
assert body['export_format'] == 'zip'
|
|
104
107
|
assert body['group_by'] == 'tag'
|
|
@@ -12,15 +12,29 @@ class SearchCommands:
|
|
|
12
12
|
if not args:
|
|
13
13
|
self.console.print('[dim]Usage: search <term> [--kind <type>][/dim]')
|
|
14
14
|
return
|
|
15
|
-
term = ' '.join(args)
|
|
16
|
-
try:
|
|
17
|
-
kind = None
|
|
18
|
-
if '--kind' in args:
|
|
19
|
-
idx = args.index('--kind')
|
|
20
|
-
if idx + 1 < len(args):
|
|
21
|
-
kind = args[idx + 1]
|
|
22
|
-
term = ' '.join(args[:idx])
|
|
23
15
|
|
|
16
|
+
# Parse args
|
|
17
|
+
raw_args = list(args)
|
|
18
|
+
kind = None
|
|
19
|
+
if '--kind' in raw_args:
|
|
20
|
+
idx = raw_args.index('--kind')
|
|
21
|
+
if idx + 1 < len(raw_args):
|
|
22
|
+
kind = raw_args[idx + 1]
|
|
23
|
+
raw_args = raw_args[:idx] + raw_args[idx + 2:]
|
|
24
|
+
term = ' '.join(raw_args)
|
|
25
|
+
|
|
26
|
+
# Context-smart: if we have paged results, filter them locally first
|
|
27
|
+
if self._paged_results and not kind:
|
|
28
|
+
filtered = [
|
|
29
|
+
r for r in self._paged_results
|
|
30
|
+
if term.lower() in (r.get('key', '') + ' ' + r.get('name', '') + ' ' + r.get('dns', '') + ' ' + r.get('title', '')).lower()
|
|
31
|
+
]
|
|
32
|
+
if filtered:
|
|
33
|
+
self._render_results(filtered, f'Search: "{term}" in {self._paged_title}')
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
# Fall through to API search
|
|
37
|
+
try:
|
|
24
38
|
results, offset = self.sdk.search.by_term(term, kind)
|
|
25
39
|
self._render_results(results, f'Search: {term}')
|
|
26
40
|
except Exception as e:
|
|
@@ -34,7 +48,7 @@ class SearchCommands:
|
|
|
34
48
|
# Parse args
|
|
35
49
|
term = []
|
|
36
50
|
kind = None
|
|
37
|
-
limit =
|
|
51
|
+
limit = 200
|
|
38
52
|
i = 0
|
|
39
53
|
while i < len(args):
|
|
40
54
|
if args[i] == '--type' and i + 1 < len(args):
|
|
@@ -54,23 +68,30 @@ class SearchCommands:
|
|
|
54
68
|
self.console.print(f'[error]{e}[/error]')
|
|
55
69
|
return
|
|
56
70
|
|
|
57
|
-
|
|
71
|
+
if not kind and all_results:
|
|
72
|
+
# Cross-type display: group by entity type and show type labels
|
|
73
|
+
self._render_typed_results(all_results, f'Find: {term}')
|
|
74
|
+
else:
|
|
75
|
+
self._render_results(all_results, f'Find: {term}')
|
|
76
|
+
|
|
58
77
|
if len(all_results) >= limit:
|
|
59
|
-
self.console.print(f'[
|
|
78
|
+
self.console.print(f'[dim]Showing {limit} results. Use --limit to increase.[/dim]')
|
|
60
79
|
|
|
61
80
|
def _cmd_assets(self, args):
|
|
62
81
|
try:
|
|
63
|
-
filter_text = self.context.scope or ''
|
|
82
|
+
filter_text = ' '.join(args) if args else (self.context.scope or '')
|
|
64
83
|
results, _ = self.sdk.assets.list(filter_text, pages=1)
|
|
65
|
-
|
|
84
|
+
title = f'Assets: {filter_text}' if filter_text else 'Assets'
|
|
85
|
+
self._render_results(results, title)
|
|
66
86
|
except Exception as e:
|
|
67
87
|
self.console.print(f'[error]{e}[/error]')
|
|
68
88
|
|
|
69
89
|
def _cmd_risks(self, args):
|
|
70
90
|
try:
|
|
71
|
-
filter_text = self.context.scope or ''
|
|
91
|
+
filter_text = ' '.join(args) if args else (self.context.scope or '')
|
|
72
92
|
results, _ = self.sdk.risks.list(filter_text, pages=1)
|
|
73
|
-
|
|
93
|
+
title = f'Risks: {filter_text}' if filter_text else 'Risks'
|
|
94
|
+
self._render_results(results, title)
|
|
74
95
|
except Exception as e:
|
|
75
96
|
self.console.print(f'[error]{e}[/error]')
|
|
76
97
|
|
|
@@ -36,6 +36,7 @@ CONSOLE_COMMANDS = [
|
|
|
36
36
|
'use', 'options', 'execute', 'exploit', 'back',
|
|
37
37
|
'accounts', 'engagements', 'home', 'su',
|
|
38
38
|
'search', 'find', 'assets', 'risks', 'jobs', 'info',
|
|
39
|
+
'next', 'n', 'prev', 'p', 'page',
|
|
39
40
|
'scan', 'tag',
|
|
40
41
|
'run', 'status', 'download', 'install', 'installed',
|
|
41
42
|
'asset-analyzer', 'brutus', 'julius', 'augustus', 'aurelius',
|
|
@@ -190,6 +191,11 @@ class GuardConsole(
|
|
|
190
191
|
'risks': self._cmd_risks,
|
|
191
192
|
'jobs': self._cmd_jobs,
|
|
192
193
|
'info': self._cmd_info,
|
|
194
|
+
'next': self._cmd_next,
|
|
195
|
+
'n': self._cmd_next,
|
|
196
|
+
'prev': self._cmd_prev,
|
|
197
|
+
'p': self._cmd_prev,
|
|
198
|
+
'page': self._cmd_page,
|
|
193
199
|
'scan': self._cmd_scan,
|
|
194
200
|
'tag': self._cmd_tag,
|
|
195
201
|
'run': self._cmd_run,
|
|
@@ -289,11 +295,14 @@ class GuardConsole(
|
|
|
289
295
|
|
|
290
296
|
help_table.add_row('', '')
|
|
291
297
|
help_table.add_row('[section]Search & Recon[/section]', '')
|
|
292
|
-
help_table.add_row('search <term>', '
|
|
293
|
-
help_table.add_row('find <term> [--type X]', 'Fulltext search (Neo4j)')
|
|
294
|
-
help_table.add_row('assets', 'List assets (
|
|
295
|
-
help_table.add_row('risks', 'List risks (
|
|
298
|
+
help_table.add_row('search <term>', 'Smart search (filters current results, or API search)')
|
|
299
|
+
help_table.add_row('find <term> [--type X]', 'Fulltext search across all entity types (Neo4j)')
|
|
300
|
+
help_table.add_row('assets [filter]', 'List assets (prefix filter or scope)')
|
|
301
|
+
help_table.add_row('risks [filter]', 'List risks (prefix filter or scope)')
|
|
296
302
|
help_table.add_row('jobs [filter]', 'List jobs (filter by target/capability)')
|
|
303
|
+
help_table.add_row('next / n', 'Next page of results')
|
|
304
|
+
help_table.add_row('prev / p', 'Previous page of results')
|
|
305
|
+
help_table.add_row('page <#>', 'Jump to page number')
|
|
297
306
|
help_table.add_row('status', 'Check status of last job')
|
|
298
307
|
help_table.add_row('status <job_key>', 'Check status of specific job')
|
|
299
308
|
help_table.add_row('download [proofs|agents|all]', 'Download job outputs to local dir')
|
|
@@ -9,22 +9,42 @@ from rich.text import Text
|
|
|
9
9
|
class RendererMixin:
|
|
10
10
|
"""Rendering helpers for console output. Mixed into GuardConsole."""
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
# Pagination state
|
|
13
|
+
_paged_results = None # full result list
|
|
14
|
+
_paged_title = '' # title for the listing
|
|
15
|
+
_paged_page = 0 # current page (0-indexed)
|
|
16
|
+
_page_size = 50 # items per page
|
|
17
|
+
|
|
18
|
+
def _render_results(self, results: list, title: str, page: int = 0):
|
|
13
19
|
if not results:
|
|
14
20
|
self.console.print(f'[dim]No results for: {title}[/dim]')
|
|
15
21
|
if self.context.scope:
|
|
16
22
|
self.console.print(f'[dim]Current scope: {self.context.scope} -- use "unset scope" to broaden[/dim]')
|
|
17
23
|
return
|
|
18
24
|
|
|
19
|
-
|
|
20
|
-
|
|
25
|
+
# Store for pagination
|
|
26
|
+
self._paged_results = results
|
|
27
|
+
self._paged_title = title
|
|
28
|
+
self._paged_page = page
|
|
29
|
+
|
|
30
|
+
total = len(results)
|
|
31
|
+
start = page * self._page_size
|
|
32
|
+
end = min(start + self._page_size, total)
|
|
33
|
+
page_items = results[start:end]
|
|
34
|
+
total_pages = (total + self._page_size - 1) // self._page_size
|
|
35
|
+
|
|
36
|
+
table = Table(
|
|
37
|
+
title=f'{title} ({total} results) — page {page + 1}/{total_pages}',
|
|
38
|
+
border_style=self.colors['primary'],
|
|
39
|
+
)
|
|
40
|
+
table.add_column('#', style=self.colors['dim'], width=6)
|
|
21
41
|
table.add_column('Key', style=self.colors['primary'])
|
|
22
42
|
table.add_column('Name', style='white')
|
|
23
43
|
table.add_column('Status', style=self.colors['accent'])
|
|
24
44
|
|
|
25
45
|
# Update _target_list so "set target <#>" works from any listing
|
|
26
46
|
self._target_list = []
|
|
27
|
-
for i, item in enumerate(
|
|
47
|
+
for i, item in enumerate(page_items, start + 1):
|
|
28
48
|
key = item.get('key', '')
|
|
29
49
|
name = item.get('name', item.get('dns', item.get('title', '')))
|
|
30
50
|
status = item.get('status', '')
|
|
@@ -32,8 +52,133 @@ class RendererMixin:
|
|
|
32
52
|
self._target_list.append(key)
|
|
33
53
|
|
|
34
54
|
self.console.print(table)
|
|
35
|
-
|
|
36
|
-
|
|
55
|
+
|
|
56
|
+
# Pagination hint
|
|
57
|
+
hints = []
|
|
58
|
+
if page > 0:
|
|
59
|
+
hints.append('"prev" or "p"')
|
|
60
|
+
if end < total:
|
|
61
|
+
hints.append('"next" or "n"')
|
|
62
|
+
if total_pages > 2:
|
|
63
|
+
hints.append(f'"page <1-{total_pages}>"')
|
|
64
|
+
if hints:
|
|
65
|
+
self.console.print(f'[dim]{" | ".join(hints)}[/dim]')
|
|
66
|
+
|
|
67
|
+
def _cmd_next(self, args):
|
|
68
|
+
"""Show next page of results."""
|
|
69
|
+
if not self._paged_results:
|
|
70
|
+
self.console.print('[dim]No results to page through.[/dim]')
|
|
71
|
+
return
|
|
72
|
+
total_pages = (len(self._paged_results) + self._page_size - 1) // self._page_size
|
|
73
|
+
if self._paged_page + 1 >= total_pages:
|
|
74
|
+
self.console.print('[dim]Already on the last page.[/dim]')
|
|
75
|
+
return
|
|
76
|
+
self._render_results(self._paged_results, self._paged_title, self._paged_page + 1)
|
|
77
|
+
|
|
78
|
+
def _cmd_prev(self, args):
|
|
79
|
+
"""Show previous page of results."""
|
|
80
|
+
if not self._paged_results:
|
|
81
|
+
self.console.print('[dim]No results to page through.[/dim]')
|
|
82
|
+
return
|
|
83
|
+
if self._paged_page <= 0:
|
|
84
|
+
self.console.print('[dim]Already on the first page.[/dim]')
|
|
85
|
+
return
|
|
86
|
+
self._render_results(self._paged_results, self._paged_title, self._paged_page - 1)
|
|
87
|
+
|
|
88
|
+
def _cmd_page(self, args):
|
|
89
|
+
"""Jump to a specific page: page <number>"""
|
|
90
|
+
if not self._paged_results:
|
|
91
|
+
self.console.print('[dim]No results to page through.[/dim]')
|
|
92
|
+
return
|
|
93
|
+
if not args:
|
|
94
|
+
self.console.print('[dim]Usage: page <number>[/dim]')
|
|
95
|
+
return
|
|
96
|
+
try:
|
|
97
|
+
page_num = int(args[0]) - 1 # 1-indexed input
|
|
98
|
+
except ValueError:
|
|
99
|
+
self.console.print('[dim]Usage: page <number>[/dim]')
|
|
100
|
+
return
|
|
101
|
+
total_pages = (len(self._paged_results) + self._page_size - 1) // self._page_size
|
|
102
|
+
if page_num < 0 or page_num >= total_pages:
|
|
103
|
+
self.console.print(f'[dim]Page must be between 1 and {total_pages}.[/dim]')
|
|
104
|
+
return
|
|
105
|
+
self._render_results(self._paged_results, self._paged_title, page_num)
|
|
106
|
+
|
|
107
|
+
def _render_typed_results(self, results: list, title: str):
|
|
108
|
+
"""Render results with entity type labels — used for cross-type searches."""
|
|
109
|
+
if not results:
|
|
110
|
+
self.console.print(f'[dim]No results for: {title}[/dim]')
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
# Store for pagination
|
|
114
|
+
self._paged_results = results
|
|
115
|
+
self._paged_title = title
|
|
116
|
+
self._paged_page = 0
|
|
117
|
+
|
|
118
|
+
total = len(results)
|
|
119
|
+
page_items = results[:self._page_size]
|
|
120
|
+
total_pages = (total + self._page_size - 1) // self._page_size
|
|
121
|
+
|
|
122
|
+
table = Table(
|
|
123
|
+
title=f'{title} ({total} results) — page 1/{total_pages}',
|
|
124
|
+
border_style=self.colors['primary'],
|
|
125
|
+
)
|
|
126
|
+
table.add_column('#', style=self.colors['dim'], width=6)
|
|
127
|
+
table.add_column('Type', style=self.colors['accent'], width=10)
|
|
128
|
+
table.add_column('Key', style=self.colors['primary'])
|
|
129
|
+
table.add_column('Name', style='white')
|
|
130
|
+
table.add_column('Status', style=self.colors['accent'])
|
|
131
|
+
|
|
132
|
+
self._target_list = []
|
|
133
|
+
for i, item in enumerate(page_items, 1):
|
|
134
|
+
key = item.get('key', '')
|
|
135
|
+
name = item.get('name', item.get('dns', item.get('title', '')))
|
|
136
|
+
status = item.get('status', '')
|
|
137
|
+
|
|
138
|
+
# Infer type from key
|
|
139
|
+
entity_type = '?'
|
|
140
|
+
if key.startswith('#risk#'):
|
|
141
|
+
entity_type = 'risk'
|
|
142
|
+
elif key.startswith('#asset#'):
|
|
143
|
+
entity_type = 'asset'
|
|
144
|
+
elif key.startswith('#ad'):
|
|
145
|
+
# AD subtypes: aduser, adgroup, adcomputer, addomain, adenterpriseca, etc.
|
|
146
|
+
parts = key.split('#')
|
|
147
|
+
entity_type = parts[1] if len(parts) > 1 else 'ad'
|
|
148
|
+
elif key.startswith('#seed#'):
|
|
149
|
+
entity_type = 'seed'
|
|
150
|
+
elif key.startswith('#job#'):
|
|
151
|
+
entity_type = 'job'
|
|
152
|
+
elif key.startswith('#port#') or key.startswith('#attribute#'):
|
|
153
|
+
parts = key.split('#')
|
|
154
|
+
entity_type = parts[1] if len(parts) > 1 else 'other'
|
|
155
|
+
else:
|
|
156
|
+
parts = key.split('#')
|
|
157
|
+
entity_type = parts[1] if len(parts) > 2 else 'other'
|
|
158
|
+
|
|
159
|
+
table.add_row(str(i), entity_type, key, str(name), status)
|
|
160
|
+
self._target_list.append(key)
|
|
161
|
+
|
|
162
|
+
self.console.print(table)
|
|
163
|
+
|
|
164
|
+
# Type summary
|
|
165
|
+
from collections import Counter
|
|
166
|
+
type_counts = Counter()
|
|
167
|
+
for item in results:
|
|
168
|
+
key = item.get('key', '')
|
|
169
|
+
parts = key.split('#')
|
|
170
|
+
t = parts[1] if len(parts) > 2 else 'other'
|
|
171
|
+
type_counts[t] += 1
|
|
172
|
+
summary = ', '.join(f'{count} {t}' for t, count in type_counts.most_common())
|
|
173
|
+
self.console.print(f'[dim]{summary}[/dim]')
|
|
174
|
+
|
|
175
|
+
hints = []
|
|
176
|
+
if total_pages > 1:
|
|
177
|
+
hints.append('"next" or "n"')
|
|
178
|
+
if total_pages > 2:
|
|
179
|
+
hints.append(f'"page <1-{total_pages}>"')
|
|
180
|
+
if hints:
|
|
181
|
+
self.console.print(f'[dim]{" | ".join(hints)}[/dim]')
|
|
37
182
|
|
|
38
183
|
def _render_evidence(self, hydrated: dict):
|
|
39
184
|
risk = hydrated.get('risk', {})
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/scripts/commands/nmap-example.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/aegis/commands/job_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/aegis/commands/schedule_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/console/commands/__init__.py
RENAMED
|
File without changes
|
{praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/console/commands/accounts.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/console/commands/reporting.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{praetorian_cli-2.4.0 → praetorian_cli-2.4.1}/praetorian_cli/ui/conversation/textual_chat.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|