praetorian-cli 2.2.3__py3-none-any.whl → 2.2.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. praetorian_cli/handlers/add.py +42 -27
  2. praetorian_cli/handlers/agent.py +29 -0
  3. praetorian_cli/handlers/cli_decorators.py +1 -1
  4. praetorian_cli/handlers/delete.py +12 -0
  5. praetorian_cli/handlers/get.py +21 -2
  6. praetorian_cli/handlers/link.py +29 -1
  7. praetorian_cli/handlers/list.py +22 -0
  8. praetorian_cli/handlers/unlink.py +29 -1
  9. praetorian_cli/handlers/utils.py +59 -0
  10. praetorian_cli/sdk/chariot.py +2 -0
  11. praetorian_cli/sdk/entities/configurations.py +1 -1
  12. praetorian_cli/sdk/entities/definitions.py +11 -2
  13. praetorian_cli/sdk/entities/files.py +10 -5
  14. praetorian_cli/sdk/entities/search.py +6 -4
  15. praetorian_cli/sdk/entities/webpage.py +180 -0
  16. praetorian_cli/sdk/model/globals.py +3 -0
  17. praetorian_cli/sdk/model/query.py +7 -0
  18. praetorian_cli/sdk/test/test_asset.py +36 -0
  19. praetorian_cli/sdk/test/test_conversation.py +195 -0
  20. praetorian_cli/sdk/test/test_webpage.py +46 -0
  21. praetorian_cli/sdk/test/test_z_cli.py +55 -0
  22. praetorian_cli/sdk/test/utils.py +5 -0
  23. praetorian_cli/ui/conversation/__init__.py +3 -0
  24. praetorian_cli/ui/conversation/textual_chat.py +622 -0
  25. {praetorian_cli-2.2.3.dist-info → praetorian_cli-2.2.5.dist-info}/METADATA +1 -1
  26. {praetorian_cli-2.2.3.dist-info → praetorian_cli-2.2.5.dist-info}/RECORD +30 -25
  27. {praetorian_cli-2.2.3.dist-info → praetorian_cli-2.2.5.dist-info}/WHEEL +0 -0
  28. {praetorian_cli-2.2.3.dist-info → praetorian_cli-2.2.5.dist-info}/entry_points.txt +0 -0
  29. {praetorian_cli-2.2.3.dist-info → praetorian_cli-2.2.5.dist-info}/licenses/LICENSE +0 -0
  30. {praetorian_cli-2.2.3.dist-info → praetorian_cli-2.2.5.dist-info}/top_level.txt +0 -0
@@ -5,7 +5,7 @@ import click
5
5
 
6
6
  from praetorian_cli.handlers.chariot import chariot
7
7
  from praetorian_cli.handlers.cli_decorators import cli_handler, praetorian_only
8
- from praetorian_cli.handlers.utils import error
8
+ from praetorian_cli.handlers.utils import error, parse_configuration_value
9
9
  from praetorian_cli.sdk.model.globals import AddRisk, Asset, Seed, Kind
10
10
 
11
11
 
@@ -29,6 +29,9 @@ def asset(sdk, name, dns, asset_type, status, surface):
29
29
  Add an asset to the Chariot database. This command requires a DNS name for the asset.
30
30
  Optionally, a name can be provided to give the asset more specific information,
31
31
  such as IP address. If no name is provided, the DNS name will be used as the name.
32
+ The DNS is the group and the name is the specific identifier. This is for legacy reasons.
33
+
34
+ The type can be one of the following: asset, addomain, repository, webapplication.
32
35
 
33
36
  \b
34
37
  Example assets:
@@ -41,6 +44,7 @@ def asset(sdk, name, dns, asset_type, status, surface):
41
44
  - praetorian chariot add asset --dns example.com
42
45
  - praetorian chariot add asset --dns example.com --name 1.2.3.4
43
46
  - praetorian chariot add asset --dns internal.example.com --name 10.2.3.4 --surface internal
