praetorian-cli 2.2.16__py3-none-any.whl → 2.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- praetorian_cli/handlers/add.py +9 -9
- praetorian_cli/handlers/agent.py +4 -4
- praetorian_cli/handlers/chariot.py +1 -1
- praetorian_cli/handlers/delete.py +2 -2
- praetorian_cli/handlers/get.py +5 -3
- praetorian_cli/handlers/imports.py +1 -1
- praetorian_cli/handlers/list.py +2 -2
- praetorian_cli/handlers/search.py +1 -1
- praetorian_cli/handlers/update.py +1 -1
- praetorian_cli/main.py +23 -0
- praetorian_cli/sdk/chariot.py +1 -9
- praetorian_cli/sdk/entities/preseeds.py +19 -8
- praetorian_cli/sdk/entities/seeds.py +24 -4
- praetorian_cli/sdk/guard.py +7 -0
- praetorian_cli/sdk/test/test_preseed.py +10 -0
- praetorian_cli/sdk/test/test_seed.py +10 -0
- praetorian_cli/sdk/test/test_z_cli.py +7 -1
- {praetorian_cli-2.2.16.dist-info → praetorian_cli-2.3.0.dist-info}/METADATA +57 -20
- {praetorian_cli-2.2.16.dist-info → praetorian_cli-2.3.0.dist-info}/RECORD +23 -22
- {praetorian_cli-2.2.16.dist-info → praetorian_cli-2.3.0.dist-info}/WHEEL +1 -1
- {praetorian_cli-2.2.16.dist-info → praetorian_cli-2.3.0.dist-info}/entry_points.txt +1 -0
- {praetorian_cli-2.2.16.dist-info → praetorian_cli-2.3.0.dist-info}/licenses/LICENSE +0 -0
- {praetorian_cli-2.2.16.dist-info → praetorian_cli-2.3.0.dist-info}/top_level.txt +0 -0
praetorian_cli/handlers/add.py
CHANGED
|
@@ -12,7 +12,7 @@ from praetorian_cli.sdk.model.globals import AddRisk, Asset, Seed, Kind
|
|
|
12
12
|
|
|
13
13
|
@chariot.group()
|
|
14
14
|
def add():
|
|
15
|
-
""" Add an entity to
|
|
15
|
+
""" Add an entity to Guard """
|
|
16
16
|
pass
|
|
17
17
|
|
|
18
18
|
|
|
@@ -27,7 +27,7 @@ def add():
|
|
|
27
27
|
def asset(sdk, name, dns, asset_type, status, surface):
|
|
28
28
|
""" Add an asset
|
|
29
29
|
|
|
30
|
-
Add an asset to the
|
|
30
|
+
Add an asset to the Guard database. This command requires a DNS name for the asset.
|
|
31
31
|
Optionally, a name can be provided to give the asset more specific information,
|
|
32
32
|
such as IP address. If no name is provided, the DNS name will be used as the name.
|
|
33
33
|
The DNS is the group and the name is the specific identifier. This is for legacy reasons.
|
|
@@ -55,12 +55,12 @@ def asset(sdk, name, dns, asset_type, status, surface):
|
|
|
55
55
|
@add.command()
|
|
56
56
|
@cli_handler
|
|
57
57
|
@click.argument('path')
|
|
58
|
-
@click.option('-n', '--name', help='The file name in
|
|
58
|
+
@click.option('-n', '--name', help='The file name in Guard. Default: the full path of the uploaded file')
|
|
59
59
|
def file(sdk, path, name):
|
|
60
60
|
""" Upload a file
|
|
61
61
|
|
|
62
62
|
This commands takes the path to a local file and uploads it to the
|
|
63
|
-
|
|
63
|
+
Guard file system. The Guard file system is where the platform
|
|
64
64
|
stores proofs of exploit, risk definitions, and other supporting data.
|
|
65
65
|
|
|
66
66
|
User files reside in the "home/" folder. Those files appear in the app
|
|
@@ -89,7 +89,7 @@ def definition(sdk, path, name):
|
|
|
89
89
|
""" Upload a risk definition
|
|
90
90
|
|
|
91
91
|
This commands takes the path to the local file and uploads it to the
|
|
92
|
-
|
|
92
|
+
Guard file system as risk definitions. Risk definitions reside
|
|
93
93
|
in the "definitions/" folder in the file system.
|
|
94
94
|
|
|
95
95
|
Risk definitions need to be in the Markdown format.
|
|
@@ -141,7 +141,7 @@ def webhook(sdk):
|
|
|
141
141
|
def risk(sdk, name, asset, status, comment, capability):
|
|
142
142
|
""" Add a risk
|
|
143
143
|
|
|
144
|
-
This command adds a risk to
|
|
144
|
+
This command adds a risk to Guard. A risk must have an associated asset.
|
|
145
145
|
The asset is specified by its key, which can be retrieved by listing and
|
|
146
146
|
searching the assets.
|
|
147
147
|
|
|
@@ -211,7 +211,7 @@ def attribute(sdk, key, name, value):
|
|
|
211
211
|
def seed(sdk, seed_type, status, field_list):
|
|
212
212
|
""" Add a seed
|
|
213
213
|
|
|
214
|
-
Add a seed to the
|
|
214
|
+
Add a seed to the Guard database. Seeds are now assets with special labeling.
|
|
215
215
|
You can specify the asset type and provide dynamic fields using --fields.
|
|
216
216
|
|
|
217
217
|
\b
|
|
@@ -247,7 +247,7 @@ def seed(sdk, seed_type, status, field_list):
|
|
|
247
247
|
def preseed(sdk, type, title, value, status):
|
|
248
248
|
""" Add a preseed
|
|
249
249
|
|
|
250
|
-
This command adds a preseed to the
|
|
250
|
+
This command adds a preseed to the Guard database.
|
|
251
251
|
Preseeds default to ACTIVE and cannot be added as PENDING.
|
|
252
252
|
|
|
253
253
|
\b
|
|
@@ -337,7 +337,7 @@ def key(sdk, name, expires):
|
|
|
337
337
|
def webpage(sdk, url, parent):
|
|
338
338
|
""" Add a Webpage
|
|
339
339
|
|
|
340
|
-
Add a web page to the
|
|
340
|
+
Add a web page to the Guard database. Webpages can optionally be associated
|
|
341
341
|
with a parent WebApplication or exist independently.
|
|
342
342
|
|
|
343
343
|
\b
|
praetorian_cli/handlers/agent.py
CHANGED
|
@@ -29,14 +29,14 @@ def affiliation(sdk, key):
|
|
|
29
29
|
|
|
30
30
|
@agent.group()
|
|
31
31
|
def mcp():
|
|
32
|
-
"""
|
|
32
|
+
""" Guard's MCP server """
|
|
33
33
|
pass
|
|
34
34
|
|
|
35
35
|
@mcp.command()
|
|
36
36
|
@cli_handler
|
|
37
37
|
@click.option('--allowed', '-a', type=str, multiple=True, default=['search_by_query', '*_list', '*_get'])
|
|
38
38
|
def start(sdk, allowed):
|
|
39
|
-
""" Starts the
|
|
39
|
+
""" Starts the Guard MCP server
|
|
40
40
|
|
|
41
41
|
\b
|
|
42
42
|
Example usages:
|
|
@@ -73,9 +73,9 @@ def tools(sdk, allowed):
|
|
|
73
73
|
@agent.command()
|
|
74
74
|
@cli_handler
|
|
75
75
|
def conversation(sdk):
|
|
76
|
-
""" Interactive conversation with
|
|
76
|
+
""" Interactive conversation with Guard AI assistant
|
|
77
77
|
|
|
78
|
-
Start an interactive chat session with the
|
|
78
|
+
Start an interactive chat session with the Guard AI assistant.
|
|
79
79
|
The AI can help you query security data, understand findings,
|
|
80
80
|
and provide insights about your attack surface.
|
|
81
81
|
|
|
@@ -7,7 +7,7 @@ def chariot(click_context):
|
|
|
7
7
|
# import done here to avoid circular import errors in praetorian_cli/handlers/cli_decorators.py
|
|
8
8
|
from praetorian_cli.sdk.chariot import Chariot
|
|
9
9
|
|
|
10
|
-
""" Command group for interacting with the
|
|
10
|
+
""" Command group for interacting with the Guard product """
|
|
11
11
|
# Replace the click context (previously a Keychain instance) with a Chariot
|
|
12
12
|
# instance, after creating it using the Keychain instance.
|
|
13
13
|
keychain = click_context.obj['keychain']
|
|
@@ -7,7 +7,7 @@ from praetorian_cli.sdk.model.globals import Risk
|
|
|
7
7
|
|
|
8
8
|
@chariot.group()
|
|
9
9
|
def delete():
|
|
10
|
-
""" Delete an entity from
|
|
10
|
+
""" Delete an entity from Guard """
|
|
11
11
|
pass
|
|
12
12
|
|
|
13
13
|
|
|
@@ -106,7 +106,7 @@ def file(chariot, filepath):
|
|
|
106
106
|
|
|
107
107
|
\b
|
|
108
108
|
Arguments:
|
|
109
|
-
- FILEPATH: The
|
|
109
|
+
- FILEPATH: The Guard file path
|
|
110
110
|
|
|
111
111
|
\b
|
|
112
112
|
Example usage:
|
praetorian_cli/handlers/get.py
CHANGED
|
@@ -9,7 +9,7 @@ from praetorian_cli.handlers.utils import print_json
|
|
|
9
9
|
|
|
10
10
|
@chariot.group()
|
|
11
11
|
def get():
|
|
12
|
-
""" Get entity details from
|
|
12
|
+
""" Get entity details from Guard """
|
|
13
13
|
pass
|
|
14
14
|
|
|
15
15
|
|
|
@@ -319,13 +319,15 @@ def webpage(chariot, key):
|
|
|
319
319
|
"""
|
|
320
320
|
print_json(chariot.webpage.get(key))
|
|
321
321
|
|
|
322
|
+
@get.command()
|
|
323
|
+
@cli_handler
|
|
322
324
|
@click.option('-t', '--type', help='Optional specific entity type (e.g., asset, risk, attribute)')
|
|
323
325
|
@click.option('-d', '--details', is_flag=True, help='Further retrieve the details of the schema')
|
|
324
326
|
def schema(chariot, type, details):
|
|
325
|
-
""" Get
|
|
327
|
+
""" Get Guard entity schema
|
|
326
328
|
|
|
327
329
|
\b
|
|
328
|
-
Returns the JSON schema for
|
|
330
|
+
Returns the JSON schema for Guard entities. Optionally filter for a
|
|
329
331
|
specific entity type.
|
|
330
332
|
|
|
331
333
|
\b
|
praetorian_cli/handlers/list.py
CHANGED
|
@@ -7,7 +7,7 @@ from praetorian_cli.handlers.utils import render_offset, render_list_results, pa
|
|
|
7
7
|
|
|
8
8
|
@chariot.group()
|
|
9
9
|
def list():
|
|
10
|
-
""" Get a list of entities from
|
|
10
|
+
""" Get a list of entities from Guard """
|
|
11
11
|
pass
|
|
12
12
|
|
|
13
13
|
|
|
@@ -121,7 +121,7 @@ def jobs(chariot, filter, details, offset, page):
|
|
|
121
121
|
def files(chariot, filter, details, offset, page):
|
|
122
122
|
""" List files
|
|
123
123
|
|
|
124
|
-
Retrieve and display a list of files in the
|
|
124
|
+
Retrieve and display a list of files in the Guard file system.
|
|
125
125
|
|
|
126
126
|
\b
|
|
127
127
|
Example usages:
|
|
@@ -16,7 +16,7 @@ from praetorian_cli.sdk.model.globals import Kind
|
|
|
16
16
|
@click.option('-desc', '--desc', is_flag=True, default=False, help='Return data in descending order')
|
|
17
17
|
@click.option('-g', '--global', 'global_', is_flag=True, default=False, help='Use the global data set')
|
|
18
18
|
def search(chariot, term, count, kind, details, offset, page, desc, global_):
|
|
19
|
-
""" Query
|
|
19
|
+
""" Query Guard for matches or counts using the search syntax
|
|
20
20
|
|
|
21
21
|
\b
|
|
22
22
|
Search syntax:
|
praetorian_cli/main.py
CHANGED
|
@@ -36,3 +36,26 @@ def main(click_context, profile, account, debug, proxy):
|
|
|
36
36
|
|
|
37
37
|
main.add_command(chariot)
|
|
38
38
|
main.add_command(configure)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@click.group()
|
|
42
|
+
@click.option('--profile', default='United States', help='The profile to use in the keychain file', show_default=True)
|
|
43
|
+
@click.option('--account', default=None, help='Assume role into this account')
|
|
44
|
+
@click.option('--debug', is_flag=True, default=False, help='Run the CLI in debug mode')
|
|
45
|
+
@click.option('--proxy', default='', help='The proxy to use in the CLI')
|
|
46
|
+
@click.pass_context
|
|
47
|
+
@click.version_option()
|
|
48
|
+
def guard_main(click_context, profile, account, debug, proxy):
|
|
49
|
+
"""Guard CLI - Praetorian's offensive security platform."""
|
|
50
|
+
from praetorian_cli.sdk.chariot import Chariot
|
|
51
|
+
if debug:
|
|
52
|
+
click.echo('Running in debug mode.')
|
|
53
|
+
chariot.is_debug = debug
|
|
54
|
+
click_context.obj = Chariot(Keychain(profile, account), proxy=proxy)
|
|
55
|
+
praetorian_cli.handlers.script.load_dynamic_commands()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# Add all chariot commands to guard_main
|
|
59
|
+
for cmd_name, cmd in chariot.commands.items():
|
|
60
|
+
guard_main.add_command(cmd, cmd_name)
|
|
61
|
+
guard_main.add_command(configure)
|
praetorian_cli/sdk/chariot.py
CHANGED
|
@@ -66,11 +66,9 @@ class Chariot:
|
|
|
66
66
|
|
|
67
67
|
def chariot_request(self, method: str, url: str, headers: dict = {}, **kwargs) -> requests.Response:
|
|
68
68
|
"""
|
|
69
|
-
Centralized wrapper around requests.request.
|
|
69
|
+
Centralized wrapper around requests.request. Takes care of proxy and
|
|
70
70
|
supplies the authentication headers
|
|
71
71
|
"""
|
|
72
|
-
self.add_beta_url_param(kwargs)
|
|
73
|
-
|
|
74
72
|
if self.proxy:
|
|
75
73
|
kwargs['proxies'] = {'http': self.proxy, 'https': self.proxy}
|
|
76
74
|
kwargs['verify'] = False
|
|
@@ -78,12 +76,6 @@ class Chariot:
|
|
|
78
76
|
return requests.request(method, url, headers=(headers | self.keychain.headers()), **kwargs)
|
|
79
77
|
|
|
80
78
|
|
|
81
|
-
def add_beta_url_param(self, kwargs: dict):
|
|
82
|
-
if 'params' in kwargs:
|
|
83
|
-
kwargs['params']['beta'] = 'true'
|
|
84
|
-
else:
|
|
85
|
-
kwargs['params'] = {'beta': 'true'}
|
|
86
|
-
|
|
87
79
|
def my(self, params: dict, pages=1) -> dict:
|
|
88
80
|
final_resp = dict()
|
|
89
81
|
|
|
@@ -85,9 +85,9 @@ class Preseeds:
|
|
|
85
85
|
"""
|
|
86
86
|
return self.api.search.by_exact_key(key, details)
|
|
87
87
|
|
|
88
|
-
def update(self, key, status):
|
|
88
|
+
def update(self, key, status, comment=None):
|
|
89
89
|
"""Update the status of an existing preseed.
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
Only the status field can be meaningfully updated for preseeds. This allows
|
|
92
92
|
you to activate, freeze, or mark preseeds for deletion without recreating them.
|
|
93
93
|
The preseed must exist before it can be updated.
|
|
@@ -101,24 +101,29 @@ class Preseeds:
|
|
|
101
101
|
- 'FR' (FROZEN_REJECTED): Preseed is frozen and rejected
|
|
102
102
|
- 'P' (PENDING): Preseed is pending activation
|
|
103
103
|
:type status: str
|
|
104
|
+
:param comment: Optional comment to include with the update (stored in History field)
|
|
105
|
+
:type comment: str or None
|
|
104
106
|
:return: The updated preseed object with new status
|
|
105
107
|
:rtype: dict
|
|
106
108
|
:raises: Prints error message if preseed with the specified key is not found
|
|
107
109
|
|
|
108
110
|
**Example Usage:**
|
|
109
|
-
|
|
111
|
+
|
|
110
112
|
.. code-block:: python
|
|
111
|
-
|
|
113
|
+
|
|
114
|
+
# Update status only
|
|
112
115
|
sdk.preseeds.update(
|
|
113
116
|
"#preseed#whois+company#Example Company#example company",
|
|
114
117
|
"A"
|
|
115
118
|
)
|
|
116
|
-
|
|
119
|
+
|
|
120
|
+
# Update status with comment
|
|
117
121
|
sdk.preseeds.update(
|
|
118
122
|
"#preseed#dns+subdomain#API Subdomain#api.example.com",
|
|
119
|
-
"F"
|
|
123
|
+
"F",
|
|
124
|
+
comment="Temporarily frozen pending review"
|
|
120
125
|
)
|
|
121
|
-
|
|
126
|
+
|
|
122
127
|
sdk.preseeds.update(
|
|
123
128
|
"#preseed#whois+company#Old Company#old company",
|
|
124
129
|
"D"
|
|
@@ -130,7 +135,13 @@ class Preseeds:
|
|
|
130
135
|
"""
|
|
131
136
|
preseed = self.api.search.by_exact_key(key)
|
|
132
137
|
if preseed:
|
|
133
|
-
|
|
138
|
+
update_payload = {'key': key, 'status': status}
|
|
139
|
+
|
|
140
|
+
# Include comment if provided
|
|
141
|
+
if comment is not None:
|
|
142
|
+
update_payload['comment'] = comment
|
|
143
|
+
|
|
144
|
+
return self.api.update('preseed', update_payload)
|
|
134
145
|
else:
|
|
135
146
|
error(f'Pre-seed {key} is not found.')
|
|
136
147
|
|
|
@@ -69,26 +69,46 @@ class Seeds:
|
|
|
69
69
|
return None
|
|
70
70
|
return results[0]
|
|
71
71
|
|
|
72
|
-
def update(self, key, status=None):
|
|
72
|
+
def update(self, key, status=None, comment=None):
|
|
73
73
|
"""
|
|
74
74
|
Update seed fields dynamically.
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
:param key: Seed/Asset key (e.g., '#seed#domain#example.com' or '#asset#domain#example.com')
|
|
77
77
|
:type key: str
|
|
78
78
|
:param status: Status for backward compatibility (can be positional)
|
|
79
79
|
:type status: str or None
|
|
80
|
+
:param comment: Optional comment to include with the update (stored in History field)
|
|
81
|
+
:type comment: str or None
|
|
80
82
|
:param kwargs: Fields to update
|
|
81
83
|
:return: The updated seed, or None if the seed was not found
|
|
82
84
|
:rtype: dict or None
|
|
85
|
+
|
|
86
|
+
**Example Usage:**
|
|
87
|
+
|
|
88
|
+
.. code-block:: python
|
|
89
|
+
|
|
90
|
+
# Update status only
|
|
91
|
+
sdk.seeds.update("#asset#domain#example.com", "A")
|
|
92
|
+
|
|
93
|
+
# Update status with comment
|
|
94
|
+
sdk.seeds.update(
|
|
95
|
+
"#asset#domain#example.com",
|
|
96
|
+
"A",
|
|
97
|
+
comment="Activated after verification"
|
|
98
|
+
)
|
|
83
99
|
"""
|
|
84
|
-
|
|
100
|
+
|
|
85
101
|
seed = self.get(key) # This already handles old key format conversion
|
|
86
102
|
if seed:
|
|
87
103
|
update_payload = {
|
|
88
104
|
'key': key,
|
|
89
105
|
'status': status
|
|
90
106
|
}
|
|
91
|
-
|
|
107
|
+
|
|
108
|
+
# Include comment if provided
|
|
109
|
+
if comment is not None:
|
|
110
|
+
update_payload['comment'] = comment
|
|
111
|
+
|
|
92
112
|
return self.api.upsert('seed', update_payload)
|
|
93
113
|
else:
|
|
94
114
|
error(f'Seed {key} not found.')
|
|
@@ -27,5 +27,15 @@ class TestPreseed:
|
|
|
27
27
|
assert len(preseeds) > 0
|
|
28
28
|
assert preseeds[0]['status'] == Preseed.FROZEN_REJECTED.value
|
|
29
29
|
|
|
30
|
+
def test_update_preseed_with_comment(self):
|
|
31
|
+
# Update preseed with comment parameter
|
|
32
|
+
self.sdk.preseeds.update(self.preseed_key, Preseed.ACTIVE.value, comment="Test comment for preseed update")
|
|
33
|
+
preseed = self.sdk.preseeds.get(self.preseed_key, details=True)
|
|
34
|
+
assert preseed['status'] == Preseed.ACTIVE.value
|
|
35
|
+
# Verify comment was stored in history
|
|
36
|
+
assert 'history' in preseed
|
|
37
|
+
assert len(preseed['history']) > 0
|
|
38
|
+
assert preseed['history'][0]['comment'] == "Test comment for preseed update"
|
|
39
|
+
|
|
30
40
|
def teardown_class(self):
|
|
31
41
|
clean_test_entities(self.sdk, self)
|
|
@@ -29,6 +29,16 @@ class TestSeed:
|
|
|
29
29
|
self.sdk.seeds.update(self.seed_asset_key, Seed.ACTIVE.value)
|
|
30
30
|
assert self.get_seed()['status'] == Seed.ACTIVE.value
|
|
31
31
|
|
|
32
|
+
def test_update_seed_with_comment(self):
|
|
33
|
+
# Update seed with comment parameter
|
|
34
|
+
self.sdk.seeds.update(self.seed_asset_key, Seed.FROZEN.value, comment="Test comment for seed update")
|
|
35
|
+
seed = self.get_seed()
|
|
36
|
+
assert seed['status'] == Seed.FROZEN.value
|
|
37
|
+
# Verify comment was stored in history
|
|
38
|
+
assert 'history' in seed
|
|
39
|
+
assert len(seed['history']) > 0
|
|
40
|
+
assert seed['history'][0]['comment'] == "Test comment for seed update"
|
|
41
|
+
|
|
32
42
|
def test_delete_seed(self):
|
|
33
43
|
self.sdk.seeds.delete(self.seed_asset_key)
|
|
34
44
|
assert self.sdk.seeds.get(self.seed_asset_key)['status'] == Seed.DELETED.value
|
|
@@ -390,8 +390,14 @@ class TestZCli:
|
|
|
390
390
|
self.verify('agent --help', ignore_stdout=True)
|
|
391
391
|
self.verify('agent affiliation --help', ignore_stdout=True)
|
|
392
392
|
|
|
393
|
+
def test_guard_cli(self):
|
|
394
|
+
"""Test the 'guard' entry point works correctly."""
|
|
395
|
+
self.verify('configure --help', expected_stdout=['Configure the CLI'])
|
|
396
|
+
self.verify('--help', expected_stdout=['configure', 'list', 'add', 'delete', 'update'])
|
|
397
|
+
self.verify('list --help', expected_stdout=['assets', 'risks', 'accounts'])
|
|
398
|
+
|
|
393
399
|
def verify(self, command, expected_stdout=[], expected_stderr=[], ignore_stdout=False):
|
|
394
|
-
result = run(f'
|
|
400
|
+
result = run(f'guard --profile "{self.sdk.keychain.profile}" {command}', capture_output=True,
|
|
395
401
|
text=True, shell=True)
|
|
396
402
|
if expected_stdout:
|
|
397
403
|
for out in expected_stdout:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: praetorian-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: For interacting with the Chariot API
|
|
5
5
|
Home-page: https://github.com/praetorian-inc/praetorian-cli
|
|
6
6
|
Author: Praetorian
|
|
@@ -31,7 +31,7 @@ Dynamic: license-file
|
|
|
31
31
|
[](https://opensource.org/)
|
|
32
32
|
[](https://github.com/praetorian-inc/chariot-ui/issues)
|
|
33
33
|
|
|
34
|
-
:link: [
|
|
34
|
+
:link: [Guard Platform](https://guard.praetorian.com)
|
|
35
35
|
:book: [Documentation](https://docs.praetorian.com)
|
|
36
36
|
:bookmark: [PyPI](https://pypi.org/project/praetorian-cli/)
|
|
37
37
|
|
|
@@ -48,14 +48,15 @@ Dynamic: license-file
|
|
|
48
48
|
- [Contributing](#contributing)
|
|
49
49
|
- [Support](#support)
|
|
50
50
|
- [License](#license)
|
|
51
|
+
- [Backwards Compatibility](#backwards-compatibility)
|
|
51
52
|
|
|
52
53
|
# Description
|
|
53
54
|
|
|
54
55
|
Praetorian CLI and SDK are open-source tools for interacting with our products and services. Currently, they support
|
|
55
|
-
access to [
|
|
56
|
+
access to [Guard](https://www.praetorian.com/proactive-cybersecurity-technology/), our
|
|
56
57
|
offensive security platform.
|
|
57
|
-
<br> The SDK exposes the full set of APIs that the
|
|
58
|
-
<br> The CLI is a fully-featured companion to the
|
|
58
|
+
<br> The SDK exposes the full set of APIs that the Guard UI uses.
|
|
59
|
+
<br> The CLI is a fully-featured companion to the Guard UI.
|
|
59
60
|
|
|
60
61
|
# Getting Started
|
|
61
62
|
|
|
@@ -74,14 +75,14 @@ pip install praetorian-cli
|
|
|
74
75
|
|
|
75
76
|
## Signing up
|
|
76
77
|
|
|
77
|
-
Register for an account for [
|
|
78
|
+
Register for an account for [Guard](http://guard.praetorian.com) using the instructions
|
|
78
79
|
in [our documentation](https://docs.praetorian.com/hc/en-us/articles/38048335323547-Account-Creation-and-Attack-Surface-Setup).
|
|
79
80
|
|
|
80
81
|
## Authentication
|
|
81
82
|
|
|
82
|
-
Once you can properly access
|
|
83
|
+
Once you can properly access Guard through the UI. You can obtain API credentials through the UI under
|
|
83
84
|
Settings -> User Settings -> API Keys. Be sure to careful copy the API credentials you created as
|
|
84
|
-
you will need to provide them to the CLI for interacting with
|
|
85
|
+
you will need to provide them to the CLI for interacting with Guard.
|
|
85
86
|
|
|
86
87
|
**Note**: SSO Organizations should provision access through API Keys as well.
|
|
87
88
|
|
|
@@ -123,49 +124,49 @@ For more advanced configuration options or managing access in SSO organizations
|
|
|
123
124
|
|
|
124
125
|
# Using the CLI
|
|
125
126
|
|
|
126
|
-
The CLI is a command and option utility for accessing the full suite of
|
|
127
|
+
The CLI is a command and option utility for accessing the full suite of Guard's API. You can see the documentation for commands
|
|
127
128
|
using the `help` option:
|
|
128
129
|
|
|
129
130
|
```zsh
|
|
130
|
-
|
|
131
|
+
guard --help
|
|
131
132
|
```
|
|
132
133
|
|
|
133
134
|
As an example, run the following command to retrieve the list of all assets in your account:
|
|
134
135
|
|
|
135
136
|
```zsh
|
|
136
|
-
|
|
137
|
+
guard --account guard+example@praetorian.com list assets
|
|
137
138
|
```
|
|
138
139
|
|
|
139
|
-
You can obtain the `account` argument by viewing the email of the first user on the Users page in your
|
|
140
|
+
You can obtain the `account` argument by viewing the email of the first user on the Users page in your Guard account, as shown below:
|
|
140
141
|
|
|
141
142
|
<img width="482" alt="image" src="https://github.com/user-attachments/assets/7c1024c9-7b74-46b1-87c5-af44671b1ec8" />
|
|
142
143
|
|
|
143
144
|
To get detailed information about a specific asset, run:
|
|
144
145
|
|
|
145
146
|
```zsh
|
|
146
|
-
|
|
147
|
+
guard --account guard+example@praetorian.com get asset <ASSET_KEY>
|
|
147
148
|
```
|
|
148
149
|
|
|
149
150
|
# Developers
|
|
150
151
|
|
|
151
152
|
Both CLI and SDK is open-source in this repository. The SDK is installed along with the `praetorian-cli`
|
|
152
|
-
package. You can extend
|
|
153
|
+
package. You can extend Guard by creating scripts using the SDK.
|
|
153
154
|
|
|
154
155
|
## SDK
|
|
155
156
|
|
|
156
157
|
Integrate the SDK into your own Python application with the following steps:
|
|
157
158
|
|
|
158
159
|
1. Include the dependency ``praetorian-cli`` in your project.
|
|
159
|
-
2. Import the
|
|
160
|
+
2. Import the Guard class ``from praetorian_cli.sdk.guard import Guard``.
|
|
160
161
|
3. Import the Keychain class ``from praetorian_cli.sdk.keychain import Keychain``.
|
|
161
|
-
4. Call any function of the
|
|
162
|
+
4. Call any function of the Guard class, which expose the full backend API. See example below:
|
|
162
163
|
|
|
163
164
|
```python
|
|
164
|
-
from praetorian_cli.sdk.
|
|
165
|
+
from praetorian_cli.sdk.guard import Guard
|
|
165
166
|
from praetorian_cli.sdk.keychain import Keychain
|
|
166
167
|
|
|
167
|
-
|
|
168
|
-
|
|
168
|
+
guard = Guard(Keychain(account='guard+example@praetorian.com'))
|
|
169
|
+
guard.add('asset', dict(name='example.com', dns='example.com'))
|
|
169
170
|
```
|
|
170
171
|
|
|
171
172
|
The best place to explore the SDK is the code of the CLI, especially
|
|
@@ -184,7 +185,7 @@ environment to point to directories where you store additional extension scripts
|
|
|
184
185
|
Those external scripts are available under the `script` commands. To see a list of them:
|
|
185
186
|
|
|
186
187
|
```zsh
|
|
187
|
-
|
|
188
|
+
guard --account guard+example@praetorian.com script --help
|
|
188
189
|
```
|
|
189
190
|
|
|
190
191
|
For developing scripts, you can refer to
|
|
@@ -209,3 +210,39 @@ If you have any questions or need support, please open an issue
|
|
|
209
210
|
## License
|
|
210
211
|
|
|
211
212
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
# Backwards Compatibility
|
|
217
|
+
|
|
218
|
+
**Guard** is a rebrand of **Chariot**.
|
|
219
|
+
|
|
220
|
+
### CLI
|
|
221
|
+
The `guard` command is the new primary CLI entry point. The legacy `praetorian chariot` command continues to work:
|
|
222
|
+
|
|
223
|
+
```zsh
|
|
224
|
+
# New (preferred):
|
|
225
|
+
guard list assets
|
|
226
|
+
guard --account example@praetorian.com list assets
|
|
227
|
+
guard configure
|
|
228
|
+
|
|
229
|
+
# Legacy (still supported):
|
|
230
|
+
praetorian chariot list assets
|
|
231
|
+
praetorian configure
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### SDK
|
|
235
|
+
Both `Guard` and `Chariot` classes are available and interchangeable:
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
# New (preferred):
|
|
239
|
+
from praetorian_cli.sdk.guard import Guard
|
|
240
|
+
guard = Guard(Keychain())
|
|
241
|
+
|
|
242
|
+
# Legacy (still supported):
|
|
243
|
+
from praetorian_cli.sdk.chariot import Chariot
|
|
244
|
+
chariot = Chariot(Keychain())
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Configuration
|
|
248
|
+
The keychain file and environment variables remain unchanged. Existing configurations will continue to work without modification.
|
|
@@ -1,31 +1,32 @@
|
|
|
1
1
|
praetorian_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
praetorian_cli/main.py,sha256=
|
|
2
|
+
praetorian_cli/main.py,sha256=B8Y5N6kSzOEAjSN3fPT8Urn2TvPPiHnauCeQcAlwpr0,2435
|
|
3
3
|
praetorian_cli/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
praetorian_cli/handlers/add.py,sha256=
|
|
4
|
+
praetorian_cli/handlers/add.py,sha256=CXABvU4wP4BszyefLvetuIqr9aOnG9-I4JzNWww2yBs,16246
|
|
5
5
|
praetorian_cli/handlers/aegis.py,sha256=1259bNmoUOVhcs7GqI8TyTyCI_ZvKzEPvfUVvAHcTzU,3936
|
|
6
|
-
praetorian_cli/handlers/agent.py,sha256=
|
|
7
|
-
praetorian_cli/handlers/chariot.py,sha256=
|
|
6
|
+
praetorian_cli/handlers/agent.py,sha256=pk9D-cNdJTJRQCbSE4YZXjn-ZoBOBNnGEwWLlLOAe7M,3157
|
|
7
|
+
praetorian_cli/handlers/chariot.py,sha256=BXO0biKVoy1EwulWPxFZ8NbaVoiMxUKl6lclw3Z1aJY,609
|
|
8
8
|
praetorian_cli/handlers/cli_decorators.py,sha256=ZLPMZH9i-vNtTT0Y0qnYQsGw8B4lxIdEE1vBX9i75Wc,2884
|
|
9
9
|
praetorian_cli/handlers/configure.py,sha256=8ABvisb_a4WekVVZ5kEhT1m4Mn2xV5RsO_xO_7f6TkM,1317
|
|
10
|
-
praetorian_cli/handlers/delete.py,sha256=
|
|
10
|
+
praetorian_cli/handlers/delete.py,sha256=tUnuM_wFoKyckK4UqzN7CouD-26vsQcqqqh8lFhtZx0,4429
|
|
11
11
|
praetorian_cli/handlers/enrich.py,sha256=KRvOAuW7mQdYg_k_XOTwtPmnf8rxOAtoL00Bb0e-N-s,485
|
|
12
|
-
praetorian_cli/handlers/get.py,sha256
|
|
13
|
-
praetorian_cli/handlers/imports.py,sha256=
|
|
12
|
+
praetorian_cli/handlers/get.py,sha256=2rMuv8F-pPo66PTLjQBoNpEsWIEMsTrrDE04FvcdT7s,9586
|
|
13
|
+
praetorian_cli/handlers/imports.py,sha256=UudGhvMBJg_yE2vWTAFFSgn0y6muEiNe_g8xJWJdtOw,1651
|
|
14
14
|
praetorian_cli/handlers/link.py,sha256=7JjHoawFZbcfropHVp8IX3Ekci6theAlcYSTPXnrFHQ,1776
|
|
15
|
-
praetorian_cli/handlers/list.py,sha256=
|
|
15
|
+
praetorian_cli/handlers/list.py,sha256=vpyJZNtUOwN2BgwBxHFg2Sc-XGvTPosG23DZUk8cJFA,13213
|
|
16
16
|
praetorian_cli/handlers/script.py,sha256=x_nWTlv0_9dLrQ4KulzKX11QwJHdtm6hWtZYgKzKcs4,2040
|
|
17
|
-
praetorian_cli/handlers/search.py,sha256=
|
|
17
|
+
praetorian_cli/handlers/search.py,sha256=juDemeZsD1reTHp73V7e10o52uleOg8AqF3olpkZC4E,2631
|
|
18
18
|
praetorian_cli/handlers/ssh_utils.py,sha256=53Kke-iFH4sJoCcweiT8q4WVRlaA7SvR5CCqdGFxHps,5903
|
|
19
19
|
praetorian_cli/handlers/test.py,sha256=uhARoRolaJf6DMRNX-1aj8SDYe1wAvhYDOBYWH39sqo,932
|
|
20
20
|
praetorian_cli/handlers/unlink.py,sha256=nUTGXZ7JBXwuHy2nzvL79sSO95Vyc0PftM6rm-9YWt8,1725
|
|
21
|
-
praetorian_cli/handlers/update.py,sha256=
|
|
21
|
+
praetorian_cli/handlers/update.py,sha256=YdxOp0O9-lB-yBYK3GZZE_dBcriCixyFly8Tplj6wzM,3338
|
|
22
22
|
praetorian_cli/handlers/utils.py,sha256=PaHyDTF2uj6mMMIr1pP1Bfh22H3dMx_UWnJgXK7_yuY,2639
|
|
23
23
|
praetorian_cli/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
24
|
praetorian_cli/scripts/utils.py,sha256=lGCf4trEpsfECa9U42pDJ-f48EimlS-hG6AjnKjNt4I,501
|
|
25
25
|
praetorian_cli/scripts/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
26
|
praetorian_cli/scripts/commands/nmap-example.py,sha256=varKTkHKG4DAs9Ssf0c6SygP9GfuCG01aFxhfvixLM0,2727
|
|
27
27
|
praetorian_cli/sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
-
praetorian_cli/sdk/chariot.py,sha256=
|
|
28
|
+
praetorian_cli/sdk/chariot.py,sha256=XRF0iWXw4ylGtlWouc6wr4g9oSR0KfzPfe8f5-3AdVY,14386
|
|
29
|
+
praetorian_cli/sdk/guard.py,sha256=ojyyr16MXMsuAmAL6RVqH3HtBKdedDnLQSm4nnkxnDo,121
|
|
29
30
|
praetorian_cli/sdk/keychain.py,sha256=KCz2mOTv09jgg6mrZQooRg1VrnvF9W0_BjRlEQhbzqU,7468
|
|
30
31
|
praetorian_cli/sdk/mcp_server.py,sha256=8UoTotD4UVl-tp1gqMjkOVpO__KeGCvy7mIpKXVc8Rg,8750
|
|
31
32
|
praetorian_cli/sdk/entities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -42,12 +43,12 @@ praetorian_cli/sdk/entities/files.py,sha256=Gw9Yt_Cm2xR-Vu8vjaekMHbGldhe930WJjje
|
|
|
42
43
|
praetorian_cli/sdk/entities/integrations.py,sha256=NVaW_dWbnMkMIs-EYr2W7QAeamPVwLhmM2ppdMJmsK0,3176
|
|
43
44
|
praetorian_cli/sdk/entities/jobs.py,sha256=k4QFw4qR5MdVyMAYstfnRIWr_ZH4q1PwDyUEGC-qBSM,8269
|
|
44
45
|
praetorian_cli/sdk/entities/keys.py,sha256=PgoGa3xyLMzWrIIQ8zgi7bfZiUFFumPtMDo64GjhdjE,6089
|
|
45
|
-
praetorian_cli/sdk/entities/preseeds.py,sha256=
|
|
46
|
+
praetorian_cli/sdk/entities/preseeds.py,sha256=C_FdoUnU5afwEgd7TWpeh4DMMXgEdlaIVTVJGqPGqO8,9298
|
|
46
47
|
praetorian_cli/sdk/entities/risks.py,sha256=D2A-Ikl91QeNiRrWwozLFD61uQ0LJ6oYeCW-8qy5iyo,8273
|
|
47
48
|
praetorian_cli/sdk/entities/scanners.py,sha256=QCr5QlBy4jfBh8HRvZt9CoZTgNqLNnKNrI4sdfJf0jE,423
|
|
48
49
|
praetorian_cli/sdk/entities/schema.py,sha256=CPVws1CdRHyOAI7oT9A20WGOCZozTFqZnfo5ox3v0HQ,807
|
|
49
50
|
praetorian_cli/sdk/entities/search.py,sha256=9vTy9HZY2BTlqf5Zwpdl8HCRIfSEvWDRij2_-rAp2Ng,16938
|
|
50
|
-
praetorian_cli/sdk/entities/seeds.py,sha256=
|
|
51
|
+
praetorian_cli/sdk/entities/seeds.py,sha256=4YMOOGDJHsSO4O7GMTm8p2nkTOdUTBxXr8V7y6e-Yjc,6010
|
|
51
52
|
praetorian_cli/sdk/entities/settings.py,sha256=F-pRCA6UvbdtnjHOLpEG2lN9ws8dcnBNcep-DFlXeTY,2750
|
|
52
53
|
praetorian_cli/sdk/entities/statistics.py,sha256=gtX-NN7r_RsNjDjlQ-zspmzG_0bzBqBFarCuM4NO-EA,7085
|
|
53
54
|
praetorian_cli/sdk/entities/webhook.py,sha256=7Bqt20GlJFbZTlmQwYTuUadsUQvydym6S4kGn9zYa50,6220
|
|
@@ -72,14 +73,14 @@ praetorian_cli/sdk/test/test_file.py,sha256=ZpFBSKbfq9kzfQdy_tmwCH8rwYxW6GB4NlhT
|
|
|
72
73
|
praetorian_cli/sdk/test/test_job.py,sha256=J7VqnVxRfvbpxNnYN9Rt3LvxmbNvSKwVqsSZxFEW1xc,1916
|
|
73
74
|
praetorian_cli/sdk/test/test_key.py,sha256=yDrR0-JLwMB3eDx-tI-ss8GGbiJaJijE4dAK8-FUuzc,1425
|
|
74
75
|
praetorian_cli/sdk/test/test_mcp.py,sha256=Ltg283j4Q12dcfCgUgDMKk6neRdDaThcndyywZl5XWs,938
|
|
75
|
-
praetorian_cli/sdk/test/test_preseed.py,sha256=
|
|
76
|
+
praetorian_cli/sdk/test/test_preseed.py,sha256=ju82GVVcRguNcuGKxmCO6E5KoBXCdDHmPYs_2EEB8cA,1739
|
|
76
77
|
praetorian_cli/sdk/test/test_risk.py,sha256=WuVTPxL9yj84nJx84oXVAlz0KshLxs8GG3tDeSe3QZU,3000
|
|
77
78
|
praetorian_cli/sdk/test/test_search.py,sha256=SB9Tgo_N3CCpfvla898oLB9IZyGK9Dju1r9REK6Wmek,1996
|
|
78
|
-
praetorian_cli/sdk/test/test_seed.py,sha256=
|
|
79
|
+
praetorian_cli/sdk/test/test_seed.py,sha256=nGyNsV5bKUGxm5AILY19zp-JNCw6q0IfmefRSLicgsQ,1785
|
|
79
80
|
praetorian_cli/sdk/test/test_setting.py,sha256=hdPQj71rjSYxa-PODG2D-kJd8C9gkAg1jQXnqYU4P6A,1326
|
|
80
81
|
praetorian_cli/sdk/test/test_webhook.py,sha256=FQJY76QQ6Yg2iLCGpxgKiXGI8TtmB4zTpMIM2SpYKCc,2228
|
|
81
82
|
praetorian_cli/sdk/test/test_webpage.py,sha256=jgKrsobD3ONibDIbbOT-yy7V_NmC5-LwEZmEYdYu0LI,1779
|
|
82
|
-
praetorian_cli/sdk/test/test_z_cli.py,sha256=
|
|
83
|
+
praetorian_cli/sdk/test/test_z_cli.py,sha256=Ypsd8W4fac3sbOv9zPpcaywOLZVkI6UTwyT_hhCdnjY,21040
|
|
83
84
|
praetorian_cli/sdk/test/ui_mocks.py,sha256=kiqAPxaM-_T0NQ-HgOZupNiUoJa5mE2CsyK2cXWiPws,4146
|
|
84
85
|
praetorian_cli/sdk/test/utils.py,sha256=svxMpzlaW4FRCij05cPgJFrTUEELVdt8G7SPKEdsgPo,3526
|
|
85
86
|
praetorian_cli/ui/__init__.py,sha256=wEgkrgIaoOguH1VVp2FndaGIxWmZ5CfAynXtNtZ6iTo,81
|
|
@@ -96,9 +97,9 @@ praetorian_cli/ui/aegis/commands/set.py,sha256=WJ0Qt30fBa_hyWEaawyA3h3rSeWHFPbJS
|
|
|
96
97
|
praetorian_cli/ui/aegis/commands/ssh.py,sha256=KGsNlN0i-Cwp6gWyr-cjML9_L13oE7xFenysF2pC8Rc,3045
|
|
97
98
|
praetorian_cli/ui/conversation/__init__.py,sha256=sNhNN_ZG1Va_7OLTaoXlIFL6ageKHWdufFVYw6F_aV8,90
|
|
98
99
|
praetorian_cli/ui/conversation/textual_chat.py,sha256=gGgEs7HhNAto9rTVrGbz62mG10OqmtArI-aKxFLSfVs,24405
|
|
99
|
-
praetorian_cli-2.
|
|
100
|
-
praetorian_cli-2.
|
|
101
|
-
praetorian_cli-2.
|
|
102
|
-
praetorian_cli-2.
|
|
103
|
-
praetorian_cli-2.
|
|
104
|
-
praetorian_cli-2.
|
|
100
|
+
praetorian_cli-2.3.0.dist-info/licenses/LICENSE,sha256=Zv97QripiVALv-WokW_Elsiz9vtOfbtNt1aLZhhk67I,1067
|
|
101
|
+
praetorian_cli-2.3.0.dist-info/METADATA,sha256=gclKruB9L7Un3YVnsukLGzraX1AjaRYo5TCDDdHbTbg,8533
|
|
102
|
+
praetorian_cli-2.3.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
103
|
+
praetorian_cli-2.3.0.dist-info/entry_points.txt,sha256=WAL6whBPvLyjqnKLuRNjKU4hHP9vUqlemLB2vebnu_U,95
|
|
104
|
+
praetorian_cli-2.3.0.dist-info/top_level.txt,sha256=QbUdRPGEj_TyHO-E7AD5BxFfR8ore37i273jP4Gn43c,15
|
|
105
|
+
praetorian_cli-2.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|