praetorian-cli 2.2.1__py3-none-any.whl → 2.2.3__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 (43) hide show
  1. praetorian_cli/handlers/add.py +25 -7
  2. praetorian_cli/handlers/aegis.py +107 -0
  3. praetorian_cli/handlers/delete.py +3 -2
  4. praetorian_cli/handlers/get.py +48 -2
  5. praetorian_cli/handlers/list.py +41 -9
  6. praetorian_cli/handlers/ssh_utils.py +154 -0
  7. praetorian_cli/handlers/test.py +7 -2
  8. praetorian_cli/handlers/update.py +3 -3
  9. praetorian_cli/main.py +1 -0
  10. praetorian_cli/sdk/chariot.py +71 -12
  11. praetorian_cli/sdk/entities/aegis.py +437 -0
  12. praetorian_cli/sdk/entities/assets.py +30 -12
  13. praetorian_cli/sdk/entities/scanners.py +13 -0
  14. praetorian_cli/sdk/entities/schema.py +27 -0
  15. praetorian_cli/sdk/entities/seeds.py +108 -56
  16. praetorian_cli/sdk/mcp_server.py +2 -3
  17. praetorian_cli/sdk/model/aegis.py +156 -0
  18. praetorian_cli/sdk/model/query.py +1 -1
  19. praetorian_cli/sdk/model/utils.py +2 -8
  20. praetorian_cli/sdk/test/pytest.ini +1 -0
  21. praetorian_cli/sdk/test/test_asset.py +2 -2
  22. praetorian_cli/sdk/test/test_seed.py +13 -14
  23. praetorian_cli/sdk/test/test_z_cli.py +22 -24
  24. praetorian_cli/sdk/test/ui_mocks.py +133 -0
  25. praetorian_cli/sdk/test/utils.py +16 -4
  26. praetorian_cli/ui/__init__.py +3 -0
  27. praetorian_cli/ui/aegis/__init__.py +5 -0
  28. praetorian_cli/ui/aegis/commands/__init__.py +2 -0
  29. praetorian_cli/ui/aegis/commands/help.py +81 -0
  30. praetorian_cli/ui/aegis/commands/info.py +136 -0
  31. praetorian_cli/ui/aegis/commands/job.py +381 -0
  32. praetorian_cli/ui/aegis/commands/list.py +14 -0
  33. praetorian_cli/ui/aegis/commands/set.py +32 -0
  34. praetorian_cli/ui/aegis/commands/ssh.py +87 -0
  35. praetorian_cli/ui/aegis/constants.py +20 -0
  36. praetorian_cli/ui/aegis/menu.py +395 -0
  37. praetorian_cli/ui/aegis/utils.py +162 -0
  38. {praetorian_cli-2.2.1.dist-info → praetorian_cli-2.2.3.dist-info}/METADATA +4 -1
  39. {praetorian_cli-2.2.1.dist-info → praetorian_cli-2.2.3.dist-info}/RECORD +43 -24
  40. {praetorian_cli-2.2.1.dist-info → praetorian_cli-2.2.3.dist-info}/WHEEL +0 -0
  41. {praetorian_cli-2.2.1.dist-info → praetorian_cli-2.2.3.dist-info}/entry_points.txt +0 -0
  42. {praetorian_cli-2.2.1.dist-info → praetorian_cli-2.2.3.dist-info}/licenses/LICENSE +0 -0
  43. {praetorian_cli-2.2.1.dist-info → praetorian_cli-2.2.3.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  from praetorian_cli.handlers.utils import error
2
2
  from praetorian_cli.sdk.model.globals import Seed, Kind
3
+ from praetorian_cli.sdk.model.query import Query, Node, Filter, KIND_TO_LABEL
3
4
 
4
5
 
5
6
  class Seeds:
@@ -9,97 +10,148 @@ class Seeds:
9
10
  def __init__(self, api):
10
11
  self.api = api
11
12
 
12
- def add(self, dns, status=Seed.PENDING.value):
13
+ def add(self, status=Seed.PENDING.value, seed_type=Kind.ASSET.value, **kwargs):
13
14
  """
14
- Add a seed to the account.
15
-
16
- :param dns: The DNS name, IP address, or CIDR range to add as a seed. Accepts domain names (e.g., 'example.com'), IP addresses (e.g., '192.168.1.1'), or CIDR ranges (e.g., '192.168.1.0/24')
17
- :type dns: str
18
- :param status: Seed status from Seed enum ('A' for Active, 'F' for Frozen, 'D' for Deleted, 'P' for Pending, 'FR' for Frozen Rejected)
19
- :type status: str
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
20
22
  :return: The seed that was added
21
23
  :rtype: dict
22
24
  """
23
- return self.api.upsert('seed', dict(dns=dns, status=status))
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)
24
35
 