47
+ - praetorian chariot add asset --dns https://example.com --name 'Example Web Application' --type webapplication
44
48
  """
45
49
  if not name:
46
50
  name = dns
@@ -270,40 +274,33 @@ def setting(sdk, name, value):
270
274
  @add.command()
271
275
  @cli_handler
272
276
  @click.option('-n', '--name', required=True, help='Name of the configuration')
273
- @click.option('-e', '--entry', required=True, multiple=True, help='Key-value pair in format key=value. Can be specified multiple times to set multiple values.')
277
+ @click.option('-e', '--entry', required=False, multiple=True,
278
+ help='Key-value pair in format key=value. Can be specified multiple times to set multiple values.')
279
+ @click.option('--string', 'string_value', required=False,
280
+ help='Set the configuration value to a string')
281
+ @click.option('--integer', 'integer_value', required=False,
282
+ help='Set the configuration value to an integer')
283
+ @click.option('--float', 'float_value', required=False,
284
+ help='Set the configuration value to a floating point number')
274
285
  @praetorian_only
275
- def configuration(sdk, name, entry):
286
+ def configuration(sdk, name, entry, string_value, integer_value, float_value):
276
287
  """ Add a configuration
277
288
 
278
- This command adds, or overwrites if exists, a name-value configuration.
289
+ This command adds, or overwrites if exists, a configuration value.
290
+
291
+ Configuration values can be provided as a mapping of key-value pairs using
292
+ ``--entry`` (the previous behavior), or as primitive values using
293
+ ``--string``, ``--integer``, or ``--float``.
279
294
 
280
295
  \b
281
296
  Example usages:
282
297
  - praetorian chariot add configuration --name "nuclei" --entry extra-tags=http,sql --entry something=else
298
+ - praetorian chariot add configuration --name "billing-status" --string PAID_MS
299
+ - praetorian chariot add configuration --name "request-timeout" --integer 60
300
+ - praetorian chariot add configuration --name "scoring-threshold" --float 0.85
283
301
  """
284
- config_dict = {}
285
- for item in entry:
286
- if '=' not in item:
287
- click.echo(f"Error: Entry '{item}' is not in the format key=value")
288
- return
289
-
290
- if item.count('=') > 1:
291
- click.echo(f"Error: Entry '{item}' contains multiple '=' characters. Format should be key=value")
292
- return
293
-
294
- key, value = item.split('=', 1)
295
-
296
- if not key:
297
- click.echo("Error: Key cannot be empty")
298
- return
299
-
300
- if not value:
301
- click.echo("Error: Value cannot be empty")
302
- return
303
-
304
- config_dict[key] = value
305
-
306
- sdk.configurations.add(name, config_dict)
302
+ config_value = parse_configuration_value(entry, string_value, integer_value, float_value)
303
+ sdk.configurations.add(name, config_value)
307
304
 
308
305
 
309
306
  @add.command()
@@ -328,3 +325,21 @@ def key(sdk, name, expires):
328
325
  return
329
326
  click.echo(f'API key created: {result.get("key", "N/A")}')
330
327
  click.echo(f'Secret (save this, it will not be shown again): {result["secret"]}')
328
+
329
+
330
+ @add.command()
331
+ @cli_handler
332
+ @click.option('-u', '--url', required=True, help='The full URL of the page')
333
+ @click.option('-p', '--parent', required=False, help='Optional key of the parent WebApplication')
334
+ def webpage(sdk, url, parent):
335
+ """ Add a Webpage
336
+
337
+ Add a web page to the Chariot database. Webpages can optionally be associated
338
+ with a parent WebApplication or exist independently.
339
+
340
+ \b
341
+ Example usages:
342
+ - praetorian chariot add webpage --url https://app.example.com/login
343
+ - praetorian chariot add webpage --url https://app.example.com/admin --parent "#webapplication#https://app.example.com"
344
+ """
345
+ sdk.webpage.add(url, parent)
@@ -69,3 +69,32 @@ def tools(sdk, allowed):
69
69
  """
70
70
  for tool in dict.keys(sdk.agents.list_mcp_tools(allowed)):
71
71
  click.echo(tool)
72
+
73
+ @agent.command()
74
+ @cli_handler
75
+ def conversation(sdk):
76
+ """ Interactive conversation with Chariot AI assistant
77
+
78
+ Start an interactive chat session with the Chariot AI assistant.
79
+ The AI can help you query security data, understand findings,
80
+ and provide insights about your attack surface.
81
+
82
+ \b
83
+ Commands within conversation:
84
+ - help Show available commands and query examples
85
+ - clear Clear the screen
86
+ - new Start a new conversation
87
+ - quit Exit the conversation
88
+
89
+ \b
90
+ Example queries:
91
+ - "Find all active assets"
92
+ - "Show me critical risks"
93
+ - "What assets do we have for example.com?"
94
+
95
+ \b
96
+ Usage:
97
+ praetorian chariot agent conversation
98
+ """
99
+ from praetorian_cli.ui.conversation import run_textual_conversation
100
+ run_textual_conversation(sdk)
@@ -68,7 +68,7 @@ def list_params(filter_by, has_details=True, has_filter=True, has_type=False):
68
68
 
