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.
Files changed (107) hide show
  1. {praetorian_cli-2.2.2/praetorian_cli.egg-info → praetorian_cli-2.2.3}/PKG-INFO +1 -1
  2. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/add.py +25 -7
  3. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/delete.py +3 -2
  4. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/get.py +31 -2
  5. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/list.py +8 -9
  6. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/update.py +3 -3
  7. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/chariot.py +4 -12
  8. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/assets.py +30 -12
  9. praetorian_cli-2.2.3/praetorian_cli/sdk/entities/schema.py +27 -0
  10. praetorian_cli-2.2.3/praetorian_cli/sdk/entities/seeds.py +166 -0
  11. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/mcp_server.py +2 -3
  12. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/model/query.py +1 -1
  13. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/model/utils.py +2 -8
  14. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_asset.py +2 -2
  15. praetorian_cli-2.2.3/praetorian_cli/sdk/test/test_seed.py +40 -0
  16. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_z_cli.py +22 -24
  17. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/utils.py +16 -4
  18. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3/praetorian_cli.egg-info}/PKG-INFO +1 -1
  19. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli.egg-info/SOURCES.txt +1 -0
  20. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/setup.cfg +1 -1
  21. praetorian_cli-2.2.2/praetorian_cli/sdk/entities/seeds.py +0 -114
  22. praetorian_cli-2.2.2/praetorian_cli/sdk/test/test_seed.py +0 -41
  23. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/LICENSE +0 -0
  24. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/MANIFEST.in +0 -0
  25. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/README.md +0 -0
  26. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/__init__.py +0 -0
  27. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/__init__.py +0 -0
  28. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/aegis.py +0 -0
  29. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/agent.py +0 -0
  30. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/chariot.py +0 -0
  31. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/cli_decorators.py +0 -0
  32. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/configure.py +0 -0
  33. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/enrich.py +0 -0
  34. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/imports.py +0 -0
  35. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/link.py +0 -0
  36. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/script.py +0 -0
  37. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/search.py +0 -0
  38. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/ssh_utils.py +0 -0
  39. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/test.py +0 -0
  40. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/unlink.py +0 -0
  41. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/handlers/utils.py +0 -0
  42. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/main.py +0 -0
  43. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/scripts/__init__.py +0 -0
  44. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/scripts/commands/__init__.py +0 -0
  45. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/scripts/commands/nmap-example.py +0 -0
  46. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/scripts/utils.py +0 -0
  47. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/__init__.py +0 -0
  48. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/__init__.py +0 -0
  49. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/accounts.py +0 -0
  50. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/aegis.py +0 -0
  51. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/agents.py +0 -0
  52. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/attributes.py +0 -0
  53. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/capabilities.py +0 -0
  54. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/configurations.py +0 -0
  55. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/credentials.py +0 -0
  56. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/definitions.py +0 -0
  57. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/files.py +0 -0
  58. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/integrations.py +0 -0
  59. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/jobs.py +0 -0
  60. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/keys.py +0 -0
  61. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/preseeds.py +0 -0
  62. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/risks.py +0 -0
  63. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/scanners.py +0 -0
  64. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/search.py +0 -0
  65. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/settings.py +0 -0
  66. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/statistics.py +0 -0
  67. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/entities/webhook.py +0 -0
  68. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/keychain.py +0 -0
  69. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/model/__init__.py +0 -0
  70. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/model/aegis.py +0 -0
  71. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/model/globals.py +0 -0
  72. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/__init__.py +0 -0
  73. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/pytest.ini +0 -0
  74. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_account.py +0 -0
  75. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_agent.py +0 -0
  76. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_attribute.py +0 -0
  77. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_capabilities.py +0 -0
  78. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_configuration.py +0 -0
  79. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_definition.py +0 -0
  80. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_extend.py +0 -0
  81. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_file.py +0 -0
  82. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_job.py +0 -0
  83. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_key.py +0 -0
  84. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_mcp.py +0 -0
  85. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_preseed.py +0 -0
  86. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_risk.py +0 -0
  87. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_search.py +0 -0
  88. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_setting.py +0 -0
  89. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/test_webhook.py +0 -0
  90. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/sdk/test/ui_mocks.py +0 -0
  91. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/__init__.py +0 -0
  92. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/__init__.py +0 -0
  93. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/commands/__init__.py +0 -0
  94. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/commands/help.py +0 -0
  95. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/commands/info.py +0 -0
  96. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/commands/job.py +0 -0
  97. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/commands/list.py +0 -0
  98. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/commands/set.py +0 -0
  99. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/commands/ssh.py +0 -0
  100. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/constants.py +0 -0
  101. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/menu.py +0 -0
  102. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli/ui/aegis/utils.py +0 -0
  103. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli.egg-info/dependency_links.txt +0 -0
  104. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli.egg-info/entry_points.txt +0 -0
  105. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli.egg-info/requires.txt +0 -0
  106. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/praetorian_cli.egg-info/top_level.txt +0 -0
  107. {praetorian_cli-2.2.2 → praetorian_cli-2.2.3}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: praetorian-cli
