praetorian-cli 2.2.2__tar.gz → 2.2.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.
- {praetorian_cli-2.2.2/praetorian_cli.egg-info → praetorian_cli-2.2.3}/PKG-INFO +1 -1
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/add.py +25 -7
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/delete.py +3 -2
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/get.py +31 -2
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/list.py +8 -9
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/update.py +3 -3
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/chariot.py +4 -12
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/assets.py +30 -12
- praetorian_cli-2.2.3/praetorian_cli/sdk/entities/schema.py +27 -0
- praetorian_cli-2.2.3/praetorian_cli/sdk/entities/seeds.py +166 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/mcp_server.py +2 -3
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/model/query.py +1 -1
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/model/utils.py +2 -8
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_asset.py +2 -2
- praetorian_cli-2.2.3/praetorian_cli/sdk/test/test_seed.py +40 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_z_cli.py +22 -24
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/utils.py +16 -4
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3/praetorian_cli.egg-info}/PKG-INFO +1 -1
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli.egg-info/SOURCES.txt +1 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/setup.cfg +1 -1
- praetorian_cli-2.2.2/praetorian_cli/sdk/entities/seeds.py +0 -114
- praetorian_cli-2.2.2/praetorian_cli/sdk/test/test_seed.py +0 -41
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/LICENSE +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/MANIFEST.in +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/README.md +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/__init__.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/__init__.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/aegis.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/agent.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/chariot.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/cli_decorators.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/configure.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/enrich.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/imports.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/link.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/script.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/search.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/ssh_utils.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/test.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/unlink.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/utils.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/main.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/scripts/__init__.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/scripts/commands/__init__.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/scripts/commands/nmap-example.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/scripts/utils.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/__init__.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/__init__.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/accounts.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/aegis.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/agents.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/attributes.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/capabilities.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/configurations.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/credentials.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/definitions.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/files.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/integrations.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/jobs.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/keys.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/preseeds.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/risks.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/scanners.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/search.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/settings.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/statistics.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/webhook.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/keychain.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/model/__init__.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/model/aegis.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/model/globals.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/__init__.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/pytest.ini +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_account.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_agent.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_attribute.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_capabilities.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_configuration.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_definition.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_extend.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_file.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_job.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_key.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_mcp.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_preseed.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_risk.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_search.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_setting.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_webhook.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/ui_mocks.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/__init__.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/__init__.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/commands/__init__.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/commands/help.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/commands/info.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/commands/job.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/commands/list.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/commands/set.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/commands/ssh.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/constants.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/menu.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/utils.py +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli.egg-info/dependency_links.txt +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli.egg-info/entry_points.txt +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli.egg-info/requires.txt +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli.egg-info/top_level.txt +0 -0
- {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/pyproject.toml +0 -0
|
@@ -196,21 +196,39 @@ def attribute(sdk, key, name, value):
|
|
|
196
196
|
|
|
197
197
|
@add.command()
|
|
198
198
|
@cli_handler
|
|
199
|
-
@click.option('-
|
|
199
|
+
@click.option('-t', '--type', 'seed_type', default='asset', help='Asset type (e.g., asset, addomain)')
|
|
200
200
|
@click.option('-s', '--status', type=click.Choice([s.value for s in Seed]),
|
|
201
201
|
default=Seed.PENDING.value, help='The status of the seed', show_default=True)
|
|
202
|
-
|
|
202
|
+
@click.option('-f', '--field', 'field_list', multiple=True,
|
|
203
|
+
help='Field in format name:value (can be specified multiple times)')
|
|
204
|
+
def seed(sdk, seed_type, status, field_list):
|
|
203
205
|
""" Add a seed
|
|
204
206
|
|
|
205
|
-
Add a seed to the Chariot database.
|
|
206
|
-
|
|
207
|
+
Add a seed to the Chariot database. Seeds are now assets with special labeling.
|
|
208
|
+
You can specify the asset type and provide dynamic fields using --fields.
|
|
207
209
|
|
|
208
210
|
\b
|
|
209
211
|
Example usages:
|
|
210
|
-
- praetorian chariot add seed --dns
|
|
211
|
-
- praetorian chariot add seed --dns
|
|
212
|
+
- praetorian chariot add seed --type asset --field dns:example.com
|
|
213
|
+
- praetorian chariot add seed --type asset --field dns:example.com --status A
|
|
214
|
+
- praetorian chariot add seed --type asset --field dns:example.com --field name:1.2.3.4
|
|
215
|
+
- praetorian chariot add seed --type addomain --field domain:corp.local --field objectid:S-1-5-21-2701466056-1043032755-2418290285
|
|
212
216
|
"""
|
|
213
|
-
|
|
217
|
+
# Collect dynamic fields from the --fields option
|
|
218
|
+
dynamic_fields = {}
|
|
219
|
+
|
|
220
|
+
# Parse field_list entries (name:value format)
|
|
221
|
+
for field in field_list:
|
|
222
|
+
if ':' in field:
|
|
223
|
+
# Split only once to allow colons in the value
|
|
224
|
+
name, value = field.split(':', 1)
|
|
225
|
+
dynamic_fields[name] = value
|
|
226
|
+
else:
|
|
227
|
+
error(f"Field '{field}' is not in the format name:value")
|
|
228
|
+
return
|
|
229
|
+
|
|
230
|
+
# Call the updated add method with type and dynamic fields
|
|
231
|
+
sdk.seeds.add(status=status, seed_type=seed_type, **dynamic_fields)
|
|
214
232
|
|
|
215
233
|
|
|
216
234
|
@add.command()
|
|
@@ -88,11 +88,12 @@ def seed(chariot, key):
|
|
|
88
88
|
|
|
89
89
|
\b
|
|
90
90
|
Arguments:
|
|
91
|
-
- KEY: the key of an existing seed
|
|
91
|
+
- KEY: the key of an existing seed (now uses asset key format)
|
|
92
92
|
|
|
93
93
|
\b
|
|
94
94
|
Example usage:
|
|
95
|
-
- praetorian chariot delete seed "#
|
|
95
|
+
- praetorian chariot delete seed "#asset#example.com#example.com"
|
|
96
|
+
- praetorian chariot delete seed "#addomain#corp.local#corp.local"
|
|
96
97
|
"""
|
|
97
98
|
chariot.seeds.delete(key)
|
|
98
99
|
|
|
@@ -186,11 +186,12 @@ def seed(chariot, key):
|
|
|
186
186
|
|
|
187
187
|
\b
|
|
188
188
|
Argument:
|
|
189
|
-
- KEY: the key of an existing
|
|
189
|
+
- KEY: the key of an existing seed (now uses asset key format)
|
|
190
190
|
|
|
191
191
|
\b
|
|
192
192
|
Example usages:
|
|
193
|
-
- praetorian chariot get
|
|
193
|
+
- praetorian chariot get seed "#asset#example.com#example.com"
|
|
194
|
+
- praetorian chariot get seed "#addomain#corp.local#corp.local"
|
|
194
195
|
"""
|
|
195
196
|
print_json(chariot.seeds.get(key))
|
|
196
197
|
|
|
@@ -295,3 +296,31 @@ def scanner(chariot, key):
|
|
|
295
296
|
- praetorian chariot get scanner "#scanner#127.0.0.1"
|
|
296
297
|
"""
|
|
297
298
|
print_json(chariot.scanners.get(key))
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
@get.command()
|
|
302
|
+
@cli_handler
|
|
303
|
+
@click.option('-t', '--type', help='Optional specific entity type (e.g., asset, risk, attribute)')
|
|
304
|
+
@click.option('-d', '--details', is_flag=True, help='Further retrieve the details of the schema')
|
|
305
|
+
def schema(chariot, type, details):
|
|
306
|
+
""" Get Chariot entity schema
|
|
307
|
+
|
|
308
|
+
\b
|
|
309
|
+
Returns the JSON schema for Chariot entities. Optionally filter for a
|
|
310
|
+
specific entity type.
|
|
311
|
+
|
|
312
|
+
\b
|
|
313
|
+
Example usages:
|
|
314
|
+
- praetorian chariot get schema
|
|
315
|
+
- praetorian chariot get schema --type asset
|
|
316
|
+
- praetorian chariot get schema --type asset --details
|
|
317
|
+
"""
|
|
318
|
+
data = chariot.schema.get(type)
|
|
319
|
+
if type:
|
|
320
|
+
data = {type: data[type]}
|
|
321
|
+
|
|
322
|
+
if details:
|
|
323
|
+
print_json(data)
|
|
324
|
+
else:
|
|
325
|
+
for hit in data:
|
|
326
|
+
click.echo(hit)
|
|
@@ -26,7 +26,7 @@ def assets(chariot, filter, model_type, details, offset, page):
|
|
|
26
26
|
- praetorian chariot list assets --page all
|
|
27
27
|
- praetorian chariot list assets --type repository
|
|
28
28
|
"""
|
|
29
|
-
render_list_results(chariot.assets.list(filter, model_type,
|
|
29
|
+
render_list_results(chariot.assets.list(filter, model_type, pagination_size(page)), details)
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
@list.command()
|
|
@@ -178,24 +178,23 @@ def attributes(chariot, filter, key, details, offset, page):
|
|
|
178
178
|
|
|
179
179
|
@list.command()
|
|
180
180
|
@list_params('DNS')
|
|
181
|
-
@click.option('-t', '--type',
|
|
181
|
+
@click.option('-t', '--type', help='Filter by seed type (e.g., asset, addomain)')
|
|
182
182
|
def seeds(chariot, type, filter, details, offset, page):
|
|
183
183
|
""" List seeds
|
|
184
184
|
|
|
185
|
-
Retrieve and display a list of seeds.
|
|
185
|
+
Retrieve and display a list of seeds. Seeds are now assets with the 'Seed' label.
|
|
186
186
|
|
|
187
187
|
\b
|
|
188
188
|
Example usages:
|
|
189
189
|
- praetorian chariot list seeds
|
|
190
|
-
- praetorian chariot list seeds --type
|
|
191
|
-
- praetorian chariot list seeds --type
|
|
190
|
+
- praetorian chariot list seeds --type asset
|
|
191
|
+
- praetorian chariot list seeds --type addomain
|
|
192
|
+
- praetorian chariot list seeds --type asset --filter example.com
|
|
192
193
|
- praetorian chariot list seeds --details
|
|
193
194
|
- praetorian chariot list seeds --page all
|
|
194
195
|
"""
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
render_list_results(chariot.seeds.list(type, filter, offset, pagination_size(page)), details)
|
|
196
|
+
# Note: filter restriction removed since we're using different key format now
|
|
197
|
+
render_list_results(chariot.seeds.list(type, filter, pagination_size(page)), details)
|
|
199
198
|
|
|
200
199
|
|
|
201
200
|
@list.command()
|
|
@@ -65,12 +65,12 @@ def seed(chariot, key, status):
|
|
|
65
65
|
|
|
66
66
|
\b
|
|
67
67
|
Example usages:
|
|
68
|
-
- praetorian chariot update seed "#
|
|
69
|
-
- praetorian chariot update seed "#
|
|
68
|
+
- praetorian chariot update seed "#asset#example.com#example.com" -s A
|
|
69
|
+
- praetorian chariot update seed "#asset#1.1.1.0/24#1.1.1.0/24" -s F
|
|
70
70
|
"""
|
|
71
|
+
|
|
71
72
|
chariot.seeds.update(key, status)
|
|
72
73
|
|
|
73
|
-
|
|
74
74
|
@update.command()
|
|
75
75
|
@cli_handler
|
|
76
76
|
@click.argument('key', required=True)
|
|
@@ -16,6 +16,7 @@ from praetorian_cli.sdk.entities.keys import Keys
|
|
|
16
16
|
from praetorian_cli.sdk.entities.preseeds import Preseeds
|
|
17
17
|
from praetorian_cli.sdk.entities.risks import Risks
|
|
18
18
|
from praetorian_cli.sdk.entities.scanners import Scanners
|
|
19
|
+
from praetorian_cli.sdk.entities.schema import Schema
|
|
19
20
|
from praetorian_cli.sdk.entities.search import Search
|
|
20
21
|
from praetorian_cli.sdk.entities.seeds import Seeds
|
|
21
22
|
from praetorian_cli.sdk.entities.settings import Settings
|
|
@@ -51,6 +52,7 @@ class Chariot:
|
|
|
51
52
|
self.keys = Keys(self)
|
|
52
53
|
self.capabilities = Capabilities(self)
|
|
53
54
|
self.credentials = Credentials(self)
|
|
55
|
+
self.schema = Schema(self)
|
|
54
56
|
self.proxy = proxy
|
|
55
57
|
|
|
56
58
|
if self.proxy == '' and os.environ.get('CHARIOT_PROXY'):
|
|
@@ -70,26 +72,16 @@ class Chariot:
|
|
|
70
72
|
kwargs['proxies'] = {'http': self.proxy, 'https': self.proxy}
|
|
71
73
|
kwargs['verify'] = False
|
|
72
74
|
|
|
73
|
-
self.
|
|
75
|
+
self.add_beta_url_param(kwargs)
|
|
74
76
|
|
|
75
77
|
return requests.request(method, url, headers=self.keychain.headers(), **kwargs)
|
|
76
78
|
|
|
77
|
-
def
|
|
78
|
-
if method == 'GET' or method == 'DELETE':
|
|
79
|
-
self._add_beta_url_param(kwargs)
|
|
80
|
-
else:
|
|
81
|
-
self._add_beta_json_param(kwargs)
|
|
82
|
-
|
|
83
|
-
def _add_beta_url_param(self, kwargs: dict):
|
|
79
|
+
def add_beta_url_param(self, kwargs: dict):
|
|
84
80
|
if 'params' in kwargs:
|
|
85
81
|
kwargs['params']['beta'] = 'true'
|
|
86
82
|
else:
|
|
87
83
|
kwargs['params'] = {'beta': 'true'}
|
|
88
84
|
|
|
89
|
-
def _add_beta_json_param(self, kwargs: dict):
|
|
90
|
-
if 'json' in kwargs:
|
|
91
|
-
kwargs['json']['beta'] = True
|
|
92
|
-
|
|
93
85
|
def my(self, params: dict, pages=1) -> dict:
|
|
94
86
|
final_resp = dict()
|
|
95
87
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
from praetorian_cli.handlers.utils import error
|
|
1
2
|
from praetorian_cli.sdk.model.globals import Asset, Kind
|
|
2
|
-
from praetorian_cli.sdk.model.query import Relationship, Node, Query, asset_of_key, RISK_NODE, ATTRIBUTE_NODE
|
|
3
|
+
from praetorian_cli.sdk.model.query import Relationship, Node, Query, Filter, KIND_TO_LABEL, asset_of_key, RISK_NODE, ATTRIBUTE_NODE
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
class Assets:
|
|
@@ -76,27 +77,44 @@ class Assets:
|
|
|
76
77
|
"""
|
|
77
78
|
return self.api.delete_by_key('asset', key)
|
|
78
79
|
|
|
79
|
-
def list(self,
|
|
80
|
+
def list(self, key_prefix='', asset_type='', pages=100000) -> tuple:
|
|
80
81
|
"""
|
|
81
82
|
List assets.
|
|
82
83
|
|
|
83
|
-
:param
|
|
84
|
-
:type
|
|
84
|
+
:param key_prefix: Supply this to perform prefix-filtering of the asset key. E.g., '#asset#example.com' or '#addomain#sevenkingdoms'
|
|
85
|
+
:type key_prefix: str
|
|
85
86
|
:param asset_type: The type of asset to filter by
|
|
86
87
|
:type asset_type: str
|
|
87
|
-
:param offset: The offset of the page you want to retrieve results. If this is not supplied, this function retrieves from the first page
|
|
88
|
-
:type offset: str or None
|
|
89
88
|
:param pages: The number of pages of results to retrieve. <mcp>Start with one page of results unless specifically requested.</mcp>
|
|
90
89
|
:type pages: int
|
|
91
90
|
:return: A tuple containing (list of assets, next page offset)
|
|
92
91
|
:rtype: tuple
|
|
93
92
|
"""
|
|
94
|
-
|
|
95
|
-
if
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
asset_type =
|
|
99
|
-
|
|
93
|
+
|
|
94
|
+
if asset_type in KIND_TO_LABEL:
|
|
95
|
+
asset_type = KIND_TO_LABEL[asset_type]
|
|
96
|
+
elif not asset_type:
|
|
97
|
+
asset_type = Node.Label.ASSET
|
|
98
|
+
else:
|
|
99
|
+
raise ValueError(f'Invalid asset type: {asset_type}')
|
|
100
|
+
|
|
101
|
+
node = Node(
|
|
102
|
+
labels=[asset_type],
|
|
103
|
+
filters=[]
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
key_filter = Filter(
|
|
107
|
+
field=Filter.Field.KEY,
|
|
108
|
+
operator=Filter.Operator.STARTS_WITH,
|
|
109
|
+
value=key_prefix
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
if key_prefix:
|
|
113
|
+
node.filters.append(key_filter)
|
|
114
|
+
|
|
115
|
+
query = Query(node=node)
|
|
116
|
+
|
|
117
|
+
return self.api.search.by_query(query, pages)
|
|
100
118
|
|
|
101
119
|
def attributes(self, key):
|
|
102
120
|
"""
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
class Schema:
|
|
2
|
+
"""Access Chariot entity schemas via the SDK.
|
|
3
|
+
|
|
4
|
+
Methods in this class are accessed from `sdk.schema`, where `sdk` is an
|
|
5
|
+
instance of `Chariot`.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
def __init__(self, api):
|
|
9
|
+
self.api = api
|
|
10
|
+
|
|
11
|
+
def get(self, entity_type: str | None = None) -> dict:
|
|
12
|
+
"""Get schema information for entity types.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
entity_type: Optional specific entity type. If provided and it exists,
|
|
16
|
+
only that schema is returned. If not provided, all schemas are returned.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
dict: Schema information.
|
|
20
|
+
"""
|
|
21
|
+
result = self.api.get('schema', )
|
|
22
|
+
if entity_type:
|
|
23
|
+
if entity_type not in result:
|
|
24
|
+
return {}
|
|
25
|
+
return {entity_type: result[entity_type]}
|
|
26
|
+
return result
|
|
27
|
+
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
from praetorian_cli.handlers.utils import error
|
|
2
|
+
from praetorian_cli.sdk.model.globals import Seed, Kind
|
|
3
|
+
from praetorian_cli.sdk.model.query import Query, Node, Filter, KIND_TO_LABEL
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Seeds:
|
|
7
|
+
""" The methods in this class are to be assessed from sdk.seeds, where sdk is an instance
|
|
8
|
+
of Chariot. """
|
|
9
|
+
|
|
10
|
+
def __init__(self, api):
|
|
11
|
+
self.api = api
|
|
12
|
+
|
|
13
|
+
def add(self, status=Seed.PENDING.value, seed_type=Kind.ASSET.value, **kwargs):
|
|
14
|
+
"""
|
|
15
|
+
Add a seed of specified type with dynamic fields.
|
|
16
|
+
|
|
17
|
+
:param status: Status for backward compatibility
|
|
18
|
+
:type status: str or None
|
|
19
|
+
:param type: Asset type (e.g., 'asset', 'addomain', etc.)
|
|
20
|
+
:type type: str
|
|
21
|
+
:param kwargs: Dynamic fields for the asset type
|
|
22
|
+
:return: The seed that was added
|
|
23
|
+
:rtype: dict
|
|
24
|
+
"""
|
|
25
|
+
# Handle status if provided
|
|
26
|
+
kwargs['status'] = status
|
|
27
|
+
|
|
28
|
+
# Build payload with type wrapper
|
|
29
|
+
payload = {
|
|
30
|
+
'type': seed_type,
|
|
31
|
+
'model': kwargs
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return self.api.upsert('seed', payload)
|
|
35
|
+
|
|
36
|
+
def get(self, key):
|
|
37
|
+
"""
|
|
38
|
+
Get details of a seed by key.
|
|
39
|
+
|
|
40
|
+
:param key: Entity key (e.g., '#asset#example.com#example.com')
|
|
41
|
+
:type key: str
|
|
42
|
+
:return: The seed matching the specified key, or None if not found
|
|
43
|
+
:rtype: dict or None
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
# Create a Filter for the key field
|
|
47
|
+
key_filter = Filter(
|
|
48
|
+
field=Filter.Field.KEY,
|
|
49
|
+
operator=Filter.Operator.EQUAL,
|
|
50
|
+
value=key
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Create a Node with Seed label and key filter
|
|
54
|
+
node = Node(
|
|
55
|
+
labels=[Node.Label.SEED],
|
|
56
|
+
filters=[key_filter]
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Create the Query object
|
|
60
|
+
query = Query(node=node)
|
|
61
|
+
|
|
62
|
+
# Call by_query with the constructed Query object
|
|
63
|
+
results_tuple = self.api.search.by_query(query)
|
|
64
|
+
if not results_tuple:
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
results, _ = results_tuple
|
|
68
|
+
if len(results) == 0:
|
|
69
|
+
return None
|
|
70
|
+
return results[0]
|
|
71
|
+
|
|
72
|
+
def update(self, key, status=None):
|
|
73
|
+
"""
|
|
74
|
+
Update seed fields dynamically.
|
|
75
|
+
|
|
76
|
+
:param key: Seed/Asset key (e.g., '#seed#domain#example.com' or '#asset#domain#example.com')
|
|
77
|
+
:type key: str
|
|
78
|
+
:param status: Status for backward compatibility (can be positional)
|
|
79
|
+
:type status: str or None
|
|
80
|
+
:param kwargs: Fields to update
|
|
81
|
+
:return: The updated seed, or None if the seed was not found
|
|
82
|
+
:rtype: dict or None
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
seed = self.get(key) # This already handles old key format conversion
|
|
86
|
+
if seed:
|
|
87
|
+
update_payload = {
|
|
88
|
+
'key': key,
|
|
89
|
+
'status': status
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return self.api.upsert('seed', update_payload)
|
|
93
|
+
else:
|
|
94
|
+
error(f'Seed {key} not found.')
|
|
95
|
+
|
|
96
|
+
def delete(self, key):
|
|
97
|
+
"""
|
|
98
|
+
Delete seed (supports both old and new key formats).
|
|
99
|
+
|
|
100
|
+
:param key: Seed/Asset key (e.g., '#asset#domain#example.com')
|
|
101
|
+
:type key: str
|
|
102
|
+
:return: The seed that was marked as deleted, or None if the seed was not found
|
|
103
|
+
:rtype: dict or None
|
|
104
|
+
"""
|
|
105
|
+
seed = self.get(key) # This already handles old key format conversion
|
|
106
|
+
|
|
107
|
+
if seed:
|
|
108
|
+
delete_payload = {
|
|
109
|
+
'key': key,
|
|
110
|
+
'status': Seed.DELETED.value
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return self.api.upsert('seed', delete_payload)
|
|
114
|
+
else:
|
|
115
|
+
error(f'Seed {key} not found.')
|
|
116
|
+
|
|
117
|
+
def list(self, seed_type=Kind.SEED.value, key_prefix='', pages=100000) -> tuple:
|
|
118
|
+
"""
|
|
119
|
+
List seeds by querying assets with 'Seed' label.
|
|
120
|
+
|
|
121
|
+
:param seed_type: Optional asset seed_type filter (e.g., 'asset', 'addomain')
|
|
122
|
+
:seed_type seed_type: str or None
|
|
123
|
+
:param key_prefix: Filter by key prefix
|
|
124
|
+
:seed_type key_prefix: str
|
|
125
|
+
:param pages: The number of pages of results to retrieve. <mcp>Start with one page of results unless specifically requested.</mcp>
|
|
126
|
+
:seed_type pages: int
|
|
127
|
+
:return: A tuple containing (list of seeds, next page offset)
|
|
128
|
+
:rseed_type: tuple
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
if seed_type in KIND_TO_LABEL:
|
|
132
|
+
seed_type = KIND_TO_LABEL[seed_type]
|
|
133
|
+
elif not seed_type:
|
|
134
|
+
seed_type = Node.Label.SEED
|
|
135
|
+
else:
|
|
136
|
+
raise ValueError(f'Invalid seed type: {seed_type}')
|
|
137
|
+
|
|
138
|
+
node = Node(
|
|
139
|
+
labels=[seed_type],
|
|
140
|
+
filters=[]
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
key_filter = Filter(
|
|
144
|
+
field=Filter.Field.KEY,
|
|
145
|
+
operator=Filter.Operator.STARTS_WITH,
|
|
146
|
+
value=key_prefix
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
if key_prefix:
|
|
150
|
+
node.filters.append(key_filter)
|
|
151
|
+
|
|
152
|
+
query = Query(node=node)
|
|
153
|
+
|
|
154
|
+
return self.api.search.by_query(query, pages)
|
|
155
|
+
|
|
156
|
+
def attributes(self, key):
|
|
157
|
+
"""
|
|
158
|
+
Get attributes associated with a seed.
|
|
159
|
+
|
|
160
|
+
:param key: Entity key in format #seed#{type}#{dns} where type is 'domain', 'ip', or 'cidr' and dns is the seed value
|
|
161
|
+
:type key: str
|
|
162
|
+
:return: List of attributes associated with the seed
|
|
163
|
+
:rtype: list
|
|
164
|
+
"""
|
|
165
|
+
attributes, _ = self.api.search.by_source(key, Kind.ATTRIBUTE.value)
|
|
166
|
+
return attributes
|
|
@@ -8,7 +8,6 @@ from mcp.server.lowlevel import Server
|
|
|
8
8
|
from mcp.server.stdio import stdio_server
|
|
9
9
|
from mcp.types import Tool, TextContent
|
|
10
10
|
|
|
11
|
-
|
|
12
11
|
class MCPServer:
|
|
13
12
|
def __init__(self, chariot_instance, allowable_tools: Optional[List[str]] = None):
|
|
14
13
|
self.chariot = chariot_instance
|
|
@@ -157,7 +156,7 @@ class MCPServer:
|
|
|
157
156
|
tools = []
|
|
158
157
|
for tool_name, tool_info in self.discovered_tools.items():
|
|
159
158
|
parameters = self._extract_parameters_from_doc(tool_info['doc'], tool_info['signature'])
|
|
160
|
-
|
|
159
|
+
|
|
161
160
|
properties = {}
|
|
162
161
|
required = []
|
|
163
162
|
|
|
@@ -170,7 +169,7 @@ class MCPServer:
|
|
|
170
169
|
"description": param_info["description"]
|
|
171
170
|
}
|
|
172
171
|
|
|
173
|
-
if param_info
|
|
172
|
+
if param_info.get("required", False):
|
|
174
173
|
required.append(param_name)
|
|
175
174
|
|
|
176
175
|
tool_schema = {
|
|
@@ -15,14 +15,11 @@ def integration_key(dns, name):
|
|
|
15
15
|
def risk_key(dns, name):
|
|
16
16
|
return f'#risk#{dns}#{name}'
|
|
17
17
|
|
|
18
|
-
|
|
19
18
|
def attribute_key(name, value, source_key):
|
|
20
19
|
return f'#attribute#{name}#{value}{source_key}'
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return f'#seed#{type}#{dns}'
|
|
25
|
-
|
|
21
|
+
def seed_asset_key(dns):
|
|
22
|
+
return f'#asset#{dns}#{dns}'
|
|
26
23
|
|
|
27
24
|
def preseed_key(type, title, value):
|
|
28
25
|
return f'#preseed#{type}#{title}#{value}'
|
|
@@ -32,6 +29,3 @@ def setting_key(name):
|
|
|
32
29
|
|
|
33
30
|
def configuration_key(name):
|
|
34
31
|
return f'#configuration#{name}'
|
|
35
|
-
|
|
36
|
-
def seed_status(type, status_code):
|
|
37
|
-
return f'{type}#{status_code}'
|
|
@@ -42,7 +42,7 @@ class TestAsset:
|
|
|
42
42
|
assert any([a['group'] == self.asset_dns for a in deleted_assets])
|
|
43
43
|
|
|
44
44
|
def test_add_ad_domain(self):
|
|
45
|
-
asset = self.sdk.assets.add(self.ad_domain_name, self.
|
|
45
|
+
asset = self.sdk.assets.add(self.ad_domain_name, self.ad_object_id, status=Asset.ACTIVE.value, surface='test-surface', type=Kind.ADDOMAIN.value)
|
|
46
46
|
assert asset['key'] == self.ad_domain_key
|
|
47
47
|
assert len(asset['attackSurface']) == 1
|
|
48
48
|
assert 'test-surface' in asset['attackSurface']
|
|
@@ -51,7 +51,7 @@ class TestAsset:
|
|
|
51
51
|
def test_get_ad_domain(self):
|
|
52
52
|
asset = self.sdk.assets.get(self.ad_domain_key)
|
|
53
53
|
assert asset['key'] == self.ad_domain_key
|
|
54
|
-
assert asset['
|
|
54
|
+
assert asset['domain'] == self.ad_domain_name
|
|
55
55
|
assert asset['status'] == Asset.ACTIVE.value
|
|
56
56
|
|
|
57
57
|
def test_list_ad_domain(self):
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from praetorian_cli.sdk.model.globals import Seed, Kind
|
|
4
|
+
from praetorian_cli.sdk.test.utils import make_test_values, clean_test_entities, setup_chariot
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.mark.coherence
|
|
8
|
+
class TestSeed:
|
|
9
|
+
|
|
10
|
+
def setup_class(self):
|
|
11
|
+
self.sdk = setup_chariot()
|
|
12
|
+
make_test_values(self)
|
|
13
|
+
|
|
14
|
+
def test_add_asset_seed(self):
|
|
15
|
+
seed = self.sdk.seeds.add(dns=self.seed_asset_dns)
|
|
16
|
+
assert seed['key'] == self.seed_asset_key
|
|
17
|
+
|
|
18
|
+
def test_get_seed(self):
|
|
19
|
+
a = self.get_seed()
|
|
20
|
+
assert a['dns'] == self.seed_asset_dns
|
|
21
|
+
assert a['status'] == Seed.PENDING.value
|
|
22
|
+
|
|
23
|
+
def test_list_seed(self):
|
|
24
|
+
results, _ = self.sdk.seeds.list(Kind.ASSET.value, f"#asset#{self.seed_asset_dns}")
|
|
25
|
+
assert len(results) == 1
|
|
26
|
+
assert results[0]['dns'] == self.seed_asset_dns
|
|
27
|
+
|
|
28
|
+
def test_update_seed(self):
|
|
29
|
+
self.sdk.seeds.update(self.seed_asset_key, Seed.ACTIVE.value)
|
|
30
|
+
assert self.get_seed()['status'] == Seed.ACTIVE.value
|
|
31
|
+
|
|
32
|
+
def test_delete_seed(self):
|
|
33
|
+
self.sdk.seeds.delete(self.seed_asset_key)
|
|
34
|
+
assert self.sdk.seeds.get(self.seed_asset_key)['status'] == Seed.DELETED.value
|
|
35
|
+
|
|
36
|
+
def get_seed(self):
|
|
37
|
+
return self.sdk.seeds.get(self.seed_asset_key)
|
|
38
|
+
|
|
39
|
+
def teardown_class(self):
|
|
40
|
+
clean_test_entities(self.sdk, self)
|