69
69
 
70
70
  def pagination(func):
71
- func = click.option('-o', '--offset', default='', help='List results from an offset')(func)
71
+ func = click.option('-o', '--offset', default=0, help='List results from an offset')(func)
72
72
  func = click.option('-p', '--page', type=click.Choice(('first', 'all')), default='first',
73
73
  help='Pagination mode. "all" pages up to 10,000 pages.', show_default=True)(func)
74
74
  return func
@@ -182,3 +182,15 @@ def key(chariot, key):
182
182
  - praetorian chariot delete key "#key#550e8400-e29b-41d4-a716-446655440000"
183
183
  """
184
184
  chariot.keys.delete(key)
185
+
186
+ @delete.command()
187
+ @cli_handler
188
+ @click.argument('key', required=True)
189
+ def webpage(chariot, key):
190
+ """ Delete a webpage
191
+
192
+ \b
193
+ Arguments:
194
+ - KEY: the key of an existing webpage
195
+ """
196
+ chariot.webpage.delete(key)
@@ -147,7 +147,8 @@ def file(chariot, name, path):
147
147
  @cli_handler
148
148
  @click.argument('name')
149
149
  @click.option('-path', '--path', default=os.getcwd(), help='Download path. Default: save to current directory')
150
- def definition(chariot, name, path):
150
+ @click.option('--global', 'global_', is_flag=True, help='Fetch from global definitions instead of user-specific')
151
+ def definition(chariot, name, path, global_):
151
152
  """ Download a definition using the risk name
152
153
 
153
154
  \b
@@ -158,8 +159,9 @@ def definition(chariot, name, path):
158
159
  Example usage:
159
160
  - praetorian chariot get definition jira-unauthenticated-user-picker
160
161
  - praetorian chariot get definition CVE-2024-23049
162
+ - praetorian chariot get definition CVE-2024-23049 --global
161
163
  """
162
- downloaded_path = chariot.definitions.get(name, path)
164
+ downloaded_path = chariot.definitions.get(name, path, global_=global_)
163
165
  click.echo(f'Saved definition at {downloaded_path}')
164
166
 
165
167
 
@@ -300,6 +302,23 @@ def scanner(chariot, key):
300
302
 
301
303
  @get.command()
302
304
  @cli_handler
305
+ @click.argument('key', required=True)
306
+ def webpage(chariot, key):
307
+ """ Get Webpage details
308
+
309
+ Retrieve detailed information about a specific web page, including
310
+ its URL, method, authentication requirements, and other metadata.
311
+
312
+ \b
313
+ Argument:
314
+ - KEY: the key of an existing Webpage
315
+
316
+ \b
317
+ Example usages:
318
+ - praetorian chariot get webpage "#webpage#https://app.example.com/dashboard"
319
+ """
320
+ print_json(chariot.webpage.get(key))
321
+
303
322
  @click.option('-t', '--type', help='Optional specific entity type (e.g., asset, risk, attribute)')
304
323
  @click.option('-d', '--details', is_flag=True, help='Further retrieve the details of the schema')
305
324
  def schema(chariot, type, details):
@@ -6,7 +6,7 @@ from praetorian_cli.handlers.cli_decorators import cli_handler
6
6
 
7
7
  @chariot.group()
8
8
  def link():
9
- """ Add a collaborator to your account """
9
+ """ Link resources to other entities """
10
10
  pass
11
11
 
12
12
 
@@ -30,3 +30,31 @@ def account(chariot, username):
30
30
  - praetorian chariot link account john@praetorian.com
31
31
  """
32
32
  chariot.accounts.add_collaborator(username)