25
36
  def get(self, key):
26
37
  """
27
38
  Get details of a seed by key.
28
39
 
29
- :param key: Entity key in format #seed#{type}#{dns} where type is 'domain', 'ip', or 'cidr' and dns is the seed value
40
+ :param key: Entity key (e.g., '#asset#example.com#example.com')
30
41
  :type key: str
31
42
  :return: The seed matching the specified key, or None if not found
32
43
  :rtype: dict or None
33
44
  """
34
- return self.api.search.by_exact_key(key, False)
35
-
36
- def update(self, key, status):
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):
37
73
  """
38
- Update a seed's status.
39
-
40
- Note: The seed PUT endpoint is different from other PUT endpoints. This method
41
- internally uses the DNS of the original seed rather than the key for the update operation.
42
-
43
- :param key: Entity key in format #seed#{type}#{dns} where type is 'domain', 'ip', or 'cidr' and dns is the seed value
74
+ Update seed fields dynamically.
75
+
76
+ :param key: Seed/Asset key (e.g., '#seed#domain#example.com' or '#asset#domain#example.com')
44
77
  :type key: str
45
- :param status: Seed status from Seed enum ('A' for Active, 'F' for Frozen, 'D' for Deleted, 'P' for Pending, 'FR' for Frozen Rejected)
46
- :type status: str
78
+ :param status: Status for backward compatibility (can be positional)
79
+ :type status: str or None
80
+ :param kwargs: Fields to update
47
81
  :return: The updated seed, or None if the seed was not found
48
82
  :rtype: dict or None
49
83
  """
50
- seed = self.api.search.by_exact_key(key)
84
+
85
+ seed = self.get(key) # This already handles old key format conversion
51
86
  if seed:
52
- # the seed PUT endpoint is different from other PUT endpoints. This one has to
53
- # take the DNS of the original seed, instead of the key of the seed record.
54
- # TODO, 2024-12-23, peter: check with Noah as to why. Ideally, we should
55
- # standardize to how other endpoints do it
56
- return self.api.upsert('seed', dict(dns=seed['dns'], status=status))
87
+ update_payload = {
88
+ 'key': key,
89
+ 'status': status
90
+ }
91
+
92
+ return self.api.upsert('seed', update_payload)
57
93
  else:
58
- error(f'Seed {key} is not found.')
94
+ error(f'Seed {key} not found.')
59
95
 
60
96
  def delete(self, key):
61
97
  """
62
- Delete a seed by setting its status to DELETED.
63
-
64
- Note: This method does not actually delete the seed from the database. Instead,
65
- it sets the seed's status to DELETED ('D'), which marks it as deleted while
66
- preserving the record for audit purposes.
67
-
68
- :param key: Entity key in format #seed#{type}#{dns} where type is 'domain', 'ip', or 'cidr' and dns is the seed value
98
+ Delete seed (supports both old and new key formats).
99
+
100
+ :param key: Seed/Asset key (e.g., '#asset#domain#example.com')
69
101
  :type key: str
70
102
  :return: The seed that was marked as deleted, or None if the seed was not found
71
103
  :rtype: dict or None
72
104
  """
73
- seed = self.api.search.by_exact_key(key)
105
+ seed = self.get(key) # This already handles old key format conversion
106
+
74
107
  if seed:
75
- # TODO, 2024-12-23, peter: check with Noah why this is different from
76
- # deleting assets and risks
77
- return self.api.upsert('seed', dict(dns=seed['dns'], status=Seed.DELETED.value))
108
+ delete_payload = {
109
+ 'key': key,
110
+ 'status': Seed.DELETED.value
111
+ }
112
+
113
+ return self.api.upsert('seed', delete_payload)
78
114
  else:
