sandbox-cli 0.2.33__tar.gz → 0.2.34__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.33 → sandbox_cli-0.2.34}/PKG-INFO +6 -6
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/pyproject.toml +6 -6
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/cli/downloader.py +73 -33
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/cli/images.py +2 -2
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/cli/scanner/__init__.py +6 -6
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/internal/helpers.py +5 -4
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/utils/downloader/__init__.py +63 -52
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/.gitignore +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/LICENSE +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/NOTICE +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/README.md +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/__main__.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/cli/__init__.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/cli/reporter.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/cli/rules/__init__.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/cli/unpack.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/console.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/internal/__init__.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/internal/config.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/models/__init__.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/models/detections.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/models/sandbox_arguments.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/utils/__init__.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/utils/compiler/__init__.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/utils/compiler/abc.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/utils/compiler/docker.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/utils/compiler/ssh.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/utils/extractors.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/utils/merge_dll_hooks.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/utils/scanner/__init__.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/utils/scanner/advanced.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/utils/scanner/rescan.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/utils/unpack/__init__.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/utils/unpack/plugins/__init__.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/utils/unpack/plugins/abc.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/sandbox_cli/utils/unpack/plugins/correlation.py +0 -0
- {sandbox_cli-0.2.33 → sandbox_cli-0.2.34}/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.34
|
|
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
|
|
@@ -12,13 +12,13 @@ License-File: LICENSE
|
|
|
12
12
|
License-File: NOTICE
|
|
13
13
|
Requires-Python: >=3.11
|
|
14
14
|
Requires-Dist: aiofiles>=24.1.0
|
|
15
|
-
Requires-Dist: asyncssh>=2.
|
|
16
|
-
Requires-Dist: cryptography>=
|
|
17
|
-
Requires-Dist: cyclopts>=3.
|
|
15
|
+
Requires-Dist: asyncssh>=2.21.0
|
|
16
|
+
Requires-Dist: cryptography>=45.0.4
|
|
17
|
+
Requires-Dist: cyclopts>=3.22.5
|
|
18
18
|
Requires-Dist: docker>=7.1.0
|
|
19
|
-
Requires-Dist: ptsandbox>=5.0.
|
|
19
|
+
Requires-Dist: ptsandbox>=5.0.6
|
|
20
20
|
Requires-Dist: pyzipper>=0.3.6
|
|
21
|
-
Requires-Dist: rich>=14.
|
|
21
|
+
Requires-Dist: rich>=14.1.0
|
|
22
22
|
Requires-Dist: zstandard>=0.23.0
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sandbox-cli"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.34"
|
|
4
4
|
description = "Command line tool for interaction with sandboxes"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -9,13 +9,13 @@ license-files = ["LICENSE", "NOTICE"]
|
|
|
9
9
|
authors = [{ name = "Alexey Kolesnikov" }]
|
|
10
10
|
dependencies = [
|
|
11
11
|
"aiofiles>=24.1.0",
|
|
12
|
-
"asyncssh>=2.
|
|
13
|
-
"cryptography>=
|
|
14
|
-
"cyclopts>=3.
|
|
12
|
+
"asyncssh>=2.21.0",
|
|
13
|
+
"cryptography>=45.0.4",
|
|
14
|
+
"cyclopts>=3.22.5",
|
|
15
15
|
"docker>=7.1.0",
|
|
16
|
-
"ptsandbox>=5.0.
|
|
16
|
+
"ptsandbox>=5.0.6",
|
|
17
17
|
"pyzipper>=0.3.6",
|
|
18
|
-
"rich>=14.
|
|
18
|
+
"rich>=14.1.0",
|
|
19
19
|
"zstandard>=0.23.0",
|
|
20
20
|
]
|
|
21
21
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import os
|
|
2
3
|
from collections.abc import Coroutine
|
|
3
4
|
from http import HTTPStatus
|
|
4
5
|
from pathlib import Path
|
|
@@ -9,10 +10,11 @@ from uuid import UUID
|
|
|
9
10
|
import aiofiles
|
|
10
11
|
import aiohttp
|
|
11
12
|
import aiohttp.client_exceptions
|
|
13
|
+
import orjson
|
|
12
14
|
from cyclopts import Parameter
|
|
13
15
|
from ptsandbox import Sandbox
|
|
14
16
|
from ptsandbox.models import Artifact, SandboxBaseTaskResponse
|
|
15
|
-
from rich.progress import Progress, SpinnerColumn, TextColumn, TimeElapsedColumn
|
|
17
|
+
from rich.progress import Progress, SpinnerColumn, TaskID, TextColumn, TimeElapsedColumn
|
|
16
18
|
|
|
17
19
|
from sandbox_cli.console import console
|
|
18
20
|
from sandbox_cli.internal.config import settings
|
|
@@ -51,20 +53,19 @@ async def download_command(
|
|
|
51
53
|
Parameter(
|
|
52
54
|
help="Links to tasks or task ids",
|
|
53
55
|
negative="",
|
|
54
|
-
required=True,
|
|
55
56
|
),
|
|
56
|
-
],
|
|
57
|
+
] = [],
|
|
57
58
|
/,
|
|
58
59
|
*,
|
|
59
60
|
key: Annotated[
|
|
60
61
|
str,
|
|
61
62
|
Parameter(
|
|
62
63
|
name=["--key", "-k"],
|
|
63
|
-
help=f"The key to access the sandbox **{'**,**'.join(x.name for x in settings.sandbox_keys)}**",
|
|
64
|
+
help=f"The key to access the sandbox **{'**,**'.join(x.name.get_secret_value() for x in settings.sandbox_keys)}**",
|
|
64
65
|
validator=validate_key,
|
|
65
66
|
group="Sandbox",
|
|
66
67
|
),
|
|
67
|
-
] = settings.sandbox_keys[0].name,
|
|
68
|
+
] = settings.sandbox_keys[0].name.get_secret_value(),
|
|
68
69
|
out_dir: Annotated[
|
|
69
70
|
Path,
|
|
70
71
|
Parameter(
|
|
@@ -127,7 +128,7 @@ async def download_command(
|
|
|
127
128
|
crashdumps: Annotated[
|
|
128
129
|
bool,
|
|
129
130
|
Parameter(
|
|
130
|
-
name=["--crashdumps", "-
|
|
131
|
+
name=["--crashdumps", "-C"],
|
|
131
132
|
help="Download crashdumps (maybe be more 1GB)",
|
|
132
133
|
negative="",
|
|
133
134
|
group="Download options",
|
|
@@ -160,11 +161,35 @@ async def download_command(
|
|
|
160
161
|
group="Download options",
|
|
161
162
|
),
|
|
162
163
|
] = False,
|
|
164
|
+
query: Annotated[
|
|
165
|
+
str | None,
|
|
166
|
+
Parameter(
|
|
167
|
+
name=["--query", "-q"],
|
|
168
|
+
help="Query for searching tasks (leave empty for last tasks)",
|
|
169
|
+
group="Search",
|
|
170
|
+
)
|
|
171
|
+
] = None,
|
|
172
|
+
count: Annotated[
|
|
173
|
+
int,
|
|
174
|
+
Parameter(
|
|
175
|
+
name=["--count", "-c"],
|
|
176
|
+
help="How many tasks find and download",
|
|
177
|
+
group="Search",
|
|
178
|
+
)
|
|
179
|
+
] = 20,
|
|
163
180
|
) -> None:
|
|
164
181
|
"""
|
|
165
182
|
Download any artifact from the sandbox.
|
|
166
183
|
"""
|
|
167
184
|
|
|
185
|
+
progress = Progress(
|
|
186
|
+
SpinnerColumn(),
|
|
187
|
+
TextColumn("{task.description}"),
|
|
188
|
+
TimeElapsedColumn(),
|
|
189
|
+
console=console,
|
|
190
|
+
transient=True,
|
|
191
|
+
)
|
|
192
|
+
|
|
168
193
|
async def worker(
|
|
169
194
|
report: SandboxBaseTaskResponse.LongReport,
|
|
170
195
|
sandbox: Sandbox,
|
|
@@ -197,39 +222,30 @@ async def download_command(
|
|
|
197
222
|
)
|
|
198
223
|
|
|
199
224
|
if unpack and out_dir.exists():
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
transient=True,
|
|
208
|
-
)
|
|
209
|
-
|
|
210
|
-
tasks: list[Coroutine[Any, Any, Artifact.EngineResult | None]] = []
|
|
225
|
+
# multiple images can be downloaded from the task
|
|
226
|
+
if not (out_dir / "events-correlated.log.gz").exists():
|
|
227
|
+
subfolders = [f.path for f in os.scandir(out_dir) if f.is_dir()]
|
|
228
|
+
for folder in subfolders:
|
|
229
|
+
Unpack(Path(folder)).run()
|
|
230
|
+
else:
|
|
231
|
+
Unpack(out_dir).run()
|
|
211
232
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if not sandbox or not task_id:
|
|
215
|
-
continue
|
|
233
|
+
async def create_task(sandbox: Sandbox, task_id: str) -> None:
|
|
234
|
+
progress_task_id = progress.add_task(description=rf"\[[green1]{task_id}[/]] fetching info", start=True)
|
|
216
235
|
|
|
217
236
|
try:
|
|
218
237
|
result = await sandbox.get_report(task_id=task_id)
|
|
219
238
|
except aiohttp.client_exceptions.ClientResponseError as e:
|
|
220
239
|
if e.status == HTTPStatus.NOT_FOUND:
|
|
221
|
-
console.warning(
|
|
222
|
-
|
|
223
|
-
"Read more about it in: https://security-experts-community.github.io/py-ptsandbox/usage/public-api/download-files/"
|
|
224
|
-
)
|
|
225
|
-
continue
|
|
240
|
+
console.warning(f"Not found information for {task_id}")
|
|
241
|
+
return
|
|
226
242
|
|
|
227
243
|
if (report := result.get_long_report()) is None:
|
|
228
|
-
console.warning(
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
244
|
+
console.warning(f"Not found information for {task_id}")
|
|
245
|
+
return
|
|
246
|
+
|
|
247
|
+
progress.stop_task(task_id=progress_task_id)
|
|
248
|
+
progress.update(task_id=progress_task_id, visible=False)
|
|
233
249
|
|
|
234
250
|
tasks.append(
|
|
235
251
|
worker(
|
|
@@ -249,6 +265,30 @@ async def download_command(
|
|
|
249
265
|
)
|
|
250
266
|
)
|
|
251
267
|
|
|
268
|
+
tasks: list[Coroutine[Any, Any, Artifact.EngineResult | None]] = []
|
|
269
|
+
|
|
270
|
+
if query is not None:
|
|
271
|
+
sandbox = Sandbox(get_key_by_name(key_name=key))
|
|
272
|
+
|
|
273
|
+
limit = 40 if count > 40 else count
|
|
274
|
+
viewed, next_cursor = 0, ""
|
|
275
|
+
|
|
276
|
+
while viewed <= count:
|
|
277
|
+
response = await sandbox.get_tasks(query=query, limit=limit, next_cursor=next_cursor)
|
|
278
|
+
|
|
279
|
+
with progress:
|
|
280
|
+
await asyncio.gather(*(create_task(sandbox, task.id) for task in response.tasks))
|
|
281
|
+
|
|
282
|
+
viewed += limit
|
|
283
|
+
next_cursor = response.next_cursor
|
|
284
|
+
|
|
285
|
+
for task in tasks_id:
|
|
286
|
+
sandbox, task_id = get_key_and_task(key, task)
|
|
287
|
+
if not sandbox or not task_id:
|
|
288
|
+
continue
|
|
289
|
+
|
|
290
|
+
await create_task(sandbox, task_id)
|
|
291
|
+
|
|
252
292
|
with progress:
|
|
253
293
|
await asyncio.gather(*tasks)
|
|
254
294
|
|
|
@@ -273,11 +313,11 @@ def download_email(
|
|
|
273
313
|
str,
|
|
274
314
|
Parameter(
|
|
275
315
|
name=["--key", "-k"],
|
|
276
|
-
help=f"The key to access the sandbox **{'**,**'.join(x.name for x in settings.sandbox_keys)}**",
|
|
316
|
+
help=f"The key to access the sandbox **{'**,**'.join(x.name.get_secret_value() for x in settings.sandbox_keys)}**",
|
|
277
317
|
validator=validate_key,
|
|
278
318
|
group="Sandbox",
|
|
279
319
|
),
|
|
280
|
-
] = settings.sandbox_keys[0].name,
|
|
320
|
+
] = settings.sandbox_keys[0].name.get_secret_value(),
|
|
281
321
|
) -> None:
|
|
282
322
|
"""
|
|
283
323
|
Upload an email and get its headers.
|
|
@@ -15,11 +15,11 @@ async def get_images(
|
|
|
15
15
|
str,
|
|
16
16
|
Parameter(
|
|
17
17
|
name=["--key", "-k"],
|
|
18
|
-
help=f"The key to access the sandbox **{'**,**'.join(x.name for x in settings.sandbox_keys)}**",
|
|
18
|
+
help=f"The key to access the sandbox **{'**,**'.join(x.name.get_secret_value() for x in settings.sandbox_keys)}**",
|
|
19
19
|
validator=validate_key,
|
|
20
20
|
group="Sandbox",
|
|
21
21
|
),
|
|
22
|
-
] = settings.sandbox_keys[0].name,
|
|
22
|
+
] = settings.sandbox_keys[0].name.get_secret_value(),
|
|
23
23
|
) -> None:
|
|
24
24
|
"""
|
|
25
25
|
Get available images in the sandbox.
|
|
@@ -80,11 +80,11 @@ async def re_scan(
|
|
|
80
80
|
str,
|
|
81
81
|
Parameter(
|
|
82
82
|
name=["--key", "-k"],
|
|
83
|
-
help=f"The key to access the sandbox **{'**,**'.join(x.name for x in settings.sandbox_keys)}**",
|
|
83
|
+
help=f"The key to access the sandbox **{'**,**'.join(x.name.get_secret_value() for x in settings.sandbox_keys)}**",
|
|
84
84
|
validator=validate_key,
|
|
85
85
|
group="Sandbox Options",
|
|
86
86
|
),
|
|
87
|
-
] = settings.sandbox_keys[0].name,
|
|
87
|
+
] = settings.sandbox_keys[0].name.get_secret_value(),
|
|
88
88
|
is_local: Annotated[
|
|
89
89
|
bool,
|
|
90
90
|
Parameter(
|
|
@@ -198,11 +198,11 @@ async def scan(
|
|
|
198
198
|
str,
|
|
199
199
|
Parameter(
|
|
200
200
|
name=["--key", "-k"],
|
|
201
|
-
help=f"The key to access the sandbox **{'**,**'.join(x.name for x in settings.sandbox_keys)}**",
|
|
201
|
+
help=f"The key to access the sandbox **{'**,**'.join(x.name.get_secret_value() for x in settings.sandbox_keys)}**",
|
|
202
202
|
validator=validate_key,
|
|
203
203
|
group="Sandbox Options",
|
|
204
204
|
),
|
|
205
|
-
] = settings.sandbox_keys[0].name,
|
|
205
|
+
] = settings.sandbox_keys[0].name.get_secret_value(),
|
|
206
206
|
is_local: Annotated[
|
|
207
207
|
bool,
|
|
208
208
|
Parameter(
|
|
@@ -444,11 +444,11 @@ async def scan_new(
|
|
|
444
444
|
str,
|
|
445
445
|
Parameter(
|
|
446
446
|
name=["--key", "-k"],
|
|
447
|
-
help=f"The key to access the sandbox **{'**,**'.join(x.name for x in settings.sandbox_keys)}**",
|
|
447
|
+
help=f"The key to access the sandbox **{'**,**'.join(x.name.get_secret_value() for x in settings.sandbox_keys)}**",
|
|
448
448
|
validator=validate_key,
|
|
449
449
|
group="Sandbox Options",
|
|
450
450
|
),
|
|
451
|
-
] = settings.sandbox_keys[0].name,
|
|
451
|
+
] = settings.sandbox_keys[0].name.get_secret_value(),
|
|
452
452
|
is_local: Annotated[
|
|
453
453
|
bool,
|
|
454
454
|
Parameter(
|
|
@@ -13,7 +13,7 @@ from sandbox_cli.models.sandbox_arguments import SandboxArguments
|
|
|
13
13
|
|
|
14
14
|
def get_key_by_name(key_name: str) -> SandboxKey:
|
|
15
15
|
for sandbox_key in settings.sandbox_keys:
|
|
16
|
-
if sandbox_key.name == key_name:
|
|
16
|
+
if sandbox_key.name.get_secret_value() == key_name:
|
|
17
17
|
return sandbox_key
|
|
18
18
|
raise KeyError()
|
|
19
19
|
|
|
@@ -30,8 +30,9 @@ def validate_key(_: Any, value: Any) -> None:
|
|
|
30
30
|
try:
|
|
31
31
|
get_key_by_name(value)
|
|
32
32
|
except KeyError:
|
|
33
|
+
available = "', '".join(x.name.get_secret_value() for x in settings.sandbox_keys)
|
|
33
34
|
console.error(
|
|
34
|
-
f'Key "{value}" doesn\'t
|
|
35
|
+
f'Key "{value}" doesn\'t exist in config. Available keys: "{available}"'
|
|
35
36
|
)
|
|
36
37
|
sys.exit(1)
|
|
37
38
|
|
|
@@ -78,11 +79,11 @@ def save_scan_arguments(out_dir: Path, scan_args: SandboxArguments) -> None:
|
|
|
78
79
|
|
|
79
80
|
|
|
80
81
|
def open_link(link: str) -> None:
|
|
81
|
-
if settings.browser
|
|
82
|
+
if settings.browser is not None:
|
|
82
83
|
webbrowser.register("new_default_browser", None, webbrowser.GenericBrowser([settings.browser.path, *settings.browser.args]), preferred=True)
|
|
83
84
|
if not webbrowser.open(link):
|
|
84
85
|
console.error("Can't open link in the specified browser. Please check browser path and args.")
|
|
85
86
|
return
|
|
86
|
-
|
|
87
|
+
|
|
87
88
|
if not webbrowser.open_new_tab(link):
|
|
88
89
|
console.error("Can't open link in the default browser. Try adding path and args for your browser to the config file.")
|
|
@@ -18,8 +18,9 @@ from rich.markup import escape
|
|
|
18
18
|
from rich.progress import Progress, TaskID
|
|
19
19
|
|
|
20
20
|
from sandbox_cli.console import console
|
|
21
|
+
from sandbox_cli.internal.config import settings
|
|
21
22
|
|
|
22
|
-
semaphore = asyncio.Semaphore(value=
|
|
23
|
+
semaphore = asyncio.Semaphore(value=16)
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
async def _save_artifact(
|
|
@@ -28,6 +29,7 @@ async def _save_artifact(
|
|
|
28
29
|
out_dir: Path,
|
|
29
30
|
path: Path,
|
|
30
31
|
file_uri: str,
|
|
32
|
+
overwrite: bool = False,
|
|
31
33
|
decompress: bool = False,
|
|
32
34
|
progress: Progress | None = None,
|
|
33
35
|
idx: str | None = None,
|
|
@@ -40,12 +42,14 @@ async def _save_artifact(
|
|
|
40
42
|
|
|
41
43
|
# sanitize path
|
|
42
44
|
path = out_dir / Path(str(path).replace(" ", "_"))
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
if not overwrite:
|
|
46
|
+
n = 1
|
|
47
|
+
while path.exists():
|
|
48
|
+
if not path.with_name(path.name + f"_{n}").exists():
|
|
49
|
+
path = path.with_name(path.name + f"_{n}")
|
|
50
|
+
break
|
|
51
|
+
n += 1
|
|
52
|
+
|
|
49
53
|
path.parent.mkdir(exist_ok=True, parents=True)
|
|
50
54
|
path.touch()
|
|
51
55
|
task_id: TaskID = None # type: ignore
|
|
@@ -107,7 +111,7 @@ async def download(
|
|
|
107
111
|
) -> None:
|
|
108
112
|
tasks: list[Coroutine[Any, Any, None]] = []
|
|
109
113
|
|
|
110
|
-
def add_task(out_dir: Path, path: Path, file_uri: str, decompress: bool = False) -> None:
|
|
114
|
+
def add_task(out_dir: Path, path: Path, file_uri: str, decompress: bool = False, overwrite: bool = False) -> None:
|
|
111
115
|
tasks.append(
|
|
112
116
|
_save_artifact(
|
|
113
117
|
scan_id=report.scan_id,
|
|
@@ -120,66 +124,73 @@ async def download(
|
|
|
120
124
|
image=image,
|
|
121
125
|
link=link,
|
|
122
126
|
decompress=decompress,
|
|
127
|
+
overwrite=overwrite,
|
|
123
128
|
)
|
|
124
129
|
)
|
|
125
130
|
|
|
126
131
|
for artifact in report.artifacts:
|
|
127
|
-
sandbox_result
|
|
128
|
-
|
|
129
|
-
|
|
132
|
+
for sandbox_result in artifact.get_sandbox_results():
|
|
133
|
+
if sandbox_result is None:
|
|
134
|
+
continue
|
|
130
135
|
|
|
131
|
-
|
|
132
|
-
|
|
136
|
+
if sandbox_result.details is None:
|
|
137
|
+
continue
|
|
133
138
|
|
|
134
|
-
|
|
135
|
-
|
|
139
|
+
if sandbox_result.details.sandbox is None:
|
|
140
|
+
continue
|
|
136
141
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
LogType.EVENT_NORMALIZED,
|
|
142
|
-
LogType.EVENT_RAW,
|
|
143
|
-
LogType.NETWORK,
|
|
144
|
-
}:
|
|
145
|
-
add_task(out_dir, log.file_name, log.file_uri)
|
|
142
|
+
if sandbox_result.details.sandbox.image and (image_id := sandbox_result.details.sandbox.image.image_id):
|
|
143
|
+
output = out_dir / image_id
|
|
144
|
+
else:
|
|
145
|
+
output = out_dir
|
|
146
146
|
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
for log in sandbox_result.details.sandbox.logs:
|
|
148
|
+
# download default logs by default
|
|
149
|
+
if (all or logs) and log.type in {
|
|
150
|
+
LogType.EVENT_CORRELATED,
|
|
151
|
+
LogType.EVENT_NORMALIZED,
|
|
152
|
+
LogType.EVENT_RAW,
|
|
153
|
+
LogType.NETWORK,
|
|
154
|
+
}:
|
|
155
|
+
add_task(output, log.file_name, log.file_uri, overwrite=True)
|
|
149
156
|
|
|
150
|
-
|
|
151
|
-
|
|
157
|
+
if (all or video) and log.type == LogType.SCREENSHOT:
|
|
158
|
+
add_task(output, log.file_name, log.file_uri, overwrite=True)
|
|
152
159
|
|
|
153
|
-
|
|
154
|
-
|
|
160
|
+
if (all or crashdumps) and log.file_name in {"crashdump.bin", "crashdump.metadata"}:
|
|
161
|
+
add_task(output / "crashdumps", log.file_name, log.file_uri)
|
|
155
162
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
continue
|
|
163
|
+
if (all or debug) and log.type in {LogType.DEBUG, LogType.GRAPH}:
|
|
164
|
+
add_task(output / "debug", log.file_name, log.file_uri)
|
|
159
165
|
|
|
160
|
-
if
|
|
161
|
-
|
|
166
|
+
if artifacts or files or procdumps or all:
|
|
167
|
+
if not sandbox_result.details:
|
|
168
|
+
continue
|
|
162
169
|
|
|
163
|
-
|
|
164
|
-
|
|
170
|
+
if not sandbox_result.details.sandbox:
|
|
171
|
+
continue
|
|
165
172
|
|
|
166
|
-
|
|
167
|
-
if not artifact.file_info:
|
|
173
|
+
if not sandbox_result.details.sandbox.artifacts:
|
|
168
174
|
continue
|
|
169
175
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
176
|
+
for artifact in sandbox_result.details.sandbox.artifacts:
|
|
177
|
+
if not artifact.file_info:
|
|
178
|
+
continue
|
|
179
|
+
|
|
180
|
+
if artifact.type == ArtifactType.FILE and (files or artifacts or all):
|
|
181
|
+
add_task(
|
|
182
|
+
output / "artifacts",
|
|
183
|
+
artifact.file_info.file_path.removeprefix("/"),
|
|
184
|
+
artifact.file_info.file_uri,
|
|
185
|
+
)
|
|
186
|
+
if artifact.type == ArtifactType.PROCESS_DUMP and (procdumps or artifacts or all):
|
|
187
|
+
add_task(
|
|
188
|
+
output / "process_dump",
|
|
189
|
+
artifact.file_info.details.process_dump.process_name.removeprefix("/"), # type: ignore
|
|
190
|
+
artifact.file_info.file_uri,
|
|
191
|
+
decompress=decompress,
|
|
192
|
+
)
|
|
193
|
+
|
|
183
194
|
if not tasks:
|
|
184
195
|
console.info(f"Nothing to download from {report.scan_id}")
|
|
185
196
|
|
|
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.33 → sandbox_cli-0.2.34}/sandbox_cli/utils/unpack/plugins/sort_by_plugins.py
RENAMED
|
File without changes
|