33
+
34
+
35
+ @link.command('webpage-source')
36
+ @cli_handler
37
+ @click.argument('webpage_key')
38
+ @click.argument('entity_key')
39
+ def webpage_source(chariot, webpage_key, entity_key):
40
+ """ Link a file or repository to a webpage as source code
41
+
42
+ This associates source code files or repositories with webpages
43
+ to track where webpage content originates from.
44
+
45
+ \b
46
+ Arguments:
47
+ - WEBPAGE_KEY: The webpage key in format #webpage#{url}
48
+ - ENTITY_KEY: The file or repository key to link
49
+ Format: #file#{path} or #repository#{url}#{name}
50
+
51
+ \b
52
+ Example usages:
53
+ - praetorian chariot link webpage-source "#webpage#https://example.com" "#file#proofs/scan.txt"
54
+ - praetorian chariot link webpage-source "#webpage#https://example.com/login" "#repository#https://github.com/org/repo.git#repo.git"
55
+ """
56
+ result = chariot.webpage.link_source(webpage_key, entity_key)
57
+ if result:
58
+ click.echo(f"Successfully linked {entity_key} to {webpage_key}")
59
+ if 'artifacts' in result:
60
+ click.echo(f"Webpage now has {len(result['artifacts'])} linked artifacts")
@@ -368,3 +368,25 @@ def scanners(chariot, filter, details, offset, page):
368
368
  - praetorian chariot list scanners --page all
369
369
  """
370
370
  render_list_results(chariot.scanners.list(filter, offset, pagination_size(page)), details)
371
+
372
+
373
+ @list.command()
374
+ @click.option('--parent', required=False, help='Optional WebApp key to filter pages')
375
+ @click.option('-f', '--filter', required=False, help='Optional URL to filter pages')
376
+ @click.option('-d', '--details', is_flag=True, default=False, help='Show detailed information')
377
+ @pagination
378
+ @cli_handler
379
+ def webpages(chariot, parent, filter, details, offset, page):
380
+ """ List WebPages
381
+
382
+ Retrieve and display a list of pages/URLs. Can optionally filter by specific WebApplication.
383
+
384
+ \b
385
+ Example usages:
386
+ - praetorian chariot list webpages
387
+ - praetorian chariot list webpages --parent "#webapplication#https://app.example.com"
388
+ - praetorian chariot list webpages --filter /login
389
+ - praetorian chariot list webpages --parent "#webapplication#https://app.example.com" --details
390
+ - praetorian chariot list webpages --page all
391
+ """
392
+ render_list_results(chariot.webpage.list(parent, filter, offset, pagination_size(page)), details)
@@ -6,7 +6,7 @@ from praetorian_cli.handlers.cli_decorators import cli_handler
6
6
 
7
7
  @chariot.group()
8
8
  def unlink():
9
- """ Remove a collaborator from your account """
9
+ """ Remove links between resources """
10
10
  pass
11
11
 
12
12
 
@@ -25,3 +25,31 @@ def account(chariot, username):
25
25
  - praetorian chariot unlink account john@praetorian.com
26
26
  """
27
27
  chariot.accounts.delete_collaborator(username)
28
+
29
+
30
+ @unlink.command('webpage-source')
31
+ @cli_handler
32
+ @click.argument('webpage_key')
33
+ @click.argument('entity_key')
34
+ def webpage_source(chariot, webpage_key, entity_key):
35
+ """ Unlink a file or repository from a webpage's source code
36
+
37
+ This removes the association between source code files or
38
+ repositories and webpages.
39
+
40
+ \b
41
+ Arguments:
42
+ - WEBPAGE_KEY: The webpage key in format #webpage#{url}
43
+ - ENTITY_KEY: The file or repository key to unlink
44
+ Format: #file#{path} or #repository#{url}#{name}
45
+
46
+ \b
47
+ Example usages:
48
+ - praetorian chariot unlink webpage-source "#webpage#https://example.com" "#file#proofs/scan.txt"
49
+ - praetorian chariot unlink webpage-source "#webpage#https://example.com/login" "#repository#https://github.com/org/repo.git#repo.git"
50
+ """
51
+ result = chariot.webpage.unlink_source(webpage_key, entity_key)
52
+ if result:
53
+ click.echo(f"Successfully unlinked {entity_key} from {webpage_key}")
54
+ if 'artifacts' in result:
55
+ click.echo(f"Webpage now has {len(result['artifacts'])} linked artifacts")
@@ -3,6 +3,65 @@ import json
3
3
  import click
4
4
 
5
5
 
