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.
Files changed (85) hide show
  1. {praetorian_cli-2.1.3/praetorian_cli.egg-info → praetorian_cli-2.1.5}/PKG-INFO +1 -1
  2. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/add.py +5 -4
  3. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/chariot.py +4 -2
  4. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/cli_decorators.py +5 -2
  5. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/list.py +5 -4
  6. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/main.py +3 -2
  7. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/chariot.py +31 -17
  8. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/assets.py +13 -8
  9. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/model/globals.py +3 -0
  10. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/model/query.py +10 -2
  11. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/model/utils.py +8 -0
  12. praetorian_cli-2.1.5/praetorian_cli/sdk/test/test_asset.py +83 -0
  13. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_search.py +3 -3
  14. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_z_cli.py +0 -17
  15. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/utils.py +6 -1
  16. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5/praetorian_cli.egg-info}/PKG-INFO +1 -1
  17. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/setup.cfg +1 -1
  18. praetorian_cli-2.1.3/praetorian_cli/sdk/test/test_asset.py +0 -50
  19. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/LICENSE +0 -0
  20. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/MANIFEST.in +0 -0
  21. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/README.md +0 -0
  22. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/__init__.py +0 -0
  23. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/__init__.py +0 -0
  24. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/agent.py +0 -0
  25. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/configure.py +0 -0
  26. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/delete.py +0 -0
  27. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/enrich.py +0 -0
  28. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/get.py +0 -0
  29. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/imports.py +0 -0
  30. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/link.py +0 -0
  31. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/script.py +0 -0
  32. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/search.py +0 -0
  33. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/test.py +0 -0
  34. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/unlink.py +0 -0
  35. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/update.py +0 -0
  36. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/handlers/utils.py +0 -0
  37. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/scripts/__init__.py +0 -0
  38. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/scripts/commands/__init__.py +0 -0
  39. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/scripts/commands/nmap-example.py +0 -0
  40. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/scripts/utils.py +0 -0
  41. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/__init__.py +0 -0
  42. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/__init__.py +0 -0
  43. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/accounts.py +0 -0
  44. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/agents.py +0 -0
  45. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/attributes.py +0 -0
  46. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/capabilities.py +0 -0
  47. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/configurations.py +0 -0
  48. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/credentials.py +0 -0
  49. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/definitions.py +0 -0
  50. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/files.py +0 -0
  51. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/integrations.py +0 -0
  52. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/jobs.py +0 -0
  53. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/keys.py +0 -0
  54. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/preseeds.py +0 -0
  55. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/risks.py +0 -0
  56. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/search.py +0 -0
  57. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/seeds.py +0 -0
  58. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/settings.py +0 -0
  59. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/statistics.py +0 -0
  60. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/entities/webhook.py +0 -0
  61. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/keychain.py +0 -0
  62. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/model/__init__.py +0 -0
  63. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/__init__.py +0 -0
  64. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/pytest.ini +0 -0
  65. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_account.py +0 -0
  66. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_agent.py +0 -0
  67. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_attribute.py +0 -0
  68. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_capabilities.py +0 -0
  69. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_configuration.py +0 -0
  70. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_definition.py +0 -0
  71. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_extend.py +0 -0
  72. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_file.py +0 -0
  73. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_job.py +0 -0
  74. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_key.py +0 -0
  75. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_preseed.py +0 -0
  76. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_risk.py +0 -0
  77. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_seed.py +0 -0
  78. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_setting.py +0 -0
  79. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli/sdk/test/test_webhook.py +0 -0
  80. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli.egg-info/SOURCES.txt +0 -0
  81. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli.egg-info/dependency_links.txt +0 -0
  82. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli.egg-info/entry_points.txt +0 -0
  83. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli.egg-info/requires.txt +0 -0
  84. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/praetorian_cli.egg-info/top_level.txt +0 -0
  85. {praetorian_cli-2.1.3 → praetorian_cli-2.1.5}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: praetorian-cli
3
- Version: 2.1.3
3
+ Version: 2.1.5
4
4
  Summary: For interacting with the Chariot API
5
5
  Home-page: https://github.com/praetorian-inc/praetorian-cli
6
6
  Author: Praetorian
@@ -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, GitHub repo URL')
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
- chariot = Chariot(keychain=keychain)
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 = requests.get(self.url('/my'), params=params, headers=self.keychain.headers())
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 = requests.post(self.url('/my'), json=raw_query, params=params, headers=self.keychain.headers())
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 = requests.post(self.url(f'/{type}'), json=body, params=params, headers=self.keychain.headers())
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 = requests.put(self.url(f'/{type}'), json=body, params=params, headers=self.keychain.headers())
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 = requests.get(self.url(f'/{type}'), params=params, headers=self.keychain.headers())
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 = requests.delete(self.url(f'/{type}'), json=body, params=params, headers=self.keychain.headers())
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 = requests.post(self.url(f'/account/{username}'), json=dict(config=config, value=value),
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 = requests.delete(self.url(f'/account/{username}'), headers=self.keychain.headers(),
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 = requests.put(self.url('/file'), params=dict(name=chariot_filepath),
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 = requests.get(self.url('/file'), params=params, allow_redirects=True, headers=self.keychain.headers())
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 = requests.get(self.url('/my/count'), params=params, headers=self.keychain.headers())
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
- requests.delete(self.url('/account/purge'), headers=self.keychain.headers())
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 = requests.put(self.url('/agent'), json=body, headers=self.keychain.headers())
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, dns, name, status=Asset.ACTIVE.value, surface=''):
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
- dns: str
17
- The DNS name of the asset
18
- name: str
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(dns=dns, name=name, status=status, surface=[surface]))[0]
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
- return self.api.search.by_key_prefix(f'#asset#{prefix_filter}', offset, pages)
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 """
@@ -100,6 +100,9 @@ class AgentType(Enum):
100
100
 
101
101
  class Kind(Enum):
102
102
  ASSET = 'asset'
103
+ REPOSITORY = 'repository'
104
+ INTEGRATION = 'integration'
105
+ ADDOMAIN = 'addomain'
103
106
  RISK = 'risk'
104
107
  ATTRIBUTE = 'attribute'
105
108
  SEED = 'seed'
@@ -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.STARTS_WITH
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['dns'] == self.asset_dns for h in hits])
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]['dns'] == self.asset_dns
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]['dns'] == self.asset_dns
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: praetorian-cli
3
- Version: 2.1.3
3
+ Version: 2.1.5
4
4
  Summary: For interacting with the Chariot API
5
5
  Home-page: https://github.com/praetorian-inc/praetorian-cli
6
6
  Author: Praetorian
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = praetorian-cli
3
- version = 2.1.3
3
+ version = 2.1.5
4
4
  author = Praetorian
5
5
  author_email = support@praetorian.com
6
6
  description = For interacting with the Chariot API
@@ -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