79
- error(f'Seed {key} is not found.')
115
+ error(f'Seed {key} not found.')
80
116
 
81
- def list(self, type='', prefix_filter='', offset=None, pages=100000) -> tuple:
117
+ def list(self, seed_type=Kind.SEED.value, key_prefix='', pages=100000) -> tuple:
82
118
  """
83
- List seeds with optional filtering.
84
-
85
- :param type: The type of seed to filter by ('domain', 'ip', 'cidr'). If empty, returns all seed types
86
- :type type: str
87
- :param prefix_filter: Supply this to perform prefix-filtering of the seed DNS/IP values after the seed type portion of the key
88
- :type prefix_filter: str
89
- :param offset: The offset of the page you want to retrieve results. If this is not supplied, this function retrieves from the first page
90
- :type offset: str or None
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
91
125
  :param pages: The number of pages of results to retrieve. <mcp>Start with one page of results unless specifically requested.</mcp>
92
- :type pages: int
126
+ :seed_type pages: int
93
127
  :return: A tuple containing (list of seeds, next page offset)
94
- :rtype: tuple
128
+ :rseed_type: tuple
95
129
  """
96
- prefix_term = '#seed#'
97
- if type:
98
- prefix_term = f'{prefix_term}{type}#'
99
- if prefix_filter:
100
- prefix_term = f'{prefix_term}{prefix_filter}'
101
130
 
102
- return self.api.search.by_key_prefix(prefix_term, offset, pages)
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)
103
155
 
104
156
  def attributes(self, key):
