sandbox-cli 0.2.47__tar.gz → 0.2.48__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.
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/PKG-INFO +8 -8
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/pyproject.toml +8 -8
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/cli/downloader.py +27 -2
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/cli/scanner/__init__.py +79 -40
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/utils/downloader/__init__.py +18 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/utils/scanner/__init__.py +4 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/utils/scanner/advanced.py +4 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/.gitignore +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/LICENSE +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/NOTICE +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/README.md +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/__main__.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/cli/__init__.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/cli/images.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/cli/reporter.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/cli/rules/__init__.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/cli/unpack.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/console.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/internal/__init__.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/internal/config.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/internal/helpers.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/models/__init__.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/models/detections.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/models/sandbox_arguments.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/utils/__init__.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/utils/compiler/__init__.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/utils/compiler/abc.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/utils/compiler/docker.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/utils/compiler/ssh.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/utils/extractors.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/utils/merge_dll_hooks.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/utils/scanner/rescan.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/utils/unpack/__init__.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/utils/unpack/plugins/__init__.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/utils/unpack/plugins/abc.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/utils/unpack/plugins/correlation.py +0 -0
- {sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/utils/unpack/plugins/sort_by_plugins.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sandbox-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.48
|
|
4
4
|
Summary: Command line tool for interaction with sandboxes
|
|
5
5
|
Project-URL: Homepage, https://github.com/Security-Experts-Community/sandbox-cli
|
|
6
6
|
Project-URL: Documentation, https://security-experts-community.github.io/sandbox-cli
|
|
@@ -11,16 +11,16 @@ License-Expression: MIT
|
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
License-File: NOTICE
|
|
13
13
|
Requires-Python: >=3.11
|
|
14
|
-
Requires-Dist: aiofiles>=
|
|
15
|
-
Requires-Dist: asyncssh>=2.
|
|
14
|
+
Requires-Dist: aiofiles>=25.1.0
|
|
15
|
+
Requires-Dist: asyncssh>=2.22.0
|
|
16
16
|
Requires-Dist: colorama>=0.4.6
|
|
17
|
-
Requires-Dist: cryptography>=46.0.
|
|
18
|
-
Requires-Dist: cyclopts>=4.2
|
|
17
|
+
Requires-Dist: cryptography>=46.0.5
|
|
18
|
+
Requires-Dist: cyclopts>=4.10.2
|
|
19
19
|
Requires-Dist: docker>=7.1.0
|
|
20
|
-
Requires-Dist: ptsandbox>=5.0.
|
|
20
|
+
Requires-Dist: ptsandbox>=5.0.10
|
|
21
21
|
Requires-Dist: pyzipper>=0.3.6
|
|
22
|
-
Requires-Dist: rich>=14.
|
|
23
|
-
Requires-Dist: zstandard>=0.
|
|
22
|
+
Requires-Dist: rich>=14.3.3
|
|
23
|
+
Requires-Dist: zstandard>=0.25.0
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
|
|
26
26
|

|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sandbox-cli"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.48"
|
|
4
4
|
description = "Command line tool for interaction with sandboxes"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -8,16 +8,16 @@ license = "MIT"
|
|
|
8
8
|
license-files = ["LICENSE", "NOTICE"]
|
|
9
9
|
authors = [{ name = "Alexey Kolesnikov" }]
|
|
10
10
|
dependencies = [
|
|
11
|
-
"aiofiles>=
|
|
12
|
-
"asyncssh>=2.
|
|
11
|
+
"aiofiles>=25.1.0",
|
|
12
|
+
"asyncssh>=2.22.0",
|
|
13
13
|
"colorama>=0.4.6",
|
|
14
|
-
"cryptography>=46.0.
|
|
15
|
-
"cyclopts>=4.2
|
|
14
|
+
"cryptography>=46.0.5",
|
|
15
|
+
"cyclopts>=4.10.2",
|
|
16
16
|
"docker>=7.1.0",
|
|
17
|
-
"ptsandbox>=5.0.
|
|
17
|
+
"ptsandbox>=5.0.10",
|
|
18
18
|
"pyzipper>=0.3.6",
|
|
19
|
-
"rich>=14.
|
|
20
|
-
"zstandard>=0.
|
|
19
|
+
"rich>=14.3.3",
|
|
20
|
+
"zstandard>=0.25.0",
|
|
21
21
|
]
|
|
22
22
|
|
|
23
23
|
[project.urls]
|
|
@@ -161,13 +161,31 @@ async def download_command(
|
|
|
161
161
|
group="Download options",
|
|
162
162
|
),
|
|
163
163
|
] = False,
|
|
164
|
+
amsi: Annotated[
|
|
165
|
+
bool,
|
|
166
|
+
Parameter(
|
|
167
|
+
name=["--amsi", "-am"],
|
|
168
|
+
help="Download amsi-dumps",
|
|
169
|
+
negative="",
|
|
170
|
+
group="Download options",
|
|
171
|
+
),
|
|
172
|
+
] = False,
|
|
173
|
+
dex: Annotated[
|
|
174
|
+
bool,
|
|
175
|
+
Parameter(
|
|
176
|
+
name=["--dex", "-dx"],
|
|
177
|
+
help="Download dex-dumps",
|
|
178
|
+
negative="",
|
|
179
|
+
group="Download options",
|
|
180
|
+
),
|
|
181
|
+
] = False,
|
|
164
182
|
query: Annotated[
|
|
165
183
|
str | None,
|
|
166
184
|
Parameter(
|
|
167
185
|
name=["--query", "-q"],
|
|
168
186
|
help="Query for searching tasks (leave empty for last tasks)",
|
|
169
187
|
group="Search",
|
|
170
|
-
)
|
|
188
|
+
),
|
|
171
189
|
] = None,
|
|
172
190
|
count: Annotated[
|
|
173
191
|
int,
|
|
@@ -175,7 +193,7 @@ async def download_command(
|
|
|
175
193
|
name=["--count", "-c"],
|
|
176
194
|
help="How many tasks find and download",
|
|
177
195
|
group="Search",
|
|
178
|
-
)
|
|
196
|
+
),
|
|
179
197
|
] = 20,
|
|
180
198
|
) -> None:
|
|
181
199
|
"""
|
|
@@ -206,6 +224,8 @@ async def download_command(
|
|
|
206
224
|
logs: bool = False,
|
|
207
225
|
procdumps: bool = False,
|
|
208
226
|
video: bool = False,
|
|
227
|
+
amsi: bool = False,
|
|
228
|
+
dex: bool = False,
|
|
209
229
|
progress: Progress | None = None,
|
|
210
230
|
) -> None:
|
|
211
231
|
await download(
|
|
@@ -219,6 +239,8 @@ async def download_command(
|
|
|
219
239
|
crashdumps=crashdumps,
|
|
220
240
|
procdumps=procdumps,
|
|
221
241
|
video=video,
|
|
242
|
+
amsi=amsi,
|
|
243
|
+
dex=dex,
|
|
222
244
|
logs=logs,
|
|
223
245
|
decompress=decompress,
|
|
224
246
|
progress=progress,
|
|
@@ -266,6 +288,8 @@ async def download_command(
|
|
|
266
288
|
crashdumps=crashdumps,
|
|
267
289
|
procdumps=procdumps,
|
|
268
290
|
video=video,
|
|
291
|
+
amsi=amsi,
|
|
292
|
+
dex=dex,
|
|
269
293
|
logs=logs,
|
|
270
294
|
decompress=decompress,
|
|
271
295
|
progress=progress,
|
|
@@ -304,6 +328,7 @@ async def download_command(
|
|
|
304
328
|
# clear last line
|
|
305
329
|
sys.stdout.write("\033[F\033[K")
|
|
306
330
|
|
|
331
|
+
|
|
307
332
|
def download_email(
|
|
308
333
|
emails: Annotated[
|
|
309
334
|
list[Path],
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import sys
|
|
2
|
+
from collections import deque
|
|
2
3
|
from collections.abc import Sequence
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
from typing import Annotated, Any
|
|
@@ -51,6 +52,40 @@ def rules_path_resolver(_: Any, tokens: Sequence[Token]) -> Path | None:
|
|
|
51
52
|
return path
|
|
52
53
|
|
|
53
54
|
|
|
55
|
+
def files_path_resolver(files: list[Path]) -> list[Path] | None:
|
|
56
|
+
files_queue = deque(files)
|
|
57
|
+
files_for_analysis: set[Path] = set()
|
|
58
|
+
|
|
59
|
+
is_ok = True
|
|
60
|
+
while files_queue:
|
|
61
|
+
file = files_queue.popleft()
|
|
62
|
+
|
|
63
|
+
# process wildcards
|
|
64
|
+
if sys.platform == "win32" and "*" in str(file):
|
|
65
|
+
for f in Path.cwd().glob(str(file)):
|
|
66
|
+
if f.is_file():
|
|
67
|
+
files_queue.append(f)
|
|
68
|
+
continue
|
|
69
|
+
|
|
70
|
+
file = file.expanduser().resolve()
|
|
71
|
+
|
|
72
|
+
if not file.exists():
|
|
73
|
+
console.error(f"{str(file)} doesn't exist")
|
|
74
|
+
is_ok = False
|
|
75
|
+
|
|
76
|
+
if file.is_dir():
|
|
77
|
+
files_for_analysis.update(file.glob("**/*"))
|
|
78
|
+
continue
|
|
79
|
+
|
|
80
|
+
files_for_analysis.add(file)
|
|
81
|
+
|
|
82
|
+
if len(files_for_analysis) == 0:
|
|
83
|
+
console.error("Nothing to scan")
|
|
84
|
+
is_ok = False
|
|
85
|
+
|
|
86
|
+
return list(files_for_analysis) if is_ok else None
|
|
87
|
+
|
|
88
|
+
|
|
54
89
|
@scanner.command(name="re-scan")
|
|
55
90
|
async def re_scan(
|
|
56
91
|
traces: Annotated[
|
|
@@ -322,6 +357,24 @@ async def scan(
|
|
|
322
357
|
group="Download options",
|
|
323
358
|
),
|
|
324
359
|
] = False,
|
|
360
|
+
amsi: Annotated[
|
|
361
|
+
bool,
|
|
362
|
+
Parameter(
|
|
363
|
+
name=["--amsi", "-am"],
|
|
364
|
+
help="Download amsi-dumps",
|
|
365
|
+
negative="",
|
|
366
|
+
group="Download options",
|
|
367
|
+
),
|
|
368
|
+
] = False,
|
|
369
|
+
dex: Annotated[
|
|
370
|
+
bool,
|
|
371
|
+
Parameter(
|
|
372
|
+
name=["--dex", "-dx"],
|
|
373
|
+
help="Download dex-dumps",
|
|
374
|
+
negative="",
|
|
375
|
+
group="Download options",
|
|
376
|
+
),
|
|
377
|
+
] = False,
|
|
325
378
|
decompress: Annotated[
|
|
326
379
|
bool,
|
|
327
380
|
Parameter(
|
|
@@ -356,27 +409,9 @@ async def scan(
|
|
|
356
409
|
if images is None:
|
|
357
410
|
images = {settings.default_image}
|
|
358
411
|
|
|
359
|
-
|
|
360
|
-
is_ok = True
|
|
361
|
-
files_for_analysis: list[Path] = []
|
|
362
|
-
for file in files:
|
|
363
|
-
file = file.expanduser().resolve()
|
|
364
|
-
|
|
365
|
-
if not file.exists():
|
|
366
|
-
console.error(f"{str(file)} doesn't exists")
|
|
367
|
-
is_ok = False
|
|
368
|
-
|
|
369
|
-
if file.is_dir():
|
|
370
|
-
files_for_analysis.extend(file.glob("**/*"))
|
|
371
|
-
continue
|
|
412
|
+
files_for_analysis = files_path_resolver(files)
|
|
372
413
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
if not is_ok:
|
|
376
|
-
sys.exit(1)
|
|
377
|
-
|
|
378
|
-
if len(files_for_analysis) == 0:
|
|
379
|
-
console.error("Nothing to scan")
|
|
414
|
+
if not files_for_analysis:
|
|
380
415
|
sys.exit(1)
|
|
381
416
|
|
|
382
417
|
await scan_internal(
|
|
@@ -401,6 +436,8 @@ async def scan(
|
|
|
401
436
|
procdumps=procdumps,
|
|
402
437
|
decompress=decompress,
|
|
403
438
|
open_browser=open_browser,
|
|
439
|
+
amsi=amsi,
|
|
440
|
+
dex=dex,
|
|
404
441
|
)
|
|
405
442
|
|
|
406
443
|
|
|
@@ -691,6 +728,24 @@ async def scan_new(
|
|
|
691
728
|
group="Download options",
|
|
692
729
|
),
|
|
693
730
|
] = False,
|
|
731
|
+
amsi: Annotated[
|
|
732
|
+
bool,
|
|
733
|
+
Parameter(
|
|
734
|
+
name=["--amsi", "-am"],
|
|
735
|
+
help="Download amsi-dumps",
|
|
736
|
+
negative="",
|
|
737
|
+
group="Download options",
|
|
738
|
+
),
|
|
739
|
+
] = False,
|
|
740
|
+
dex: Annotated[
|
|
741
|
+
bool,
|
|
742
|
+
Parameter(
|
|
743
|
+
name=["--dex", "-dx"],
|
|
744
|
+
help="Download dex-dumps",
|
|
745
|
+
negative="",
|
|
746
|
+
group="Download options",
|
|
747
|
+
),
|
|
748
|
+
] = False,
|
|
694
749
|
decompress: Annotated[
|
|
695
750
|
bool,
|
|
696
751
|
Parameter(
|
|
@@ -730,27 +785,9 @@ async def scan_new(
|
|
|
730
785
|
if extra_files is None:
|
|
731
786
|
extra_files = []
|
|
732
787
|
|
|
733
|
-
|
|
734
|
-
is_ok = True
|
|
735
|
-
files_for_analysis: list[Path] = []
|
|
736
|
-
for file in files:
|
|
737
|
-
file = file.expanduser().resolve()
|
|
788
|
+
files_for_analysis = files_path_resolver(files)
|
|
738
789
|
|
|
739
|
-
|
|
740
|
-
console.error(f"{str(file)} doesn't exists")
|
|
741
|
-
is_ok = False
|
|
742
|
-
|
|
743
|
-
if file.is_dir():
|
|
744
|
-
files_for_analysis.extend(file.glob("**/*"))
|
|
745
|
-
continue
|
|
746
|
-
|
|
747
|
-
files_for_analysis.append(file)
|
|
748
|
-
|
|
749
|
-
if not is_ok:
|
|
750
|
-
sys.exit(1)
|
|
751
|
-
|
|
752
|
-
if len(files_for_analysis) == 0:
|
|
753
|
-
console.error("Nothing to scan")
|
|
790
|
+
if not files_for_analysis:
|
|
754
791
|
sys.exit(1)
|
|
755
792
|
|
|
756
793
|
await scan_internal_advanced(
|
|
@@ -790,4 +827,6 @@ async def scan_new(
|
|
|
790
827
|
open_browser=open_browser,
|
|
791
828
|
preserve_filename=preserve_filename,
|
|
792
829
|
outbound_connections=outbound_connections,
|
|
830
|
+
amsi=amsi,
|
|
831
|
+
dex=dex,
|
|
793
832
|
)
|
|
@@ -25,8 +25,10 @@ semaphore = asyncio.Semaphore(value=16)
|
|
|
25
25
|
# At first glance, it's counter-intuitive, but ArtifactType can contain not only predefined fields in it, but also newly added.
|
|
26
26
|
# This usually happens when a new feature is released in the sandbox.
|
|
27
27
|
_KNOWN_ARTIFACT_TYPES = {
|
|
28
|
+
ArtifactType.AMSI,
|
|
28
29
|
ArtifactType.ARCHIVE,
|
|
29
30
|
ArtifactType.COMPRESSED,
|
|
31
|
+
ArtifactType.DEX_DUMP,
|
|
30
32
|
ArtifactType.EMAIL,
|
|
31
33
|
ArtifactType.FILE,
|
|
32
34
|
ArtifactType.PROCESS_DUMP,
|
|
@@ -122,6 +124,8 @@ async def download(
|
|
|
122
124
|
logs: bool = False,
|
|
123
125
|
procdumps: bool = False,
|
|
124
126
|
video: bool = False,
|
|
127
|
+
amsi: bool = False,
|
|
128
|
+
dex: bool = False,
|
|
125
129
|
progress: Progress | None = None,
|
|
126
130
|
idx: str | None = None,
|
|
127
131
|
image: str | None = None,
|
|
@@ -213,6 +217,20 @@ async def download(
|
|
|
213
217
|
decompress=decompress,
|
|
214
218
|
)
|
|
215
219
|
|
|
220
|
+
if artifact.type == ArtifactType.AMSI and (amsi or all):
|
|
221
|
+
add_task(
|
|
222
|
+
output / "amsi",
|
|
223
|
+
f"{artifact.file_info.sha256}.bin",
|
|
224
|
+
artifact.file_info.file_uri,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
if artifact.type == ArtifactType.DEX_DUMP and (dex or all):
|
|
228
|
+
add_task(
|
|
229
|
+
output / "dex",
|
|
230
|
+
f"{artifact.file_info.sha256}.bin",
|
|
231
|
+
artifact.file_info.file_uri,
|
|
232
|
+
)
|
|
233
|
+
|
|
216
234
|
if all and artifact.type not in _KNOWN_ARTIFACT_TYPES:
|
|
217
235
|
add_task(
|
|
218
236
|
output / "other",
|
|
@@ -153,6 +153,8 @@ async def scan_internal(
|
|
|
153
153
|
crashdumps: bool,
|
|
154
154
|
procdumps: bool,
|
|
155
155
|
decompress: bool,
|
|
156
|
+
amsi: bool,
|
|
157
|
+
dex: bool,
|
|
156
158
|
open_browser: bool,
|
|
157
159
|
) -> None:
|
|
158
160
|
key = get_key_by_name(key_name)
|
|
@@ -216,6 +218,8 @@ async def scan_internal(
|
|
|
216
218
|
files=download_files,
|
|
217
219
|
crashdumps=crashdumps,
|
|
218
220
|
procdumps=procdumps,
|
|
221
|
+
amsi=amsi,
|
|
222
|
+
dex=dex,
|
|
219
223
|
video=True,
|
|
220
224
|
logs=True,
|
|
221
225
|
decompress=decompress,
|
|
@@ -250,6 +250,8 @@ async def scan_internal_advanced(
|
|
|
250
250
|
crashdumps: bool,
|
|
251
251
|
procdumps: bool,
|
|
252
252
|
decompress: bool,
|
|
253
|
+
amsi: bool,
|
|
254
|
+
dex: bool,
|
|
253
255
|
open_browser: bool,
|
|
254
256
|
preserve_filename: bool,
|
|
255
257
|
outbound_connections: list[str] | None,
|
|
@@ -365,6 +367,8 @@ async def scan_internal_advanced(
|
|
|
365
367
|
procdumps=procdumps,
|
|
366
368
|
progress=progress,
|
|
367
369
|
video=True, # by default download video
|
|
370
|
+
amsi=amsi,
|
|
371
|
+
dex=dex,
|
|
368
372
|
idx=idx,
|
|
369
373
|
image=formatted_image,
|
|
370
374
|
link=formatted_link,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sandbox_cli-0.2.47 → sandbox_cli-0.2.48}/sandbox_cli/utils/unpack/plugins/sort_by_plugins.py
RENAMED
|
File without changes
|