3
- Version: 2.2.2
3
+ Version: 2.2.3
4
4
  Summary: For interacting with the Chariot API
5
5
  Home-page: https://github.com/praetorian-inc/praetorian-cli
6
6
  Author: Praetorian
@@ -196,21 +196,39 @@ def attribute(sdk, key, name, value):
196
196
 
197
197
  @add.command()
198
198
  @cli_handler
199
- @click.option('-d', '--dns', required=True, help='The DNS of the asset')
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
- def seed(sdk, dns, status):
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. This command requires DNS of the seed to be
206
- specified. When status is not specified, the seed is added as PENDING.
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 example.com
211
- - praetorian chariot add seed --dns example.com --status A
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
- sdk.seeds.add(dns, status)
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 "#seed#domain#example.com"
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 pre-seed
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 preseed "#preseed#domain#example.com"
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, offset, pagination_size(page)), details)
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', type=click.Choice(['ip', 'domain']), help=f'Filter by type of the seeds')
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 ip
191
- - praetorian chariot list seeds --type domain --filter example.com
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
- if filter and not type:
196
- error('When the DNS filter is specified, you also need to specify the type of the filter: ip or domain.')
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 "#seed#domain#example.com" -s A
69
- - praetorian chariot update seed "#seed#ip#1.1.1.0/24" -s F
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._add_beta_filter(method, kwargs)
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 _add_beta_filter(self, method: str, kwargs: dict):
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, prefix_filter='', asset_type='', offset=None, pages=100000) -> tuple:
80
+ def list(self, key_prefix='', asset_type='', pages=100000) -> tuple:
80
81
  """
81
82
  List assets.
82
83
 
83
- :param prefix_filter: Supply this to perform prefix-filtering of the asset keys after the "#asset#" portion of the asset key. Asset keys read '#asset#{dns}#{name}'
84
- :type prefix_filter: str
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
- dns_prefix = ''
95
- if prefix_filter:
96
- dns_prefix = f'group:{prefix_filter}'
97
- if asset_type == '':
98
- asset_type = Kind.ASSET.value
99
- return self.api.search.by_term(dns_prefix, asset_type, offset, pages)
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["required"]:
172
+ if param_info.get("required", False):
174
173
  required.append(param_name)
175
174
 
176
175
  tool_schema = {
@@ -91,7 +91,7 @@ class Node:
91
91
  ASSET = 'Asset'
92
92
  REPOSITORY = 'Repository'
93
93
  INTEGRATION = 'Integration'
94
- ADDOMAIN = 'Addomain'
94
+ ADDOMAIN = 'ADDomain'
95
95
  ATTRIBUTE = 'Attribute'
96
96
  RISK = 'Risk'
97
97
  PRESEED = 'Preseed'
@@ -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
- def seed_key(type, dns):
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.ad_domain_name, status=Asset.ACTIVE.value, surface='test-surface', type=Kind.ADDOMAIN.value)
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['name'] == self.ad_domain_name
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)