105
157
  """
@@ -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 = {
@@ -0,0 +1,156 @@
1
+ """
2
+ Aegis agent data models and structures.
3
+
4
+ This module contains dataclass definitions for Aegis agent entities,
5
+ including network interfaces, tunnel status, health checks, and agent metadata.
6
+ """
7
+ from dataclasses import dataclass
8
+ from typing import Optional, List, Dict, Any
9
+ from datetime import datetime
10
+
11
+
12
+ @dataclass
13
+ class NetworkInterface:
14
+ """Represents a network interface on an agent"""
15
+ name: str
16
+ ip_addresses: List[str]
17
+
18
+ @classmethod
19
+ def from_dict(cls, data: Dict[str, Any]) -> 'NetworkInterface':
20
+ return cls(
21
+ name=data.get('name', ''),
22
+ ip_addresses=data.get('ip_addresses', [])
23
+ )
24
+
25
+
26
+ @dataclass
27
+ class CloudflaredStatus:
28
+ """Represents Cloudflared tunnel status"""
29
+ hostname: Optional[str] = None
30
+ tunnel_name: Optional[str] = None
31
+ authorized_users: Optional[str] = None
32
+
33
+ @classmethod
34
+ def from_dict(cls, data: Dict[str, Any]) -> 'CloudflaredStatus':
35
+ return cls(
36
+ hostname=data.get('hostname'),
37
+ tunnel_name=data.get('tunnel_name'),
38
+ authorized_users=data.get('authorized_users')
39
+ )
40
+
41
+
42
+ @dataclass
43
+ class HealthCheck:
44
+ """Represents agent health check data"""
45
+ cloudflared_status: Optional[CloudflaredStatus] = None
46
+
47
+ @classmethod
48
+ def from_dict(cls, data: Dict[str, Any]) -> 'HealthCheck':
49
+ cf_data = data.get('cloudflared_status')
50
+ return cls(
51
+ cloudflared_status=CloudflaredStatus.from_dict(cf_data) if cf_data else None
52
+ )
53
+
54
+
55
+ @dataclass
56
+ class Agent:
57
+ """Represents an Aegis agent"""
58
+ client_id: str = 'N/A'
59
+ hostname: str = 'Unknown'
60
+ fqdn: str = 'N/A'
61
+ os: str = 'unknown'
62
+ os_version: str = ''
63
+ architecture: str = 'Unknown'
64
+ last_seen_at: Optional[int] = None
65
+ network_interfaces: List[NetworkInterface] = None
66
+ health_check: Optional[HealthCheck] = None
67
+ key: Optional[str] = None
68
+
69
+ def __post_init__(self):
70
+ if self.network_interfaces is None:
71
+ self.network_interfaces = []
72
+
73
+ @classmethod
74
+ def from_dict(cls, data: Dict[str, Any]) -> 'Agent':
75
+ """Create an Agent from dictionary data"""
76
+ network_interfaces = []
77
+ for iface_data in data.get('network_interfaces', []):
78
+ if isinstance(iface_data, dict):
79
+ network_interfaces.append(NetworkInterface.from_dict(iface_data))
80
+
81
+ health_data = data.get('health_check')
82
+ health_check = HealthCheck.from_dict(health_data) if health_data else None
83
+
84
+ return cls(
85
+ client_id=data.get('client_id', 'N/A'),
86
+ hostname=data.get('hostname', 'Unknown'),
87
+ fqdn=data.get('fqdn', 'N/A'),
88
+ os=data.get('os', 'unknown'),
89
+ os_version=data.get('os_version', ''),
90
+ architecture=data.get('architecture', 'Unknown'),
91
+ last_seen_at=data.get('last_seen_at'),
92
+ network_interfaces=network_interfaces,
93
+ health_check=health_check,
94
+ key=data.get('key')
95
+ )
96
+
97
+
98
+ @property
99
+ def has_tunnel(self) -> bool:
100
+ """Check if agent has an active Cloudflare tunnel"""
101
+ return (self.health_check is not None and
102
+ self.health_check.cloudflared_status is not None and
103
+ self.health_check.cloudflared_status.hostname is not None)
104
+
105
+ @property
106
+ def is_online(self) -> bool:
107
+ """Check if agent is currently online (last seen within 60 seconds)"""
108
+ if not self.last_seen_at:
109
+ return False
110
+
111
+ current_time = datetime.now().timestamp()
112
+ last_seen_seconds = (self.last_seen_at / 1000000
113
+ if self.last_seen_at > 1000000000000
114
+ else self.last_seen_at)
115
+
116
+ return (current_time - last_seen_seconds) < 60
117
+
118
+ @property
119
+ def ip_addresses(self) -> List[str]:
120
+ """Get all non-loopback IP addresses"""
121
+ ips = []
122
+ for iface in self.network_interfaces:
123
+ if iface.name != 'lo': # Skip loopback
124
+ ips.extend(iface.ip_addresses)
125
+ return [ip for ip in ips if ip] # Filter empty strings
126
+
127
+ def __str__(self) -> str:
128
+ """Return a simple string representation of the agent"""
129
+ status = "🔗" if self.has_tunnel else "○"
130
+ return f"{status} {self.hostname} ({self.client_id})"
131
+
132
+ def to_detailed_string(self) -> str:
133
+ """Return a detailed string representation of the agent"""
134
+ os_info = f"{self.os} {self.os_version}".strip()
135
+
136
+ lines = [
137
+ f"\n{self.hostname} ({self.client_id})",
138
+ f" OS: {os_info}",
139
+ f" Architecture: {self.architecture}",
140
+ f" FQDN: {self.fqdn}"
141
+ ]
142
+
143
+ if self.has_tunnel:
144
+ cf_status = self.health_check.cloudflared_status
145
+ lines.append(f" Tunnel: {cf_status.tunnel_name}")
146
+ lines.append(f" Public hostname: {cf_status.hostname}")
147
+ if cf_status.authorized_users:
148
+ lines.append(f" Authorized users: {cf_status.authorized_users}")
149
+ else:
150
+ lines.append(" Tunnel: Not configured")
151
+
152
+ ips = self.ip_addresses
153
+ if ips:
154
+ lines.append(f" IP addresses: {', '.join(ips)}")
155
+
156
+ return '\n'.join(lines)
@@ -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}'
@@ -3,3 +3,4 @@ addopts = -p no:cacheprovider
3
3
  markers =
4
4
  coherence: these are tests on product workflows via the backend API
5
5
  cli: these are tests on the CLI interface
6
+ tui: these are tests on the TUI interface
@@ -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):
@@ -1,7 +1,6 @@
1
1
  import pytest
2
2
 
3
- from praetorian_cli.sdk.model.globals import Seed
4
- from praetorian_cli.sdk.model.utils import seed_status
3
+ from praetorian_cli.sdk.model.globals import Seed, Kind
5
4
  from praetorian_cli.sdk.test.utils import make_test_values, clean_test_entities, setup_chariot
6
5
 
7
6
 
@@ -12,30 +11,30 @@ class TestSeed:
12
11
  self.sdk = setup_chariot()
13
12
  make_test_values(self)
14
13
 
15
- def test_add_seed(self):
16
- seed = self.sdk.seeds.add(self.seed_dns)
17
- assert seed['key'] == self.seed_key
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
18
17
 
19
18
  def test_get_seed(self):
20
19
  a = self.get_seed()
21
- assert a['dns'] == self.seed_dns
22
- assert a['status'] == seed_status('domain', Seed.PENDING.value)
20
+ assert a['dns'] == self.seed_asset_dns
21
+ assert a['status'] == Seed.PENDING.value
23
22
 
24
23
  def test_list_seed(self):
25
- results, _ = self.sdk.seeds.list('domain', self.seed_dns)
24
+ results, _ = self.sdk.seeds.list(Kind.ASSET.value, f"#asset#{self.seed_asset_dns}")
26
25
  assert len(results) == 1
27
- assert results[0]['dns'] == self.seed_dns
26
+ assert results[0]['dns'] == self.seed_asset_dns
28
27
 
29
28
  def test_update_seed(self):
30
- self.sdk.seeds.update(self.seed_key, Seed.ACTIVE.value)
31
- assert self.get_seed()['status'] == seed_status('domain', Seed.ACTIVE.value)
29
+ self.sdk.seeds.update(self.seed_asset_key, Seed.ACTIVE.value)
30
+ assert self.get_seed()['status'] == Seed.ACTIVE.value
32
31
 
33
32
  def test_delete_seed(self):
34
- self.sdk.seeds.delete(self.seed_key)
35
- assert self.sdk.seeds.get(self.seed_key)['status'] == seed_status('domain', Seed.DELETED.value)
33
+ self.sdk.seeds.delete(self.seed_asset_key)
34
+ assert self.sdk.seeds.get(self.seed_asset_key)['status'] == Seed.DELETED.value
36
35
 
37
36
  def get_seed(self):
38
- return self.sdk.seeds.get(self.seed_key)
37
+ return self.sdk.seeds.get(self.seed_asset_key)
39
38
 
40
39
  def teardown_class(self):
41
40
  clean_test_entities(self.sdk, self)
@@ -5,7 +5,6 @@ from subprocess import run
5
5
  import pytest
6
6
 
7
7
  from praetorian_cli.sdk.model.globals import AddRisk, Asset, Risk, Seed, Preseed
8
- from praetorian_cli.sdk.model.utils import seed_status
9
8
  from praetorian_cli.sdk.test.utils import epoch_micro, random_ip, make_test_values, clean_test_entities, setup_chariot
10
9
 
11
10
 
@@ -21,11 +20,12 @@ class TestZCli:
21
20
  self.verify(f'add asset -n {o.asset_name} -d {o.asset_dns}')
22
21
 
23
22
  self.verify('list assets -p all', [o.asset_key])
24
- self.verify(f'list assets -f "{o.asset_dns}"', [o.asset_key])
25
- self.verify(f'list assets -f "{o.asset_dns}" -p first', [o.asset_key])
26
- self.verify(f'list assets -f "{o.asset_dns}" -p all', [o.asset_key])
27
- self.verify(f'list assets -f "{o.asset_dns}" -d', [o.asset_key, '"key"', '"data"'])
23
+ self.verify(f'list assets -f "#asset#{o.asset_dns}"', [o.asset_key])
24
+ self.verify(f'list assets -f "#asset#{o.asset_dns}" -p first', [o.asset_key])
25
+ self.verify(f'list assets -f "#asset#{o.asset_dns}" -p all', [o.asset_key])
26
+ self.verify(f'list assets -f "#asset#{o.asset_dns}" -d', [o.asset_key, '"key"', '"data"'])
28
27
 
28
+ self.verify(f'list assets -f "#asset#{epoch_micro()}"')
29
29
  self.verify(f'list assets -f {epoch_micro()}')
30
30
 
31
31
  self.verify(f'get asset "{o.asset_key}"', [o.asset_key, f'"status": "{Asset.ACTIVE.value}"'])
@@ -43,30 +43,28 @@ class TestZCli:
43
43
  def test_seed_cli(self):
44
44
  o = make_test_values(lambda: None)
45
45
 
46
- self.verify(f'add seed -d {o.seed_dns}')
46
+ self.verify(f'add seed --field dns:{o.seed_asset_dns}')
47
47
 
48
- self.verify('list seeds -p all', [o.seed_key])
49
- self.verify('list seeds -t domain -p all', [o.seed_key])
50
- self.verify(f'list seeds -t domain -f "{o.seed_dns}"', [o.seed_key])
51
- self.verify(f'list seeds -t domain -f "{o.seed_dns}" -p first', [o.seed_key])
52
- self.verify(f'list seeds -t domain -f "{o.seed_dns}" -p all', [o.seed_key])
53
- self.verify(f'list seeds -t domain -f "{o.seed_dns}" -p first', [o.seed_key])
54
- self.verify(f'list seeds -t domain -f "{o.seed_dns}" -d', [o.seed_dns, '"key"', '"data"'])
55
- self.verify(f'list seeds -t ip -f "{o.seed_dns}"')
56
- self.verify(f'list seeds -f "{o.seed_dns}"', [],
57
- ["When the DNS filter is specified, you also need to specify the type of the filter"])
48
+ self.verify('list seeds -p all', [o.seed_asset_key])
49
+ self.verify('list seeds -t asset -p all', [o.seed_asset_key])
50
+ self.verify(f'list seeds -t asset -f "#asset#{o.seed_asset_dns}"', [o.seed_asset_key])
51
+ self.verify(f'list seeds -t asset -f "#asset#{o.seed_asset_dns}" -p first', [o.seed_asset_key])
52
+ self.verify(f'list seeds -t asset -f "#asset#{o.seed_asset_dns}" -p all', [o.seed_asset_key])
53
+ self.verify(f'list seeds -t asset -f "#asset#{o.seed_asset_dns}" -d', [o.seed_asset_dns, '"key"', '"data"'])
54
+ self.verify(f'list seeds -t notatype -f "#asset#{o.seed_asset_dns}"', [], ['Invalid seed type: notatype'])
55
+ self.verify(f'list seeds -f "#asset#{o.seed_asset_dns}"', [o.seed_asset_key])
58
56
 
59
- self.verify(f'list seeds -t domain -f {epoch_micro()}')
57
+ self.verify(f'list seeds -t asset -f {epoch_micro()}')
60
58
 
61
- self.verify(f'get seed "{o.seed_key}"',
62
- [o.seed_key, f'"status": "{seed_status("domain", Seed.PENDING.value)}"'])
59
+ self.verify(f'get seed "{o.seed_asset_key}"',
60
+ [o.seed_asset_key, f'"status": "{Seed.PENDING.value}"'])
63
61
 
64
- self.verify(f'update seed -s {Seed.ACTIVE.value} "{o.seed_key}"')
65
- self.verify(f'get seed "{o.seed_key}"',
66
- [o.seed_key, f'"status": "{seed_status("domain", Seed.ACTIVE.value)}"'])
62
+ self.verify(f'update seed -s {Seed.ACTIVE.value} "{o.seed_asset_key}"')
63
+ self.verify(f'get seed "{o.seed_asset_key}"',
64
+ [o.seed_asset_key, f'"status": "{Seed.ACTIVE.value}"'])
67
65
 
68
- self.verify(f'delete seed "{o.seed_key}"')
69
- self.verify(f'get seed "{o.seed_key}"', [f'"status": "{seed_status("domain", Seed.DELETED.value)}"'])
66
+ self.verify(f'delete seed "{o.seed_asset_key}"')
67
+ self.verify(f'get seed "{o.seed_asset_key}"', [f'"status": "{Seed.DELETED.value}"'])
70
68
 
71
69
  clean_test_entities(self.sdk, o)
72
70