praetorian-cli 2.1.3__tar.gz → 2.1.5__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.1.3/praetorian_cli.egg-info → praetorian_cli-2.1.5}/PKG-INFO +1 -1
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/add.py +5 -4
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/chariot.py +4 -2
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/cli_decorators.py +5 -2
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/list.py +5 -4
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/main.py +3 -2
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/chariot.py +31 -17
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/assets.py +13 -8
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/model/globals.py +3 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/model/query.py +10 -2
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/model/utils.py +8 -0
- praetorian_cli-2.1.5/praetorian_cli/sdk/test/test_asset.py +83 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_search.py +3 -3
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_z_cli.py +0 -17
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/utils.py +6 -1
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5/praetorian_cli.egg-info}/PKG-INFO +1 -1
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/setup.cfg +1 -1
- praetorian_cli-2.1.3/praetorian_cli/sdk/test/test_asset.py +0 -50
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/LICENSE +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/MANIFEST.in +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/README.md +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/__init__.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/__init__.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/agent.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/configure.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/delete.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/enrich.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/get.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/imports.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/link.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/script.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/search.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/test.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/unlink.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/update.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/utils.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/scripts/__init__.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/scripts/commands/__init__.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/scripts/commands/nmap-example.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/scripts/utils.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/__init__.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/__init__.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/accounts.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/agents.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/attributes.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/capabilities.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/configurations.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/credentials.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/definitions.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/files.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/integrations.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/jobs.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/keys.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/preseeds.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/risks.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/search.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/seeds.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/settings.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/statistics.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/webhook.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/keychain.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/model/__init__.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/__init__.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/pytest.ini +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_account.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_agent.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_attribute.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_capabilities.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_configuration.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_definition.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_extend.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_file.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_job.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_key.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_preseed.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_risk.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_seed.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_setting.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_webhook.py +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli.egg-info/SOURCES.txt +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli.egg-info/dependency_links.txt +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli.egg-info/entry_points.txt +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli.egg-info/requires.txt +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli.egg-info/top_level.txt +0 -0
- {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/pyproject.toml +0 -0
|
@@ -6,7 +6,7 @@ import click
|
|
|
6
6
|
from praetorian_cli.handlers.chariot import chariot
|
|
7
7
|
from praetorian_cli.handlers.cli_decorators import cli_handler, praetorian_only
|
|
8
8
|
from praetorian_cli.handlers.utils import error
|
|
9
|
-
from praetorian_cli.sdk.model.globals import AddRisk, Asset, Seed
|
|
9
|
+
from praetorian_cli.sdk.model.globals import AddRisk, Asset, Seed, Kind
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
@chariot.group()
|
|
@@ -18,11 +18,12 @@ def add():
|
|
|
18
18
|
@add.command()
|
|
19
19
|
@cli_handler
|
|
20
20
|
@click.option('-d', '--dns', required=True, help='The DNS of the asset')
|
|
21
|
-
@click.option('-n', '--name', required=False, help='The name of the asset, e.g, IP address
|
|
21
|
+
@click.option('-n', '--name', required=False, help='The name of the asset, e.g, IP address')
|
|
22
|
+
@click.option('-t', '--type', 'asset_type', required=False, help='The type of the asset (asset, repository, etc.)', default=Kind.ASSET.value)
|
|
22
23
|
@click.option('-s', '--status', type=click.Choice([s.value for s in Asset]), required=False,
|
|
23
24
|
default=Asset.ACTIVE.value, help=f'Status of the asset', show_default=True)
|
|
24
25
|
@click.option('-f', '--surface', required=False, default='', help=f'Attack surface of the asset', show_default=False)
|
|
25
|
-
def asset(sdk, name, dns, status, surface):
|
|
26
|
+
def asset(sdk, name, dns, asset_type, status, surface):
|
|
26
27
|
""" Add an asset
|
|
27
28
|
|
|
28
29
|
Add an asset to the Chariot database. This command requires a DNS name for the asset.
|
|
@@ -43,7 +44,7 @@ def asset(sdk, name, dns, status, surface):
|
|
|
43
44
|
"""
|
|
44
45
|
if not name:
|
|
45
46
|
name = dns
|
|
46
|
-
sdk.assets.add(dns, name, status, surface)
|
|
47
|
+
sdk.assets.add(dns, name, asset_type, status, surface)
|
|
47
48
|
|
|
48
49
|
|
|
49
50
|
@add.command()
|
|
@@ -10,6 +10,8 @@ def chariot(click_context):
|
|
|
10
10
|
""" Command group for interacting with the Chariot 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
|
-
keychain = click_context.obj
|
|
14
|
-
|
|
13
|
+
keychain = click_context.obj['keychain']
|
|
14
|
+
proxy = click_context.obj['proxy']
|
|
15
|
+
|
|
16
|
+
chariot = Chariot(keychain=keychain, proxy=proxy)
|
|
15
17
|
click_context.obj = chariot
|
|
@@ -52,13 +52,16 @@ def cli_handler(func):
|
|
|
52
52
|
return func
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
def list_params(filter_by, has_details=True):
|
|
55
|
+
def list_params(filter_by, has_details=True, has_filter=True, has_type=False):
|
|
56
56
|
def decorator(func):
|
|
57
|
-
func = click.option('-f', '--filter', default='', help=f'Filter by {filter_by}')(func)
|
|
58
57
|
func = pagination(func)
|
|
59
58
|
func = cli_handler(func)
|
|
59
|
+
if has_filter:
|
|
60
|
+
func = click.option('-f', '--filter', default='', help=f'Filter by {filter_by}')(func)
|
|
60
61
|
if has_details:
|
|
61
62
|
func = click.option('-d', '--details', is_flag=True, default=False, help='Show detailed information')(func)
|
|
63
|
+
if has_type:
|
|
64
|
+
func = click.option('-t', '--type', "model_type", default='', help='Select only a subset by type')(func)
|
|
62
65
|
return func
|
|
63
66
|
|
|
64
67
|
return decorator
|
|
@@ -12,8 +12,8 @@ def list():
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
@list.command()
|
|
15
|
-
@list_params('DNS')
|
|
16
|
-
def assets(chariot, filter, details, offset, page):
|
|
15
|
+
@list_params('DNS', has_type=True)
|
|
16
|
+
def assets(chariot, filter, model_type, details, offset, page):
|
|
17
17
|
""" List assets
|
|
18
18
|
|
|
19
19
|
Retrieve and display a list of assets.
|
|
@@ -24,8 +24,9 @@ def assets(chariot, filter, details, offset, page):
|
|
|
24
24
|
- praetorian chariot list assets --filter api.example.com
|
|
25
25
|
- praetorian chariot list assets --details
|
|
26
26
|
- praetorian chariot list assets --page all
|
|
27
|
+
- praetorian chariot list assets --type repository
|
|
27
28
|
"""
|
|
28
|
-
render_list_results(chariot.assets.list(filter, offset, pagination_size(page)), details)
|
|
29
|
+
render_list_results(chariot.assets.list(filter, model_type, offset, pagination_size(page)), details)
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
@list.command()
|
|
@@ -309,7 +310,7 @@ def keys(chariot, details, offset, page):
|
|
|
309
310
|
render_list_results(chariot.keys.list(offset, pagination_size(page)), details)
|
|
310
311
|
|
|
311
312
|
@list.command()
|
|
312
|
-
@list_params('credential ID')
|
|
313
|
+
@list_params('credential ID', has_details=False, has_filter=False)
|
|
313
314
|
def credentials(chariot, offset, page):
|
|
314
315
|
""" List credentials
|
|
315
316
|
|
|
@@ -22,13 +22,14 @@ from praetorian_cli.sdk.keychain import Keychain
|
|
|
22
22
|
@click.option('--profile', default='United States', help='The profile to use in the keychain file', show_default=True)
|
|
23
23
|
@click.option('--account', default=None, help='Assume role into this account')
|
|
24
24
|
@click.option('--debug', is_flag=True, default=False, help='Run the CLI in debug mode')
|
|
25
|
+
@click.option('--proxy', default='', help='The proxy to use in the CLI')
|
|
25
26
|
@click.pass_context
|
|
26
27
|
@click.version_option()
|
|
27
|
-
def main(click_context, profile, account, debug):
|
|
28
|
+
def main(click_context, profile, account, debug, proxy):
|
|
28
29
|
if debug:
|
|
29
30
|
click.echo('Running in debug mode.')
|
|
30
31
|
chariot.is_debug = debug
|
|
31
|
-
click_context.obj = Keychain(profile, account)
|
|
32
|
+
click_context.obj = {'keychain': Keychain(profile, account), 'proxy': proxy}
|
|
32
33
|
praetorian_cli.handlers.script.load_dynamic_commands()
|
|
33
34
|
|
|
34
35
|
|
|
@@ -28,7 +28,7 @@ from praetorian_cli.sdk.model.query import Query, my_params_to_query, DEFAULT_PA
|
|
|
28
28
|
|
|
29
29
|
class Chariot:
|
|
30
30
|
|
|
31
|
-
def __init__(self, keychain: Keychain):
|
|
31
|
+
def __init__(self, keychain: Keychain, proxy: str=''):
|
|
32
32
|
self.keychain = keychain
|
|
33
33
|
self.assets = Assets(self)
|
|
34
34
|
self.seeds = Seeds(self)
|
|
@@ -49,6 +49,23 @@ class Chariot:
|
|
|
49
49
|
self.keys = Keys(self)
|
|
50
50
|
self.capabilities = Capabilities(self)
|
|
51
51
|
self.credentials = Credentials(self)
|
|
52
|
+
self.proxy = proxy
|
|
53
|
+
|
|
54
|
+
if self.proxy:
|
|
55
|
+
import urllib3
|
|
56
|
+
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|
57
|
+
|
|
58
|
+
def _make_request(self, method: str, url: str, **kwargs) -> requests.Response:
|
|
59
|
+
"""
|
|
60
|
+
Centralized method to make HTTP requests with proxy configuration.
|
|
61
|
+
Automatically applies proxy settings and verify=False if proxy is defined.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
if self.proxy:
|
|
65
|
+
kwargs['proxies'] = {'http': self.proxy, 'https': self.proxy}
|
|
66
|
+
kwargs['verify'] = False
|
|
67
|
+
|
|
68
|
+
return requests.request(method, url, headers=self.keychain.headers(), **kwargs)
|
|
52
69
|
|
|
53
70
|
def my(self, params: dict, pages=1) -> dict:
|
|
54
71
|
final_resp = dict()
|
|
@@ -60,7 +77,7 @@ class Chariot:
|
|
|
60
77
|
|
|
61
78
|
# The search is on data in DynamoDB, which uses DynamoDB's native offset format.
|
|
62
79
|
for _ in range(pages):
|
|
63
|
-
resp =
|
|
80
|
+
resp = self._make_request('GET', self.url('/my'), params=params)
|
|
64
81
|
process_failure(resp)
|
|
65
82
|
resp = resp.json()
|
|
66
83
|
extend(final_resp, resp)
|
|
@@ -87,7 +104,7 @@ class Chariot:
|
|
|
87
104
|
final_resp = dict()
|
|
88
105
|
|
|
89
106
|
while pages > 0:
|
|
90
|
-
resp =
|
|
107
|
+
resp = self._make_request('POST', self.url('/my'), json=raw_query, params=params)
|
|
91
108
|
if is_query_limit_failure(resp):
|
|
92
109
|
# In this block, the data size is too large for the number of records requested in raw_query['limit'].
|
|
93
110
|
# We need to halve the page size: LIMIT = LIMIT / 2
|
|
@@ -114,22 +131,22 @@ class Chariot:
|
|
|
114
131
|
return final_resp
|
|
115
132
|
|
|
116
133
|
def post(self, type: str, body: dict, params: dict = {}) -> dict:
|
|
117
|
-
resp =
|
|
134
|
+
resp = self._make_request('POST', self.url(f'/{type}'), json=body, params=params)
|
|
118
135
|
process_failure(resp)
|
|
119
136
|
return resp.json()
|
|
120
137
|
|
|
121
138
|
def put(self, type: str, body: dict, params: dict = {}) -> dict:
|
|
122
|
-
resp =
|
|
139
|
+
resp = self._make_request('PUT', self.url(f'/{type}'), json=body, params=params)
|
|
123
140
|
process_failure(resp)
|
|
124
141
|
return resp.json()
|
|
125
142
|
|
|
126
143
|
def get(self, type: str, params: dict = {}) -> dict:
|
|
127
|
-
resp =
|
|
144
|
+
resp = self._make_request('GET', self.url(f'/{type}'), params=params)
|
|
128
145
|
process_failure(resp)
|
|
129
146
|
return resp.json()
|
|
130
147
|
|
|
131
148
|
def delete(self, type: str, body: dict, params: dict) -> dict:
|
|
132
|
-
resp =
|
|
149
|
+
resp = self._make_request('DELETE', self.url(f'/{type}'), json=body, params=params)
|
|
133
150
|
process_failure(resp)
|
|
134
151
|
return resp.json()
|
|
135
152
|
|
|
@@ -149,14 +166,12 @@ class Chariot:
|
|
|
149
166
|
return self.put(type, body, params)
|
|
150
167
|
|
|
151
168
|
def link_account(self, username: str, value: str = '', config: dict = {}) -> dict:
|
|
152
|
-
resp =
|
|
153
|
-
headers=self.keychain.headers())
|
|
169
|
+
resp = self._make_request('POST', self.url(f'/account/{username}'), json=dict(config=config, value=value))
|
|
154
170
|
process_failure(resp)
|
|
155
171
|
return resp.json()
|
|
156
172
|
|
|
157
173
|
def unlink(self, username: str, value: str = '', config: dict = {}) -> dict:
|
|
158
|
-
resp =
|
|
159
|
-
json=dict(value=value, config=config))
|
|
174
|
+
resp = self._make_request('DELETE', self.url(f'/account/{username}'), json=dict(value=value, config=config))
|
|
160
175
|
process_failure(resp)
|
|
161
176
|
return resp.json()
|
|
162
177
|
|
|
@@ -170,8 +185,7 @@ class Chariot:
|
|
|
170
185
|
def _upload(self, chariot_filepath: str, content: str) -> dict:
|
|
171
186
|
# It is a two-step upload. The PUT request to the /file endpoint is to get a presigned URL for S3.
|
|
172
187
|
# There is no data transfer.
|
|
173
|
-
presigned_url =
|
|
174
|
-
headers=self.keychain.headers())
|
|
188
|
+
presigned_url = self._make_request('PUT', self.url('/file'), params=dict(name=chariot_filepath))
|
|
175
189
|
process_failure(presigned_url)
|
|
176
190
|
resp = requests.put(presigned_url.json()['url'], data=content)
|
|
177
191
|
process_failure(resp)
|
|
@@ -182,12 +196,12 @@ class Chariot:
|
|
|
182
196
|
if global_:
|
|
183
197
|
params |= GLOBAL_FLAG
|
|
184
198
|
|
|
185
|
-
resp =
|
|
199
|
+
resp = self._make_request('GET', self.url('/file'), params=params, allow_redirects=True)
|
|
186
200
|
process_failure(resp)
|
|
187
201
|
return resp.content
|
|
188
202
|
|
|
189
203
|
def count(self, params: dict) -> dict:
|
|
190
|
-
resp =
|
|
204
|
+
resp = self._make_request('GET', self.url('/my/count'), params=params)
|
|
191
205
|
process_failure(resp)
|
|
192
206
|
return resp.json()
|
|
193
207
|
|
|
@@ -196,11 +210,11 @@ class Chariot:
|
|
|
196
210
|
return json.loads(self.download(f'enrichments/{type}/{filename}', True).decode('utf-8'))
|
|
197
211
|
|
|
198
212
|
def purge(self):
|
|
199
|
-
|
|
213
|
+
self._make_request('DELETE', self.url('/account/purge'))
|
|
200
214
|
|
|
201
215
|
def agent(self, agent: str, body: dict) -> dict:
|
|
202
216
|
body = body | dict(agent=agent)
|
|
203
|
-
resp =
|
|
217
|
+
resp = self._make_request('PUT', self.url('/agent'), json=body)
|
|
204
218
|
process_failure(resp)
|
|
205
219
|
return resp.json()
|
|
206
220
|
|
|
@@ -9,20 +9,20 @@ class Assets:
|
|
|
9
9
|
def __init__(self, api):
|
|
10
10
|
self.api = api
|
|
11
11
|
|
|
12
|
-
def add(self,
|
|
12
|
+
def add(self, group, identifier, type=Kind.ASSET.value, status=Asset.ACTIVE.value, surface=''):
|
|
13
13
|
""" Add an asset
|
|
14
14
|
|
|
15
15
|
Arguments:
|
|
16
|
-
|
|
17
|
-
The DNS
|
|
18
|
-
|
|
19
|
-
The name of the asset
|
|
16
|
+
group: str
|
|
17
|
+
The group (DNS, repository URL, etc.) of the asset
|
|
18
|
+
identifier: str
|
|
19
|
+
The identifier (IP address, repository name, etc.) of the asset
|
|
20
20
|
status: str
|
|
21
21
|
The status of the asset
|
|
22
22
|
surface: str
|
|
23
23
|
The attack surface of the asset
|
|
24
24
|
"""
|
|
25
|
-
return self.api.upsert('asset', dict(
|
|
25
|
+
return self.api.upsert('asset', dict(group=group, identifier=identifier, status=status, surface=[surface], type=type))[0]
|
|
26
26
|
|
|
27
27
|
def get(self, key, details=False):
|
|
28
28
|
""" Get details of an asset
|
|
@@ -65,7 +65,7 @@ class Assets:
|
|
|
65
65
|
"""
|
|
66
66
|
return self.api.delete_by_key('asset', key)
|
|
67
67
|
|
|
68
|
-
def list(self, prefix_filter='', offset=None, pages=100000) -> tuple:
|
|
68
|
+
def list(self, prefix_filter='', asset_type='', offset=None, pages=100000) -> tuple:
|
|
69
69
|
""" List assets
|
|
70
70
|
|
|
71
71
|
Arguments:
|
|
@@ -78,7 +78,12 @@ class Assets:
|
|
|
78
78
|
pages: int
|
|
79
79
|
The number of pages of results to retrieve.
|
|
80
80
|
"""
|
|
81
|
-
|
|
81
|
+
dns_prefix = ''
|
|
82
|
+
if prefix_filter:
|
|
83
|
+
dns_prefix = f'group:{prefix_filter}'
|
|
84
|
+
if asset_type == '':
|
|
85
|
+
asset_type = Kind.ASSET.value
|
|
86
|
+
return self.api.search.by_term(dns_prefix, asset_type, offset, pages)
|
|
82
87
|
|
|
83
88
|
def attributes(self, key):
|
|
84
89
|
""" list associated attributes """
|
|
@@ -21,6 +21,8 @@ class Filter:
|
|
|
21
21
|
|
|
22
22
|
class Field(Enum):
|
|
23
23
|
KEY = 'key'
|
|
24
|
+
GROUP = 'group'
|
|
25
|
+
IDENTIFIER = 'identifier'
|
|
24
26
|
DNS = 'dns'
|
|
25
27
|
NAME = 'name'
|
|
26
28
|
VALUE = 'value'
|
|
@@ -87,6 +89,9 @@ class Relationship:
|
|
|
87
89
|
class Node:
|
|
88
90
|
class Label(Enum):
|
|
89
91
|
ASSET = 'Asset'
|
|
92
|
+
REPOSITORY = 'Repository'
|
|
93
|
+
INTEGRATION = 'Integration'
|
|
94
|
+
ADDOMAIN = 'Addomain'
|
|
90
95
|
ATTRIBUTE = 'Attribute'
|
|
91
96
|
RISK = 'Risk'
|
|
92
97
|
PRESEED = 'Preseed'
|
|
@@ -149,6 +154,9 @@ KIND_TO_LABEL = {
|
|
|
149
154
|
Kind.ATTRIBUTE.value: Node.Label.ATTRIBUTE,
|
|
150
155
|
Kind.SEED.value: Node.Label.SEED,
|
|
151
156
|
Kind.PRESEED.value: Node.Label.PRESEED,
|
|
157
|
+
Kind.REPOSITORY.value: Node.Label.REPOSITORY,
|
|
158
|
+
Kind.INTEGRATION.value: Node.Label.INTEGRATION,
|
|
159
|
+
Kind.ADDOMAIN.value: Node.Label.ADDOMAIN,
|
|
152
160
|
}
|
|
153
161
|
|
|
154
162
|
|
|
@@ -196,8 +204,8 @@ def my_params_to_query(params: dict):
|
|
|
196
204
|
return None
|
|
197
205
|
|
|
198
206
|
field = Filter.Field(key.split(':')[0])
|
|
199
|
-
value = key.split(':')[1]
|
|
200
|
-
operator = Filter.Operator.
|
|
207
|
+
value = key.split(':', 1)[1]
|
|
208
|
+
operator = Filter.Operator.CONTAINS
|
|
201
209
|
|
|
202
210
|
filter = Filter(field, operator, value)
|
|
203
211
|
|
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
def asset_key(dns, name):
|
|
4
4
|
return f'#asset#{dns}#{name}'
|
|
5
5
|
|
|
6
|
+
def ad_domain_key(dns, name):
|
|
7
|
+
return f'#addomain#{dns}#{name}'
|
|
8
|
+
|
|
9
|
+
def repository_key(dns, name):
|
|
10
|
+
return f'#repository#{dns}#{name}'
|
|
11
|
+
|
|
12
|
+
def integration_key(dns, name):
|
|
13
|
+
return f'#integration#{dns}#{name}'
|
|
6
14
|
|
|
7
15
|
def risk_key(dns, name):
|
|
8
16
|
return f'#risk#{dns}#{name}'
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from praetorian_cli.sdk.model.globals import Asset, 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 TestAsset:
|
|
9
|
+
|
|
10
|
+
def setup_class(self):
|
|
11
|
+
self.sdk = setup_chariot()
|
|
12
|
+
make_test_values(self)
|
|
13
|
+
|
|
14
|
+
def test_add_asset(self):
|
|
15
|
+
asset = self.sdk.assets.add(self.asset_dns, self.asset_name, status=Asset.ACTIVE.value, surface='test-surface')
|
|
16
|
+
assert asset['key'] == self.asset_key
|
|
17
|
+
assert len(asset['surface']) == 1
|
|
18
|
+
assert asset['surface'][0] == 'test-surface'
|
|
19
|
+
assert asset['status'] == Asset.ACTIVE.value
|
|
20
|
+
|
|
21
|
+
def test_get_asset(self):
|
|
22
|
+
a = self.get_asset()
|
|
23
|
+
assert a['group'] == self.asset_dns
|
|
24
|
+
assert a['identifier'] == self.asset_name
|
|
25
|
+
assert a['status'] == Asset.ACTIVE.value
|
|
26
|
+
|
|
27
|
+
def test_list_asset(self):
|
|
28
|
+
results, _ = self.sdk.assets.list()
|
|
29
|
+
assert len(results) > 0
|
|
30
|
+
assert any([a['group'] == self.asset_dns for a in results])
|
|
31
|
+
|
|
32
|
+
def test_update_asset(self):
|
|
33
|
+
self.sdk.assets.update(self.asset_key, status=Asset.FROZEN.value)
|
|
34
|
+
assert self.get_asset()['status'] == Asset.FROZEN.value
|
|
35
|
+
self.sdk.assets.update(self.asset_key, surface='abc')
|
|
36
|
+
attributes = self.sdk.assets.attributes(self.asset_key)
|
|
37
|
+
assert any([a['name'] == 'surface' and a['value'] == 'abc' for a in attributes])
|
|
38
|
+
|
|
39
|
+
def test_delete_asset(self):
|
|
40
|
+
self.sdk.assets.delete(self.asset_key)
|
|
41
|
+
assert self.get_asset()['status'] == Asset.DELETED.value
|
|
42
|
+
deleted_assets, _ = self.sdk.search.by_status(Asset.DELETED.value, Kind.ASSET.value)
|
|
43
|
+
assert any([a['group'] == self.asset_dns for a in deleted_assets])
|
|
44
|
+
|
|
45
|
+
def test_add_ad_domain(self):
|
|
46
|
+
asset = self.sdk.assets.add(self.ad_domain_name, self.ad_domain_name, status=Asset.ACTIVE.value, surface='test-surface', type=Kind.ADDOMAIN.value)
|
|
47
|
+
assert asset['key'] == self.ad_domain_key
|
|
48
|
+
assert len(asset['surface']) == 1
|
|
49
|
+
assert asset['surface'][0] == 'test-surface'
|
|
50
|
+
assert asset['status'] == Asset.ACTIVE.value
|
|
51
|
+
|
|
52
|
+
def test_get_ad_domain(self):
|
|
53
|
+
asset = self.sdk.assets.get(self.ad_domain_key)
|
|
54
|
+
assert asset['key'] == self.ad_domain_key
|
|
55
|
+
assert asset['name'] == self.ad_domain_name
|
|
56
|
+
assert asset['status'] == Asset.ACTIVE.value
|
|
57
|
+
|
|
58
|
+
def test_list_ad_domain(self):
|
|
59
|
+
results, _ = self.sdk.assets.list(asset_type=Kind.ADDOMAIN.value)
|
|
60
|
+
assert len(results) > 0
|
|
61
|
+
assert any([a['group'] == self.ad_domain_name for a in results])
|
|
62
|
+
|
|
63
|
+
def test_update_ad_domain(self):
|
|
64
|
+
self.sdk.assets.update(self.ad_domain_key, status=Asset.FROZEN.value)
|
|
65
|
+
assert self.get_ad_domain()['status'] == Asset.FROZEN.value
|
|
66
|
+
self.sdk.assets.update(self.ad_domain_key, surface='abc')
|
|
67
|
+
attributes = self.sdk.assets.attributes(self.ad_domain_key)
|
|
68
|
+
assert any([a['name'] == 'surface' and a['value'] == 'abc' for a in attributes])
|
|
69
|
+
|
|
70
|
+
def test_delete_ad_domain(self):
|
|
71
|
+
self.sdk.assets.delete(self.ad_domain_key)
|
|
72
|
+
assert self.get_ad_domain()['status'] == Asset.DELETED.value
|
|
73
|
+
deleted_assets, _ = self.sdk.search.by_status(Asset.DELETED.value, Kind.ADDOMAIN.value)
|
|
74
|
+
assert any([a['key'] == self.ad_domain_key for a in deleted_assets])
|
|
75
|
+
|
|
76
|
+
def get_asset(self):
|
|
77
|
+
return self.sdk.assets.get(self.asset_key)
|
|
78
|
+
|
|
79
|
+
def get_ad_domain(self):
|
|
80
|
+
return self.sdk.assets.get(self.ad_domain_key)
|
|
81
|
+
|
|
82
|
+
def teardown_class(self):
|
|
83
|
+
clean_test_entities(self.sdk, self)
|
|
@@ -35,17 +35,17 @@ class TestSearch:
|
|
|
35
35
|
def test_search_by_status(self):
|
|
36
36
|
hits, _ = self.sdk.search.by_status(Asset.ACTIVE.value, Kind.ASSET.value)
|
|
37
37
|
assert len(hits) >= 1
|
|
38
|
-
assert any([h['
|
|
38
|
+
assert any([h['group'] == self.asset_dns for h in hits])
|
|
39
39
|
|
|
40
40
|
def test_search_by_dns(self):
|
|
41
41
|
hits, _ = self.sdk.search.by_dns(self.asset_dns, Kind.ASSET.value)
|
|
42
42
|
assert len(hits) == 1
|
|
43
|
-
assert hits[0]['
|
|
43
|
+
assert hits[0]['group'] == self.asset_dns
|
|
44
44
|
|
|
45
45
|
def test_search_by_name(self):
|
|
46
46
|
hits, _ = self.sdk.search.by_name(self.asset_name, Kind.ASSET.value)
|
|
47
47
|
assert len(hits) == 1
|
|
48
|
-
assert hits[0]['
|
|
48
|
+
assert hits[0]['group'] == self.asset_dns
|
|
49
49
|
|
|
50
50
|
def teardown_class(self):
|
|
51
51
|
clean_test_entities(self.sdk, self)
|
|
@@ -260,23 +260,6 @@ class TestZCli:
|
|
|
260
260
|
|
|
261
261
|
self.verify(f'delete configuration "{o.configuration_key}"', ignore_stdout=True)
|
|
262
262
|
|
|
263
|
-
def test_job_cli(self):
|
|
264
|
-
o = make_test_values(lambda: None)
|
|
265
|
-
self.verify(f'add asset -n {o.asset_name} -d {o.asset_dns}')
|
|
266
|
-
|
|
267
|
-
config = {"test_config_key": "test_config_value"}
|
|
268
|
-
config_json = json.dumps(config)
|
|
269
|
-
self.verify(f'add job -k "{o.asset_key}" --config \'{config_json}\'')
|
|
270
|
-
|
|
271
|
-
self.verify(f'list jobs -f {o.asset_dns}', [o.asset_dns])
|
|
272
|
-
|
|
273
|
-
invalid_json = '{"invalid_json": "missing_closing_brace"'
|
|
274
|
-
self.verify(f'add job -k "{o.asset_key}" --config \'{invalid_json}\'',
|
|
275
|
-
expected_stderr=["Invalid JSON in configuration string"])
|
|
276
|
-
|
|
277
|
-
clean_test_entities(self.sdk, o)
|
|
278
|
-
|
|
279
|
-
|
|
280
263
|
def test_help_cli(self):
|
|
281
264
|
self.verify('--help', ignore_stdout=True)
|
|
282
265
|
self.verify('list --help', ignore_stdout=True)
|
|
@@ -5,7 +5,7 @@ from random import randint
|
|
|
5
5
|
from praetorian_cli.sdk.chariot import Chariot
|
|
6
6
|
from praetorian_cli.sdk.keychain import Keychain
|
|
7
7
|
from praetorian_cli.sdk.model.globals import Risk, Preseed
|
|
8
|
-
from praetorian_cli.sdk.model.utils import risk_key, asset_key, attribute_key, seed_key, preseed_key, setting_key, configuration_key
|
|
8
|
+
from praetorian_cli.sdk.model.utils import risk_key, asset_key, ad_domain_key, attribute_key, seed_key, preseed_key, setting_key, configuration_key
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def epoch_micro():
|
|
@@ -24,10 +24,15 @@ def random_dns():
|
|
|
24
24
|
return f'test-{epoch_micro()}.com'
|
|
25
25
|
|
|
26
26
|
|
|
27
|
+
def random_ad_domain():
|
|
28
|
+
return f'test-{epoch_micro()}.local'
|
|
29
|
+
|
|
27
30
|
def make_test_values(o):
|
|
28
31
|
o.asset_dns = random_dns()
|
|
29
32
|
o.asset_name = random_ip()
|
|
30
33
|
o.asset_key = asset_key(o.asset_dns, o.asset_name)
|
|
34
|
+
o.ad_domain_name = random_ad_domain()
|
|
35
|
+
o.ad_domain_key = ad_domain_key(o.ad_domain_name, o.ad_domain_name)
|
|
31
36
|
o.seed_dns = random_dns()
|
|
32
37
|
o.seed_key = seed_key('domain', o.seed_dns)
|
|
33
38
|
o.risk_name = f'test-risk-name-{epoch_micro()}'
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
|
|
3
|
-
from praetorian_cli.sdk.model.globals import Asset, 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 TestAsset:
|
|
9
|
-
|
|
10
|
-
def setup_class(self):
|
|
11
|
-
self.sdk = setup_chariot()
|
|
12
|
-
make_test_values(self)
|
|
13
|
-
|
|
14
|
-
def test_add_asset(self):
|
|
15
|
-
asset = self.sdk.assets.add(self.asset_dns, self.asset_name, Asset.ACTIVE.value, 'test-surface')
|
|
16
|
-
print(asset)
|
|
17
|
-
assert asset['key'] == self.asset_key
|
|
18
|
-
assert len(asset['surface']) == 1
|
|
19
|
-
assert asset['surface'][0] == 'test-surface'
|
|
20
|
-
assert asset['status'] == Asset.ACTIVE.value
|
|
21
|
-
|
|
22
|
-
def test_get_asset(self):
|
|
23
|
-
a = self.get_asset()
|
|
24
|
-
assert a['dns'] == self.asset_dns
|
|
25
|
-
assert a['name'] == self.asset_name
|
|
26
|
-
assert a['status'] == Asset.ACTIVE.value
|
|
27
|
-
|
|
28
|
-
def test_list_asset(self):
|
|
29
|
-
results, _ = self.sdk.assets.list()
|
|
30
|
-
assert len(results) > 0
|
|
31
|
-
assert any([a['dns'] == self.asset_dns for a in results])
|
|
32
|
-
|
|
33
|
-
def test_update_asset(self):
|
|
34
|
-
self.sdk.assets.update(self.asset_key, status=Asset.FROZEN.value)
|
|
35
|
-
assert self.get_asset()['status'] == Asset.FROZEN.value
|
|
36
|
-
self.sdk.assets.update(self.asset_key, surface='abc')
|
|
37
|
-
attributes = self.sdk.assets.attributes(self.asset_key)
|
|
38
|
-
assert any([a['name'] == 'surface' and a['value'] == 'abc' for a in attributes])
|
|
39
|
-
|
|
40
|
-
def test_delete_asset(self):
|
|
41
|
-
self.sdk.assets.delete(self.asset_key)
|
|
42
|
-
assert self.get_asset()['status'] == Asset.DELETED.value
|
|
43
|
-
deleted_assets, _ = self.sdk.search.by_status(Asset.DELETED.value, Kind.ASSET.value)
|
|
44
|
-
assert any([a['dns'] == self.asset_dns for a in deleted_assets])
|
|
45
|
-
|
|
46
|
-
def get_asset(self):
|
|
47
|
-
return self.sdk.assets.get(self.asset_key)
|
|
48
|
-
|
|
49
|
-
def teardown_class(self):
|
|
50
|
-
clean_test_entities(self.sdk, self)
|
|
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.1.3 → praetorian_cli-2.1.5}/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
|