6
+ def parse_configuration_value(
7
+ entries = [], s_val=None, i_val=None, f_val=None):
8
+ """Return a configuration value derived from CLI inputs."""
9
+ has_entries = len(entries) > 0
10
+ typed_values = {
11
+ 'string': s_val,
12
+ 'integer': i_val,
13
+ 'float': f_val,
14
+ }
15
+
16
+ if has_entries and any(value is not None for value in typed_values.values()):
17
+ error('--entry cannot be combined with --string, --integer, or --float')
18
+
19
+ if has_entries:
20
+ return _parse_entry_dict(entries)
21
+
22
+ provided = [(name, value) for name, value in typed_values.items() if value is not None]
23
+
24
+ if not provided:
25
+ error('Provide configuration data via --entry, --string, --integer, or --float')
26
+ if len(provided) > 1:
27
+ error('Specify only one of --string, --integer, or --float')
28
+
29
+ value_type, raw_value = provided[0]
30
+ return _cast_typed_value(value_type, raw_value)
31
+
32
+
33
+ def _parse_entry_dict(entries):
34
+ parsed = {}
35
+
36
+ for item in entries:
37
+ key, value = _split_entry(item)
38
+ if not key:
39
+ error(f'Key cannot be empty: {item}')
40
+ if not value:
41
+ error(f'Value cannot be empty: {item}')
42
+ parsed[key] = value
43
+ return parsed
44
+
45
+
46
+ def _split_entry(item):
47
+ if '=' not in item:
48
+ error(f"Entry '{item}' is not in the format key=value")
49
+ if item.count('=') > 1:
50
+ error(f"Entry '{item}' contains multiple '=' characters. Format should be key=value")
51
+ return item.split('=', 1)
52
+
53
+
54
+ def _cast_typed_value(value_type, raw_value):
55
+ try:
56
+ if value_type == 'integer':
57
+ return int(raw_value)
58
+ if value_type == 'float':
59
+ return float(raw_value)
60
+ except ValueError:
61
+ error(f'{value_type} must be a valid {value_type}')
62
+ return raw_value
63
+
64
+
6
65
  def render_list_results(list_results, details):
7
66
  list_data, offset = list_results
8
67
  if details:
@@ -21,6 +21,7 @@ from praetorian_cli.sdk.entities.search import Search
21
21
  from praetorian_cli.sdk.entities.seeds import Seeds
22
22
  from praetorian_cli.sdk.entities.settings import Settings
23
23
  from praetorian_cli.sdk.entities.statistics import Statistics
24
+ from praetorian_cli.sdk.entities.webpage import Webpage
24
25
  from praetorian_cli.sdk.entities.webhook import Webhook
25
26
  from praetorian_cli.sdk.keychain import Keychain
26
27
  from praetorian_cli.sdk.model.globals import GLOBAL_FLAG
@@ -52,6 +53,7 @@ class Chariot:
52
53
  self.keys = Keys(self)
53
54
  self.capabilities = Capabilities(self)
54
55
  self.credentials = Credentials(self)
56
+ self.webpage = Webpage(self)
55
57
  self.schema = Schema(self)
56
58
  self.proxy = proxy
57
59
 
@@ -27,7 +27,7 @@ class Configurations:
27
27
  "Please contact your Praetorian representative for assistance."
28
28
  )
29
29
 
30
- def add(self, name, value: dict):
30
+ def add(self, name, value):
31
31
  """
32
32
  Add or update a configuration.
33
33
 
@@ -29,7 +29,7 @@ class Definitions:
29
29
  definition_name = os.path.basename(local_filepath)
30
30
  return self.api.files.add(local_filepath, f'definitions/{definition_name}')
31
31
 
32
- def get(self, definition_name, download_directory=os.getcwd()):
32
+ def get(self, definition_name, download_directory=os.getcwd(), global_=False):
33
33
  """
34
34
  Download a risk definition file from the definitions folder.
35
35
 
@@ -37,15 +37,24 @@ class Definitions:
37
37
  :type definition_name: str
38
38
  :param download_directory: The directory to save the downloaded file (defaults to current working directory)
39
39
  :type download_directory: str
40
+ :param global_: If True, fetch from global definitions instead of user-specific
41
+ :type global_: bool
40
42
  :return: The local file path where the definition was saved
41
43
  :rtype: str
42
44
  """
43
- content = self.api.files.get_utf8(f'definitions/{definition_name}')
45
+ try:
46
+ content = self.api.files.get_utf8(f'definitions/{definition_name}', _global=global_)
47
+ except Exception as e:
48
+ if global_:
49
+ raise Exception(f'Global definition {definition_name} not found or inaccessible.')
50
+ else:
51
+ raise
44
52
  download_path = os.path.join(download_directory, definition_name)
45
53
  with open(download_path, 'w') as file:
46
54
  file.write(content)
47
55
  return download_path
48
56
 
57
+
49
58
  def list(self, name_filter='', offset=None, pages=100000) -> tuple:
50
59
  """
