praetorian-cli 2.2.3__py3-none-any.whl → 2.2.4__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.
- praetorian_cli/handlers/add.py +22 -0
- praetorian_cli/handlers/cli_decorators.py +1 -1
- praetorian_cli/handlers/delete.py +12 -0
- praetorian_cli/handlers/get.py +17 -0
- praetorian_cli/handlers/link.py +29 -1
- praetorian_cli/handlers/list.py +22 -0
- praetorian_cli/handlers/unlink.py +29 -1
- praetorian_cli/sdk/chariot.py +2 -0
- praetorian_cli/sdk/entities/webpage.py +180 -0
- praetorian_cli/sdk/model/globals.py +2 -0
- praetorian_cli/sdk/model/query.py +7 -0
- praetorian_cli/sdk/test/test_asset.py +36 -0
- praetorian_cli/sdk/test/test_webpage.py +46 -0
- praetorian_cli/sdk/test/test_z_cli.py +31 -0
- praetorian_cli/sdk/test/utils.py +5 -0
- {praetorian_cli-2.2.3.dist-info → praetorian_cli-2.2.4.dist-info}/METADATA +1 -1
- {praetorian_cli-2.2.3.dist-info → praetorian_cli-2.2.4.dist-info}/RECORD +21 -19
- {praetorian_cli-2.2.3.dist-info → praetorian_cli-2.2.4.dist-info}/WHEEL +0 -0
- {praetorian_cli-2.2.3.dist-info → praetorian_cli-2.2.4.dist-info}/entry_points.txt +0 -0
- {praetorian_cli-2.2.3.dist-info → praetorian_cli-2.2.4.dist-info}/licenses/LICENSE +0 -0
- {praetorian_cli-2.2.3.dist-info → praetorian_cli-2.2.4.dist-info}/top_level.txt +0 -0
praetorian_cli/handlers/add.py
CHANGED
|
@@ -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
|
|
@@ -328,3 +332,21 @@ def key(sdk, name, expires):
|
|
|
328
332
|
return
|
|
329
333
|
click.echo(f'API key created: {result.get("key", "N/A")}')
|
|
330
334
|
click.echo(f'Secret (save this, it will not be shown again): {result["secret"]}')
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
@add.command()
|
|
338
|
+
@cli_handler
|
|
339
|
+
@click.option('-u', '--url', required=True, help='The full URL of the page')
|
|
340
|
+
@click.option('-p', '--parent', required=False, help='Optional key of the parent WebApplication')
|
|
341
|
+
def webpage(sdk, url, parent):
|
|
342
|
+
""" Add a Webpage
|
|
343
|
+
|
|
344
|
+
Add a web page to the Chariot database. Webpages can optionally be associated
|
|
345
|
+
with a parent WebApplication or exist independently.
|
|
346
|
+
|
|
347
|
+
\b
|
|
348
|
+
Example usages:
|
|
349
|
+
- praetorian chariot add webpage --url https://app.example.com/login
|
|
350
|
+
- praetorian chariot add webpage --url https://app.example.com/admin --parent "#webapplication#https://app.example.com"
|
|
351
|
+
"""
|
|
352
|
+
sdk.webpage.add(url, parent)
|
|
@@ -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=
|
|
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)
|
praetorian_cli/handlers/get.py
CHANGED
|
@@ -300,6 +300,23 @@ def scanner(chariot, key):
|
|
|
300
300
|
|
|
301
301
|
@get.command()
|
|
302
302
|
@cli_handler
|
|
303
|
+
@click.argument('key', required=True)
|
|
304
|
+
def webpage(chariot, key):
|
|
305
|
+
""" Get Webpage details
|
|
306
|
+
|
|
307
|
+
Retrieve detailed information about a specific web page, including
|
|
308
|
+
its URL, method, authentication requirements, and other metadata.
|
|
309
|
+
|
|
310
|
+
\b
|
|
311
|
+
Argument:
|
|
312
|
+
- KEY: the key of an existing Webpage
|
|
313
|
+
|
|
314
|
+
\b
|
|
315
|
+
Example usages:
|
|
316
|
+
- praetorian chariot get webpage "#webpage#https://app.example.com/dashboard"
|
|
317
|
+
"""
|
|
318
|
+
print_json(chariot.webpage.get(key))
|
|
319
|
+
|
|
303
320
|
@click.option('-t', '--type', help='Optional specific entity type (e.g., asset, risk, attribute)')
|
|
304
321
|
@click.option('-d', '--details', is_flag=True, help='Further retrieve the details of the schema')
|
|
305
322
|
def schema(chariot, type, details):
|
praetorian_cli/handlers/link.py
CHANGED
|
@@ -6,7 +6,7 @@ from praetorian_cli.handlers.cli_decorators import cli_handler
|
|
|
6
6
|
|
|
7
7
|
@chariot.group()
|
|
8
8
|
def link():
|
|
9
|
-
"""
|
|
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")
|
praetorian_cli/handlers/list.py
CHANGED
|
@@ -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
|
|
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")
|
praetorian_cli/sdk/chariot.py
CHANGED
|
@@ -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
|
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
from praetorian_cli.sdk.model.query import Query, Node, Filter, Relationship
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Webpage:
|
|
5
|
+
"""The methods in this class are to be accessed from sdk.webpage, where sdk
|
|
6
|
+
is an instance of Chariot."""
|
|
7
|
+
|
|
8
|
+
def __init__(self, api):
|
|
9
|
+
self.api = api
|
|
10
|
+
|
|
11
|
+
def add(self, url, parent_key=None):
|
|
12
|
+
"""
|
|
13
|
+
Add a Webpage to the Chariot database.
|
|
14
|
+
|
|
15
|
+
WebPages represent individual pages or endpoints that can be optionally
|
|
16
|
+
associated with a parent WebApplication. The backend uses a WebpageRequest
|
|
17
|
+
structure with embedded webpage data and optional parent key.
|
|
18
|
+
|
|
19
|
+
:param url: The full URL of the page
|
|
20
|
+
:type url: str
|
|
21
|
+
:param parent_key: Optional key of the parent WebApplication
|
|
22
|
+
:type webapp_key: str or None
|
|
23
|
+
:return: The created WebPage object
|
|
24
|
+
:rtype: dict
|
|
25
|
+
:raises Exception: If the URL is invalid or the request fails
|
|
26
|
+
|
|
27
|
+
**Example Usage:**
|
|
28
|
+
>>> # Add a simple page without parent
|
|
29
|
+
>>> page = sdk.webpage.add("https://app.example.com/login")
|
|
30
|
+
|
|
31
|
+
>>> # Add a page with parent WebApplication
|
|
32
|
+
>>> page = sdk.webpage.add(
|
|
33
|
+
... url="https://app.example.com/admin",
|
|
34
|
+
... parent_key="#webapplication#https://app.example.com")
|
|
35
|
+
|
|
36
|
+
**WebPage Object Structure:**
|
|
37
|
+
The returned Webpage object contains:
|
|
38
|
+
- key: Webpage identifier in format #webpage#{url}
|
|
39
|
+
- url: Full URL of the page
|
|
40
|
+
- status: Current status
|
|
41
|
+
- parent: Parent WebApplication relationship (if applicable)
|
|
42
|
+
- created: Creation timestamp
|
|
43
|
+
"""
|
|
44
|
+
if not url:
|
|
45
|
+
raise Exception("URL is required for Webpage")
|
|
46
|
+
|
|
47
|
+
if parent_key and not parent_key.startswith('#webapplication#'):
|
|
48
|
+
raise Exception("Invalid WebApplication key format")
|
|
49
|
+
|
|
50
|
+
payload = {
|
|
51
|
+
'webpage': {
|
|
52
|
+
'url': url,
|
|
53
|
+
'status': 'A' # Active status
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if parent_key:
|
|
59
|
+
payload['parent_key'] = parent_key
|
|
60
|
+
|
|
61
|
+
return self.api.post('webpage', payload)
|
|
62
|
+
|
|
63
|
+
def get(self, key):
|
|
64
|
+
"""
|
|
65
|
+
Get details of a specific Webpage by its key.
|
|
66
|
+
|
|
67
|
+
:param key: The WebPage key identifier
|
|
68
|
+
:type key: str
|
|
69
|
+
:return: Webpage object with detailed information, or None if not found
|
|
70
|
+
:rtype: dict or None
|
|
71
|
+
|
|
72
|
+
**Example Usage:**
|
|
73
|
+
>>> # Get a specific Webpage
|
|
74
|
+
>>> page = sdk.webpage.get("webpage_key_123")
|
|
75
|
+
|
|
76
|
+
**Webpage Object Structure:**
|
|
77
|
+
The returned Webpage object contains:
|
|
78
|
+
- key: Webpage identifier
|
|
79
|
+
- url: Full URL of the page
|
|
80
|
+
- created: Creation timestamp
|
|
81
|
+
- updated: Last update timestamp
|
|
82
|
+
"""
|
|
83
|
+
query = Query(node=Node(labels=[Node.Label.WEBPAGE], filters=[Filter(field=Filter.Field.KEY, operator=Filter.Operator.EQUAL, value=key)]))
|
|
84
|
+
return self.api.search.by_query(query)[0][0]
|
|
85
|
+
|
|
86
|
+
def list(self, parent_key=None, filter=None, offset=0, pages=100000) -> tuple:
|
|
87
|
+
"""
|
|
88
|
+
List Webpages, optionally filtered by parent WebApplication.
|
|
89
|
+
|
|
90
|
+
Retrieve Webpage entities with optional filtering capabilities. Can filter by
|
|
91
|
+
parent WebApplication.
|
|
92
|
+
|
|
93
|
+
:param parent_key: Filter pages by specific WebApplication (optional)
|
|
94
|
+
:type parent_key: str or None
|
|
95
|
+
:param filter: Filter pages by specific URL (optional)
|
|
96
|
+
:type filter: str or None
|
|
97
|
+
:param offset: The offset for pagination to retrieve a specific page of results
|
|
98
|
+
:type offset: str or None
|
|
99
|
+
:param pages: Maximum number of pages to retrieve (default: 100000 for all results)
|
|
100
|
+
:type pages: int
|
|
101
|
+
:return: A tuple containing (list of matching Webpages, next page offset)
|
|
102
|
+
:rtype: tuple
|
|
103
|
+
|
|
104
|
+
**Example Usage:**
|
|
105
|
+
>>> # List all WebPages
|
|
106
|
+
>>> pages, offset = sdk.webpage.list()
|
|
107
|
+
|
|
108
|
+
>>> # List pages for specific WebApplication
|
|
109
|
+
>>> pages, offset = sdk.webpage.list(
|
|
110
|
+
... webapp_key="#asset#webapp#https://app.example.com#https://app.example.com")
|
|
111
|
+
|
|
112
|
+
**WebPage Filtering:**
|
|
113
|
+
- parent_key: Filters by parent WebApplication
|
|
114
|
+
- filter: Filters by specific URL
|
|
115
|
+
"""
|
|
116
|
+
if parent_key and not parent_key.startswith('#webapplication#'):
|
|
117
|
+
raise Exception("Invalid WebApplication key format")
|
|
118
|
+
|
|
119
|
+
relationships = []
|
|
120
|
+
filters = []
|
|
121
|
+
if parent_key:
|
|
122
|
+
parentFilter = Filter(field=Filter.Field.KEY, operator=Filter.Operator.EQUAL, value=parent_key)
|
|
123
|
+
relationship = Relationship(label=Relationship.Label.HAS_WEBPAGE, target=Node(labels=[Node.Label.WEBAPPLICATION], filters=[parentFilter]))
|
|
124
|
+
relationships.append(relationship)
|
|
125
|
+
if filter:
|
|
126
|
+
urlFilter = Filter(field=Filter.Field.KEY, operator=Filter.Operator.CONTAINS, value=filter)
|
|
127
|
+
filters.append(urlFilter)
|
|
128
|
+
node = Node(labels=[Node.Label.WEBPAGE], filters=filters, relationships=relationships)
|
|
129
|
+
query = Query(node=node, page=offset, limit=pages)
|
|
130
|
+
return self.api.search.by_query(query, pages)
|
|
131
|
+
|
|
132
|
+
def delete(self, key):
|
|
133
|
+
"""
|
|
134
|
+
Delete a webpage by its key.
|
|
135
|
+
|
|
136
|
+
:param key: The WebPage key identifier
|
|
137
|
+
:type key: str
|
|
138
|
+
"""
|
|
139
|
+
body = {
|
|
140
|
+
'webpage': {
|
|
141
|
+
'key': key
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
self.api.delete('webpage', params={}, body=body)
|
|
145
|
+
|
|
146
|
+
def link_source(self, webpage_key, entity_key):
|
|
147
|
+
"""
|
|
148
|
+
Link a file or repository to a webpage as source code.
|
|
149
|
+
|
|
150
|
+
:param webpage_key: The webpage key in format #webpage#{url}
|
|
151
|
+
:type webpage_key: str
|
|
152
|
+
:param entity_key: The entity key (file or repository) to link. Format: #file#{path} or #repository#{url}#{name}
|
|
153
|
+
:type entity_key: str
|
|
154
|
+
:return: The updated webpage with linked artifacts
|
|
155
|
+
:rtype: dict
|
|
156
|
+
"""
|
|
157
|
+
data = {
|
|
158
|
+
'webpageKey': webpage_key,
|
|
159
|
+
'entityKey': entity_key
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return self.api.put('webpage/link', data, {})
|
|
163
|
+
|
|
164
|
+
def unlink_source(self, webpage_key, entity_key):
|
|
165
|
+
"""
|
|
166
|
+
Unlink a file or repository from a webpage's source code.
|
|
167
|
+
|
|
168
|
+
:param webpage_key: The webpage key in format #webpage#{url}
|
|
169
|
+
:type webpage_key: str
|
|
170
|
+
:param entity_key: The entity key (file or repository) to unlink. Format: #file#{path} or #repository#{url}#{name}
|
|
171
|
+
:type entity_key: str
|
|
172
|
+
:return: The updated webpage with artifacts removed
|
|
173
|
+
:rtype: dict
|
|
174
|
+
"""
|
|
175
|
+
data = {
|
|
176
|
+
'webpageKey': webpage_key,
|
|
177
|
+
'entityKey': entity_key
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return self.api.delete('webpage/link', data, {})
|
|
@@ -49,6 +49,8 @@ class Filter:
|
|
|
49
49
|
KEV = 'kev'
|
|
50
50
|
EXPLOIT = 'exploit'
|
|
51
51
|
PRIVATE = 'private'
|
|
52
|
+
PRIMARY_URL = 'primary_url'
|
|
53
|
+
URL = 'url'
|
|
52
54
|
|
|
53
55
|
def __init__(self, field: Field, operator: Operator, value: str, not_: bool = False):
|
|
54
56
|
self.field = field
|
|
@@ -65,6 +67,7 @@ class Relationship:
|
|
|
65
67
|
HAS_VULNERABILITY = 'HAS_VULNERABILITY'
|
|
66
68
|
DISCOVERED = 'DISCOVERED'
|
|
67
69
|
HAS_ATTRIBUTE = 'HAS_ATTRIBUTE'
|
|
70
|
+
HAS_WEBPAGE = 'HAS_WEBPAGE'
|
|
68
71
|
|
|
69
72
|
def __init__(self, label: Label, source: 'Node' = None, target: 'Node' = None, optional: bool = False, length: int = 0):
|
|
70
73
|
self.label = label
|
|
@@ -97,6 +100,8 @@ class Node:
|
|
|
97
100
|
PRESEED = 'Preseed'
|
|
98
101
|
SEED = 'Seed'
|
|
99
102
|
TTL = 'TTL'
|
|
103
|
+
WEBAPPLICATION = 'WebApplication'
|
|
104
|
+
WEBPAGE = 'Webpage'
|
|
100
105
|
|
|
101
106
|
def __init__(self, labels: list[Label] = None, filters: list[Filter] = None,
|
|
102
107
|
relationships: list[Relationship] = None):
|
|
@@ -157,6 +162,8 @@ KIND_TO_LABEL = {
|
|
|
157
162
|
Kind.REPOSITORY.value: Node.Label.REPOSITORY,
|
|
158
163
|
Kind.INTEGRATION.value: Node.Label.INTEGRATION,
|
|
159
164
|
Kind.ADDOMAIN.value: Node.Label.ADDOMAIN,
|
|
165
|
+
Kind.WEBAPPLICATION.value: Node.Label.WEBAPPLICATION,
|
|
166
|
+
Kind.WEBPAGE.value: Node.Label.WEBPAGE,
|
|
160
167
|
}
|
|
161
168
|
|
|
162
169
|
|
|
@@ -71,11 +71,47 @@ class TestAsset:
|
|
|
71
71
|
deleted_assets, _ = self.sdk.search.by_status(Asset.DELETED.value, Kind.ADDOMAIN.value)
|
|
72
72
|
assert any([a['key'] == self.ad_domain_key for a in deleted_assets])
|
|
73
73
|
|
|
74
|
+
def test_add_webapplication(self):
|
|
75
|
+
asset = self.sdk.assets.add(self.webapp_name, self.webapp_url, status=Asset.ACTIVE.value, surface='test-surface', type=Kind.WEBAPPLICATION.value)
|
|
76
|
+
assert asset['key'] == self.webapp_key
|
|
77
|
+
assert len(asset['attackSurface']) == 1
|
|
78
|
+
assert 'test-surface' in asset['attackSurface']
|
|
79
|
+
assert asset['status'] == Asset.ACTIVE.value
|
|
80
|
+
|
|
81
|
+
def test_get_webapplication(self):
|
|
82
|
+
asset = self.sdk.assets.get(self.webapp_key)
|
|
83
|
+
assert asset['key'] == self.webapp_key
|
|
84
|
+
assert asset['group'] == self.webapp_name
|
|
85
|
+
assert asset['identifier'] == self.webapp_url
|
|
86
|
+
assert asset['status'] == Asset.ACTIVE.value
|
|
87
|
+
|
|
88
|
+
def test_list_webapplication(self):
|
|
89
|
+
results, _ = self.sdk.assets.list(asset_type=Kind.WEBAPPLICATION.value)
|
|
90
|
+
assert len(results) > 0
|
|
91
|
+
assert any([a['key'] == self.webapp_key for a in results])
|
|
92
|
+
assert any([a['group'] == self.webapp_name for a in results])
|
|
93
|
+
assert any([a['identifier'] == self.webapp_url for a in results])
|
|
94
|
+
|
|
95
|
+
def test_update_webapplication(self):
|
|
96
|
+
self.sdk.assets.update(self.webapp_key, status=Asset.FROZEN.value, surface='abc')
|
|
97
|
+
asset = self.get_webapplication()
|
|
98
|
+
assert asset['status'] == Asset.FROZEN.value
|
|
99
|
+
assert 'abc' in asset['attackSurface']
|
|
100
|
+
|
|
101
|
+
def test_delete_webapplication(self):
|
|
102
|
+
self.sdk.assets.delete(self.webapp_key)
|
|
103
|
+
assert self.get_webapplication()['status'] == Asset.DELETED.value
|
|
104
|
+
deleted_assets, _ = self.sdk.search.by_status(Asset.DELETED.value, Kind.WEBAPPLICATION.value)
|
|
105
|
+
assert any([a['key'] == self.webapp_key for a in deleted_assets])
|
|
106
|
+
|
|
74
107
|
def get_asset(self):
|
|
75
108
|
return self.sdk.assets.get(self.asset_key)
|
|
76
109
|
|
|
77
110
|
def get_ad_domain(self):
|
|
78
111
|
return self.sdk.assets.get(self.ad_domain_key)
|
|
79
112
|
|
|
113
|
+
def get_webapplication(self):
|
|
114
|
+
return self.sdk.assets.get(self.webapp_key)
|
|
115
|
+
|
|
80
116
|
def teardown_class(self):
|
|
81
117
|
clean_test_entities(self.sdk, self)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from praetorian_cli.sdk.test.utils import make_test_values, setup_chariot
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@pytest.mark.coherence
|
|
7
|
+
class TestWebpage:
|
|
8
|
+
"""Test suite for the Webpage entity class."""
|
|
9
|
+
|
|
10
|
+
def setup_class(self):
|
|
11
|
+
self.sdk = setup_chariot()
|
|
12
|
+
make_test_values(self)
|
|
13
|
+
|
|
14
|
+
def test_add_webpage(self):
|
|
15
|
+
"""Test adding a Webpage with URL provided."""
|
|
16
|
+
result = self.sdk.webpage.add(self.webpage_url)
|
|
17
|
+
|
|
18
|
+
assert result is not None
|
|
19
|
+
webpage = result.get('webpages')[0]
|
|
20
|
+
assert webpage.get('key') == self.webpage_key
|
|
21
|
+
assert webpage.get('url') == self.webpage_url
|
|
22
|
+
|
|
23
|
+
def test_get_webpage(self):
|
|
24
|
+
"""Test retrieving a Webpage by key."""
|
|
25
|
+
result = self.sdk.webpage.get(self.webpage_key)
|
|
26
|
+
assert result is not None
|
|
27
|
+
assert result.get('key') == self.webpage_key
|
|
28
|
+
assert result.get('url') == self.webpage_url
|
|
29
|
+
|
|
30
|
+
def test_list_webpages(self):
|
|
31
|
+
"""Test listing Webpages."""
|
|
32
|
+
results, offset = self.sdk.webpage.list(filter=self.webpage_url[:len(self.webpage_url)//2])
|
|
33
|
+
assert isinstance(results, list)
|
|
34
|
+
assert len(results) > 0
|
|
35
|
+
assert any(r.get('key') == self.webpage_key for r in results)
|
|
36
|
+
assert any(r.get('url') == self.webpage_url for r in results)
|
|
37
|
+
|
|
38
|
+
def test_add_webpage_empty_url_raises_exception(self):
|
|
39
|
+
"""Test that adding a Webpage with empty URL raises an exception."""
|
|
40
|
+
with pytest.raises(Exception, match="URL is required for Webpage"):
|
|
41
|
+
self.sdk.webpage.add("")
|
|
42
|
+
|
|
43
|
+
def test_add_webpage_none_url_raises_exception(self):
|
|
44
|
+
"""Test that adding a Webpage with None URL raises an exception."""
|
|
45
|
+
with pytest.raises(Exception, match="URL is required for Webpage"):
|
|
46
|
+
self.sdk.webpage.add(None)
|
|
@@ -210,6 +210,21 @@ class TestZCli:
|
|
|
210
210
|
self.verify(f'list accounts -f {o.email}', [o.email])
|
|
211
211
|
self.verify(f'unlink account {o.email}')
|
|
212
212
|
self.verify(f'list accounts -f {o.email}')
|
|
213
|
+
|
|
214
|
+
def test_webpage_source_cli(self):
|
|
215
|
+
o = make_test_values(lambda: None)
|
|
216
|
+
|
|
217
|
+
self.verify(f'add webpage --url "{o.webpage_url}"')
|
|
218
|
+
|
|
219
|
+
file_key = f'"#file#test-nonexistent-{epoch_micro()}-2.txt"'
|
|
220
|
+
|
|
221
|
+
self.verify(f'link webpage-source "{o.webpage_key}" {file_key}',
|
|
222
|
+
expected_stderr=['not found'])
|
|
223
|
+
self.verify(f'unlink webpage-source "{o.webpage_key}" {file_key}',
|
|
224
|
+
expected_stdout=[o.webpage_key])
|
|
225
|
+
|
|
226
|
+
# Clean up
|
|
227
|
+
self.verify(f'delete webpage "{o.webpage_key}"', ignore_stdout=True)
|
|
213
228
|
|
|
214
229
|
def test_integration_cli(self):
|
|
215
230
|
self.verify('list integrations', ignore_stdout=True)
|
|
@@ -258,6 +273,20 @@ class TestZCli:
|
|
|
258
273
|
|
|
259
274
|
self.verify(f'delete configuration "{o.configuration_key}"', ignore_stdout=True)
|
|
260
275
|
|
|
276
|
+
def test_webapplication_cli(self):
|
|
277
|
+
o = make_test_values(lambda: None)
|
|
278
|
+
self.verify(f'add asset --dns "{o.webapp_name}" --name "{o.webapp_url}" --type webapplication')
|
|
279
|
+
self.verify(f'get asset "{o.webapp_key}"', expected_stdout=[o.webapp_key, o.webapp_url, o.webapp_name, '"status"', '"A"'])
|
|
280
|
+
self.verify(f'list assets -f "{o.webapp_name}"', expected_stdout=[o.webapp_key])
|
|
281
|
+
self.verify(f'delete asset "{o.webapp_key}"', ignore_stdout=True)
|
|
282
|
+
|
|
283
|
+
def test_webpage_cli(self):
|
|
284
|
+
o = make_test_values(lambda: None)
|
|
285
|
+
self.verify(f'add webpage --url "{o.webpage_url}"')
|
|
286
|
+
self.verify(f'get webpage "{o.webpage_key}"', expected_stdout=[o.webpage_key, o.webpage_url, '"status"', '"A"'])
|
|
287
|
+
self.verify(f'list webpages -p all -f "{o.webpage_url[:len(o.webpage_url)//2]}"', expected_stdout=[o.webpage_key])
|
|
288
|
+
self.verify(f'delete webpage "{o.webpage_key}"', ignore_stdout=True)
|
|
289
|
+
|
|
261
290
|
def test_help_cli(self):
|
|
262
291
|
self.verify('--help', ignore_stdout=True)
|
|
263
292
|
self.verify('list --help', ignore_stdout=True)
|
|
@@ -310,9 +339,11 @@ class TestZCli:
|
|
|
310
339
|
|
|
311
340
|
self.verify('link --help', ignore_stdout=True)
|
|
312
341
|
self.verify('link account --help', ignore_stdout=True)
|
|
342
|
+
self.verify('link webpage-source --help', ignore_stdout=True)
|
|
313
343
|
|
|
314
344
|
self.verify('unlink --help', ignore_stdout=True)
|
|
315
345
|
self.verify('unlink account --help', ignore_stdout=True)
|
|
346
|
+
self.verify('unlink webpage-source --help', ignore_stdout=True)
|
|
316
347
|
|
|
317
348
|
self.verify('delete --help', ignore_stdout=True)
|
|
318
349
|
self.verify('delete asset --help', ignore_stdout=True)
|
praetorian_cli/sdk/test/utils.py
CHANGED
|
@@ -66,6 +66,11 @@ def make_test_values(o):
|
|
|
66
66
|
o.configuration_value = {o.configuration_name: o.configuration_name}
|
|
67
67
|
o.configuration_key = configuration_key(o.configuration_name)
|
|
68
68
|
o.key_name = f'test-key-name-{epoch_micro()}'
|
|
69
|
+
o.webapp_name = f'test-webapp-name-{epoch_micro()}'
|
|
70
|
+
o.webapp_url = f'https://test-webapp-{epoch_micro()}.com/'
|
|
71
|
+
o.webapp_key = f'#webapplication#{o.webapp_url}'
|
|
72
|
+
o.webpage_url = f'https://test-webpage-{epoch_micro()}.com/index.html'
|
|
73
|
+
o.webpage_key = f'#webpage#{o.webpage_url}'
|
|
69
74
|
return o
|
|
70
75
|
|
|
71
76
|
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
praetorian_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
praetorian_cli/main.py,sha256=AVrOCQgioLDKm-Y8-b3lLLdLtaO1WwOAzUfs0obe5Nw,1451
|
|
3
3
|
praetorian_cli/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
praetorian_cli/handlers/add.py,sha256=
|
|
4
|
+
praetorian_cli/handlers/add.py,sha256=1n0EZqlKsx2BX99jVs5-gwMKrnfJG4ajQajuWA6qMFI,13443
|
|
5
5
|
praetorian_cli/handlers/aegis.py,sha256=1259bNmoUOVhcs7GqI8TyTyCI_ZvKzEPvfUVvAHcTzU,3936
|
|
6
6
|
praetorian_cli/handlers/agent.py,sha256=8lmb_R1aWe9bhk-4NPOfXXxaE8E_U97DFNEvJ2mcteg,2291
|
|
7
7
|
praetorian_cli/handlers/chariot.py,sha256=HClwYdsgFKlLY68RhV65W1Y4g-JgbBDdI4PdP4s8MgI,611
|
|
8
|
-
praetorian_cli/handlers/cli_decorators.py,sha256=
|
|
8
|
+
praetorian_cli/handlers/cli_decorators.py,sha256=ZLPMZH9i-vNtTT0Y0qnYQsGw8B4lxIdEE1vBX9i75Wc,2884
|
|
9
9
|
praetorian_cli/handlers/configure.py,sha256=8ABvisb_a4WekVVZ5kEhT1m4Mn2xV5RsO_xO_7f6TkM,1317
|
|
10
|
-
praetorian_cli/handlers/delete.py,sha256=
|
|
10
|
+
praetorian_cli/handlers/delete.py,sha256=8K7yEnQQT88vbJG-ouX-aFq6FyjkWNGDMt97VYZGSZg,4433
|
|
11
11
|
praetorian_cli/handlers/enrich.py,sha256=KRvOAuW7mQdYg_k_XOTwtPmnf8rxOAtoL00Bb0e-N-s,485
|
|
12
|
-
praetorian_cli/handlers/get.py,sha256=
|
|
12
|
+
praetorian_cli/handlers/get.py,sha256=l9fFo_pSLXIx5Ax6bmSXsVlT7jazzrB235usJJbx82Y,9356
|
|
13
13
|
praetorian_cli/handlers/imports.py,sha256=u1TK4w3eCMD5ASPFare2NgTX1_m0_F1ELCmvV7OF-PI,1653
|
|
14
|
-
praetorian_cli/handlers/link.py,sha256=
|
|
15
|
-
praetorian_cli/handlers/list.py,sha256=
|
|
14
|
+
praetorian_cli/handlers/link.py,sha256=7JjHoawFZbcfropHVp8IX3Ekci6theAlcYSTPXnrFHQ,1776
|
|
15
|
+
praetorian_cli/handlers/list.py,sha256=bTPztFlyHHyCO5y5pDOw68CDxGN_Ps8DrhbStgSFgrc,13217
|
|
16
16
|
praetorian_cli/handlers/script.py,sha256=x_nWTlv0_9dLrQ4KulzKX11QwJHdtm6hWtZYgKzKcs4,2040
|
|
17
17
|
praetorian_cli/handlers/search.py,sha256=wPXiHrBx4NpNB8a79S9wE6TMArBjg5WsEFKzDoUAR-0,2633
|
|
18
18
|
praetorian_cli/handlers/ssh_utils.py,sha256=53Kke-iFH4sJoCcweiT8q4WVRlaA7SvR5CCqdGFxHps,5903
|
|
19
19
|
praetorian_cli/handlers/test.py,sha256=uhARoRolaJf6DMRNX-1aj8SDYe1wAvhYDOBYWH39sqo,932
|
|
20
|
-
praetorian_cli/handlers/unlink.py,sha256=
|
|
20
|
+
praetorian_cli/handlers/unlink.py,sha256=nUTGXZ7JBXwuHy2nzvL79sSO95Vyc0PftM6rm-9YWt8,1725
|
|
21
21
|
praetorian_cli/handlers/update.py,sha256=rgdOsFTaEivTdTUjUxPNEo13XR7uoIpBFRUqUdFVjaI,2846
|
|
22
22
|
praetorian_cli/handlers/utils.py,sha256=2WA_50l6bWo-00cpxsHtqZYF10pSVxwly94qjl_tutY,858
|
|
23
23
|
praetorian_cli/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -25,7 +25,7 @@ praetorian_cli/scripts/utils.py,sha256=lGCf4trEpsfECa9U42pDJ-f48EimlS-hG6AjnKjNt
|
|
|
25
25
|
praetorian_cli/scripts/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
26
|
praetorian_cli/scripts/commands/nmap-example.py,sha256=varKTkHKG4DAs9Ssf0c6SygP9GfuCG01aFxhfvixLM0,2727
|
|
27
27
|
praetorian_cli/sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
-
praetorian_cli/sdk/chariot.py,sha256=
|
|
28
|
+
praetorian_cli/sdk/chariot.py,sha256=Vs-_J6teHKgz0IEPcF4CQDLRvj5vKQB9spLvyQAoMZY,13364
|
|
29
29
|
praetorian_cli/sdk/keychain.py,sha256=yWm4ohMHXD1I_hEDuuBh-F9EO-tssRLMb2xreDa65k4,7305
|
|
30
30
|
praetorian_cli/sdk/mcp_server.py,sha256=8UoTotD4UVl-tp1gqMjkOVpO__KeGCvy7mIpKXVc8Rg,8750
|
|
31
31
|
praetorian_cli/sdk/entities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -51,16 +51,17 @@ praetorian_cli/sdk/entities/seeds.py,sha256=eYDFtfgzIX_o9c5wuPiz9I2xDfTz7k9PZPzL
|
|
|
51
51
|
praetorian_cli/sdk/entities/settings.py,sha256=F-pRCA6UvbdtnjHOLpEG2lN9ws8dcnBNcep-DFlXeTY,2750
|
|
52
52
|
praetorian_cli/sdk/entities/statistics.py,sha256=gtX-NN7r_RsNjDjlQ-zspmzG_0bzBqBFarCuM4NO-EA,7085
|
|
53
53
|
praetorian_cli/sdk/entities/webhook.py,sha256=7Bqt20GlJFbZTlmQwYTuUadsUQvydym6S4kGn9zYa50,6220
|
|
54
|
+
praetorian_cli/sdk/entities/webpage.py,sha256=FBS3HzuUJnQR9Blm_cBCd9efYAdc2wA2tUFY-cpLj4k,6787
|
|
54
55
|
praetorian_cli/sdk/model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
55
56
|
praetorian_cli/sdk/model/aegis.py,sha256=KNeynkCJtaR5bIhv3-VNirjxq4i-6JhCZGTb5rmxkQ4,5344
|
|
56
|
-
praetorian_cli/sdk/model/globals.py,sha256=
|
|
57
|
-
praetorian_cli/sdk/model/query.py,sha256=
|
|
57
|
+
praetorian_cli/sdk/model/globals.py,sha256=DxzOyYrVnu9FyvN_L69-1tAmLrFYSQ-nu1xiF8Mmg1I,2961
|
|
58
|
+
praetorian_cli/sdk/model/query.py,sha256=lqYC_RJRBCSQRPmElmBH6VXcXS8Z8AriLpG7DYUkFhI,6523
|
|
58
59
|
praetorian_cli/sdk/model/utils.py,sha256=G8cU7K2uG0-J0GanjcCrzDuUtKzDOucFWF6hGtfOsTM,781
|
|
59
60
|
praetorian_cli/sdk/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
60
61
|
praetorian_cli/sdk/test/pytest.ini,sha256=uM552lJOoxWRplXtonIeo2v7M2kHZmJKis7OgnvoLxg,213
|
|
61
62
|
praetorian_cli/sdk/test/test_account.py,sha256=OHYiiD8Ks2aeFq3Y9btgxG51qLweubPlYY17Ec7qPWM,886
|
|
62
63
|
praetorian_cli/sdk/test/test_agent.py,sha256=I7-NcmsNyw-nLqbdzEUbTSfUTKVIrYOlo0pM5PZqe7o,997
|
|
63
|
-
praetorian_cli/sdk/test/test_asset.py,sha256=
|
|
64
|
+
praetorian_cli/sdk/test/test_asset.py,sha256=jcnSJxgnLhRgJ_cjkrlLQTlAYg3wruMsEInA3X8mOrg,5114
|
|
64
65
|
praetorian_cli/sdk/test/test_attribute.py,sha256=AwD_w488rKhc1mxTBwSJcItmhcIYHJHX5DjgQYE3uPo,1169
|
|
65
66
|
praetorian_cli/sdk/test/test_capabilities.py,sha256=HoAu_WFjqVkwciPgrLp5z0-UbkuqKdqD46jb_8o1PbM,407
|
|
66
67
|
praetorian_cli/sdk/test/test_configuration.py,sha256=ysyWpt7iq_tNkdvLU8gULCuwbXVqt2HMXgZpWLnWccg,2174
|
|
@@ -76,9 +77,10 @@ praetorian_cli/sdk/test/test_search.py,sha256=SB9Tgo_N3CCpfvla898oLB9IZyGK9Dju1r
|
|
|
76
77
|
praetorian_cli/sdk/test/test_seed.py,sha256=WfnEPZwMXHFtt4jyVT-1JitIW1zTrl7rwlX8jXz22vY,1303
|
|
77
78
|
praetorian_cli/sdk/test/test_setting.py,sha256=hdPQj71rjSYxa-PODG2D-kJd8C9gkAg1jQXnqYU4P6A,1326
|
|
78
79
|
praetorian_cli/sdk/test/test_webhook.py,sha256=FQJY76QQ6Yg2iLCGpxgKiXGI8TtmB4zTpMIM2SpYKCc,2228
|
|
79
|
-
praetorian_cli/sdk/test/
|
|
80
|
+
praetorian_cli/sdk/test/test_webpage.py,sha256=jgKrsobD3ONibDIbbOT-yy7V_NmC5-LwEZmEYdYu0LI,1779
|
|
81
|
+
praetorian_cli/sdk/test/test_z_cli.py,sha256=2_cUOExQJOuTVJUdzBmZsQz9tgf6D8M_o2XEIW5-HQY,19234
|
|
80
82
|
praetorian_cli/sdk/test/ui_mocks.py,sha256=kiqAPxaM-_T0NQ-HgOZupNiUoJa5mE2CsyK2cXWiPws,4146
|
|
81
|
-
praetorian_cli/sdk/test/utils.py,sha256=
|
|
83
|
+
praetorian_cli/sdk/test/utils.py,sha256=svxMpzlaW4FRCij05cPgJFrTUEELVdt8G7SPKEdsgPo,3526
|
|
82
84
|
praetorian_cli/ui/__init__.py,sha256=wEgkrgIaoOguH1VVp2FndaGIxWmZ5CfAynXtNtZ6iTo,81
|
|
83
85
|
praetorian_cli/ui/aegis/__init__.py,sha256=1HE5aQ6C6Qruf1X-KmKvDw1M3lzaUbPZoz9QOp4Lofs,123
|
|
84
86
|
praetorian_cli/ui/aegis/constants.py,sha256=trr62uBwdBwm4LrFVms7_moThdtOPJi2uzCeIv4m3VU,578
|
|
@@ -91,9 +93,9 @@ praetorian_cli/ui/aegis/commands/job.py,sha256=BUcp47K-4PBIYq9nyUQTKhOakZwXM4fOf
|
|
|
91
93
|
praetorian_cli/ui/aegis/commands/list.py,sha256=puIiy0skYE59Q2hVSMsla1tKiYYAFTOaz8oPM17tCyI,352
|
|
92
94
|
praetorian_cli/ui/aegis/commands/set.py,sha256=ODa9u_6yW2dbKVQBuV9YeiS_ty_R_Xk_vPz7YrW3OWs,1101
|
|
93
95
|
praetorian_cli/ui/aegis/commands/ssh.py,sha256=KGsNlN0i-Cwp6gWyr-cjML9_L13oE7xFenysF2pC8Rc,3045
|
|
94
|
-
praetorian_cli-2.2.
|
|
95
|
-
praetorian_cli-2.2.
|
|
96
|
-
praetorian_cli-2.2.
|
|
97
|
-
praetorian_cli-2.2.
|
|
98
|
-
praetorian_cli-2.2.
|
|
99
|
-
praetorian_cli-2.2.
|
|
96
|
+
praetorian_cli-2.2.4.dist-info/licenses/LICENSE,sha256=Zv97QripiVALv-WokW_Elsiz9vtOfbtNt1aLZhhk67I,1067
|
|
97
|
+
praetorian_cli-2.2.4.dist-info/METADATA,sha256=Zfbe0aPsveflR-8qlRyKbgL6kC0_vx9RjQY1qZQmrtE,7751
|
|
98
|
+
praetorian_cli-2.2.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
99
|
+
praetorian_cli-2.2.4.dist-info/entry_points.txt,sha256=uJbDvZdkYaLiCh2DMvXPUGKFm2p5ZfzJCizUK3-PUEE,56
|
|
100
|
+
praetorian_cli-2.2.4.dist-info/top_level.txt,sha256=QbUdRPGEj_TyHO-E7AD5BxFfR8ore37i273jP4Gn43c,15
|
|
101
|
+
praetorian_cli-2.2.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|