51
60
  List the definition names, optionally prefix-filtered by a definition name.
@@ -46,30 +46,35 @@ class Files:
46
46
 
47
47
  return download_path
48
48
 
49
- def get(self, chariot_filepath) -> bytes:
49
+ def get(self, chariot_filepath, _global=False) -> bytes:
50
50
  """
51
51
  Download a file from Chariot storage into memory as bytes.
52
52
 
53
53
  :param chariot_filepath: Path of the file in Chariot storage to download
54
54
  :type chariot_filepath: str
55
+ :param _global: If True, fetch from global storage instead of user-specific
56
+ :type _global: bool
55
57
  :return: The file content as bytes
56
58
  :rtype: bytes
57
59
  :raises Exception: If the file does not exist in Chariot storage
58
60
  """
59
- self.raise_if_missing(chariot_filepath)
60
- return self.api.download(chariot_filepath)
61
+ if not _global:
62
+ self.raise_if_missing(chariot_filepath)
63
+ return self.api.download(chariot_filepath, global_=_global)
61
64
 
62
- def get_utf8(self, chariot_filepath) -> str:
65
+ def get_utf8(self, chariot_filepath, _global=False) -> str:
63
66
  """
64
67
  Download a file from Chariot storage into memory as a UTF-8 string.
65
68
 
66
69
  :param chariot_filepath: Path of the file in Chariot storage to download
67
70
  :type chariot_filepath: str
71
+ :param _global: If True, fetch from global storage instead of user-specific
72
+ :type _global: bool
68
73
  :return: The file content as a UTF-8 decoded string
69
74
  :rtype: str
70
75
  :raises Exception: If the file does not exist in Chariot storage
71
76
  """
72
- return self.get(chariot_filepath).decode('utf-8')
77
+ return self.get(chariot_filepath, _global=_global).decode('utf-8')
73
78
 
74
79
  def list(self, prefix_filter='', offset=None, pages=100000) -> tuple:
75
80
  """
@@ -1,6 +1,6 @@
1
1
  import json
2
2
  from praetorian_cli.sdk.model.query import Query
3
- from praetorian_cli.sdk.model.globals import EXACT_FLAG, DESCENDING_FLAG, GLOBAL_FLAG, Kind
3
+ from praetorian_cli.sdk.model.globals import EXACT_FLAG, DESCENDING_FLAG, GLOBAL_FLAG, USER_FLAG, Kind
4
4
  class Search:
5
5
 
6
6
  def __init__(self, api):
@@ -17,7 +17,7 @@ class Search:
17
17
  """
18
18
  return self.api.count(dict(key=search_term))
19
19
 
20
- def by_key_prefix(self, key_prefix, offset=None, pages=100000) -> tuple:
20
+ def by_key_prefix(self, key_prefix, offset=None, pages=100000, user=False) -> tuple:
21
21
  """
22
22
  Search for entities by key prefix. <mcp>If the response is too large, make your query more specific.<mcp>
23
23
 
@@ -30,7 +30,7 @@ class Search:
30
30
  :return: A tuple containing (list of matching entities, next page offset)
31
31
  :rtype: tuple
32
32
  """
33
- return self.by_term(key_prefix, None, offset, pages)
33
+ return self.by_term(key_prefix, None, offset, pages, user=user)
34
34
 
35
35
  def by_exact_key(self, key, get_attributes=False) -> {}:
36
36
  """
@@ -119,7 +119,7 @@ class Search:
119
119
  return self.by_term(f'dns:{dns_prefix}', kind, offset, pages)
120
120
 
121
121
  def by_term(self, search_term, kind=None, offset=None, pages=100000, exact=False, descending=False,
122
- global_=False) -> tuple:
122
+ global_=False, user=False) -> tuple:
123
123
  """
124
124
  Search for a given kind by term.
125
125
 
@@ -151,6 +151,8 @@ class Search:
151
151
  params |= DESCENDING_FLAG
152
152
  if global_:
153
153
  params |= GLOBAL_FLAG
154
+ if user:
155
+ params |= USER_FLAG
154
156
 
155
157
  results = self.api.my(params, pages)
156
158