sandbox-cli 0.2.49__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. {sandbox_cli-0.2.49 → sandbox_cli-0.3.0}/PKG-INFO +8 -9
  2. {sandbox_cli-0.2.49 → sandbox_cli-0.3.0}/pyproject.toml +44 -27
  3. sandbox_cli-0.3.0/sandbox_cli/__main__.py +41 -0
  4. sandbox_cli-0.3.0/sandbox_cli/cli/__init__.py +135 -0
  5. sandbox_cli-0.3.0/sandbox_cli/cli/_converters.py +131 -0
  6. sandbox_cli-0.3.0/sandbox_cli/cli/browser.py +47 -0
  7. sandbox_cli-0.3.0/sandbox_cli/cli/download/__init__.py +6 -0
  8. sandbox_cli-0.3.0/sandbox_cli/cli/download/artifacts.py +376 -0
  9. sandbox_cli-0.3.0/sandbox_cli/cli/download/email.py +66 -0
  10. sandbox_cli-0.3.0/sandbox_cli/cli/images.py +86 -0
  11. sandbox_cli-0.3.0/sandbox_cli/cli/report.py +202 -0
  12. sandbox_cli-0.3.0/sandbox_cli/cli/rules/__init__.py +17 -0
  13. sandbox_cli-0.3.0/sandbox_cli/cli/rules/compile.py +56 -0
  14. sandbox_cli-0.3.0/sandbox_cli/cli/rules/test.py +59 -0
  15. sandbox_cli-0.3.0/sandbox_cli/cli/scanner/__init__.py +19 -0
  16. sandbox_cli-0.3.0/sandbox_cli/cli/scanner/re_scan.py +116 -0
  17. sandbox_cli-0.3.0/sandbox_cli/cli/scanner/scan.py +272 -0
  18. sandbox_cli-0.3.0/sandbox_cli/cli/scanner/scan_new.py +422 -0
  19. {sandbox_cli-0.2.49 → sandbox_cli-0.3.0}/sandbox_cli/cli/unpack.py +2 -2
  20. sandbox_cli-0.3.0/sandbox_cli/console.py +74 -0
  21. sandbox_cli-0.3.0/sandbox_cli/core/browser.py +26 -0
  22. sandbox_cli-0.3.0/sandbox_cli/core/config.py +222 -0
  23. sandbox_cli-0.3.0/sandbox_cli/core/exceptions.py +53 -0
  24. sandbox_cli-0.3.0/sandbox_cli/core/progress.py +95 -0
  25. sandbox_cli-0.3.0/sandbox_cli/core/sandbox.py +75 -0
  26. sandbox_cli-0.3.0/sandbox_cli/core/scan.py +72 -0
  27. sandbox_cli-0.3.0/sandbox_cli/services/__init__.py +1 -0
  28. sandbox_cli-0.3.0/sandbox_cli/services/compiler/__init__.py +31 -0
  29. sandbox_cli-0.3.0/sandbox_cli/services/compiler/abc.py +98 -0
  30. sandbox_cli-0.3.0/sandbox_cli/services/compiler/docker.py +215 -0
  31. sandbox_cli-0.3.0/sandbox_cli/services/compiler/ssh.py +237 -0
  32. sandbox_cli-0.3.0/sandbox_cli/services/downloader.py +374 -0
  33. sandbox_cli-0.3.0/sandbox_cli/services/report.py +66 -0
  34. sandbox_cli-0.3.0/sandbox_cli/services/scanner/__init__.py +3 -0
  35. sandbox_cli-0.3.0/sandbox_cli/services/scanner/advanced.py +374 -0
  36. sandbox_cli-0.3.0/sandbox_cli/services/scanner/basic.py +220 -0
  37. sandbox_cli-0.3.0/sandbox_cli/services/scanner/compile.py +76 -0
  38. sandbox_cli-0.2.49/sandbox_cli/utils/merge_dll_hooks.py → sandbox_cli-0.3.0/sandbox_cli/services/scanner/hooks.py +17 -14
  39. sandbox_cli-0.3.0/sandbox_cli/services/scanner/images.py +72 -0
  40. sandbox_cli-0.3.0/sandbox_cli/services/scanner/rescan.py +248 -0
  41. sandbox_cli-0.3.0/sandbox_cli/services/scanner/tasks.py +59 -0
  42. sandbox_cli-0.3.0/sandbox_cli/services/scanner/uploads.py +84 -0
  43. sandbox_cli-0.3.0/sandbox_cli/services/scanner/utils.py +53 -0
  44. {sandbox_cli-0.2.49/sandbox_cli/utils → sandbox_cli-0.3.0/sandbox_cli/services}/unpack/__init__.py +44 -26
  45. sandbox_cli-0.3.0/sandbox_cli/services/unpack/plugins/abc.py +58 -0
  46. sandbox_cli-0.3.0/sandbox_cli/services/unpack/plugins/correlation.py +35 -0
  47. sandbox_cli-0.3.0/sandbox_cli/services/unpack/plugins/sort_by_plugins.py +11 -0
  48. sandbox_cli-0.2.49/sandbox_cli/__main__.py +0 -28
  49. sandbox_cli-0.2.49/sandbox_cli/cli/__init__.py +0 -52
  50. sandbox_cli-0.2.49/sandbox_cli/cli/downloader.py +0 -384
  51. sandbox_cli-0.2.49/sandbox_cli/cli/images.py +0 -45
  52. sandbox_cli-0.2.49/sandbox_cli/cli/reporter.py +0 -210
  53. sandbox_cli-0.2.49/sandbox_cli/cli/rules/__init__.py +0 -83
  54. sandbox_cli-0.2.49/sandbox_cli/cli/scanner/__init__.py +0 -842
  55. sandbox_cli-0.2.49/sandbox_cli/console.py +0 -23
  56. sandbox_cli-0.2.49/sandbox_cli/internal/config.py +0 -157
  57. sandbox_cli-0.2.49/sandbox_cli/internal/helpers.py +0 -89
  58. sandbox_cli-0.2.49/sandbox_cli/models/detections.py +0 -91
  59. sandbox_cli-0.2.49/sandbox_cli/models/sandbox_arguments.py +0 -22
  60. sandbox_cli-0.2.49/sandbox_cli/utils/__init__.py +0 -0
  61. sandbox_cli-0.2.49/sandbox_cli/utils/compiler/__init__.py +0 -60
  62. sandbox_cli-0.2.49/sandbox_cli/utils/compiler/abc.py +0 -51
  63. sandbox_cli-0.2.49/sandbox_cli/utils/compiler/docker.py +0 -136
  64. sandbox_cli-0.2.49/sandbox_cli/utils/compiler/ssh.py +0 -203
  65. sandbox_cli-0.2.49/sandbox_cli/utils/downloader/__init__.py +0 -244
  66. sandbox_cli-0.2.49/sandbox_cli/utils/extractors.py +0 -64
  67. sandbox_cli-0.2.49/sandbox_cli/utils/scanner/__init__.py +0 -291
  68. sandbox_cli-0.2.49/sandbox_cli/utils/scanner/advanced.py +0 -459
  69. sandbox_cli-0.2.49/sandbox_cli/utils/scanner/rescan.py +0 -279
  70. sandbox_cli-0.2.49/sandbox_cli/utils/unpack/plugins/__init__.py +0 -0
  71. sandbox_cli-0.2.49/sandbox_cli/utils/unpack/plugins/abc.py +0 -18
  72. sandbox_cli-0.2.49/sandbox_cli/utils/unpack/plugins/correlation.py +0 -50
  73. sandbox_cli-0.2.49/sandbox_cli/utils/unpack/plugins/sort_by_plugins.py +0 -28
  74. {sandbox_cli-0.2.49 → sandbox_cli-0.3.0}/.gitignore +0 -0
  75. {sandbox_cli-0.2.49 → sandbox_cli-0.3.0}/LICENSE +0 -0
  76. {sandbox_cli-0.2.49 → sandbox_cli-0.3.0}/NOTICE +0 -0
  77. {sandbox_cli-0.2.49 → sandbox_cli-0.3.0}/README.md +0 -0
  78. {sandbox_cli-0.2.49/sandbox_cli/internal → sandbox_cli-0.3.0/sandbox_cli/core}/__init__.py +0 -0
  79. {sandbox_cli-0.2.49/sandbox_cli/models → sandbox_cli-0.3.0/sandbox_cli/services/unpack/plugins}/__init__.py +0 -0
@@ -1,25 +1,24 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sandbox-cli
3
- Version: 0.2.49
3
+ Version: 0.3.0
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
7
7
  Project-URL: Repository, https://github.com/Security-Experts-Community/sandbox-cli
8
8
  Project-URL: Issues, https://github.com/Security-Experts-Community/sandbox-cli/issues
9
- Author: Alexey Kolesnikov
9
+ Author-email: Alexey Kolesnikov <delvinru@gmail.com>
10
10
  License-Expression: MIT
11
11
  License-File: LICENSE
12
12
  License-File: NOTICE
13
13
  Requires-Python: >=3.11
14
14
  Requires-Dist: aiofiles>=25.1.0
15
- Requires-Dist: asyncssh>=2.22.0
15
+ Requires-Dist: asyncssh>=2.24.0
16
16
  Requires-Dist: colorama>=0.4.6
17
- Requires-Dist: cryptography>=46.0.5
18
- Requires-Dist: cyclopts>=4.10.2
19
- Requires-Dist: docker>=7.1.0
20
- Requires-Dist: ptsandbox>=5.0.11
21
- Requires-Dist: pyzipper>=0.3.6
22
- Requires-Dist: rich>=14.3.3
17
+ Requires-Dist: cryptography>=49.0.0
18
+ Requires-Dist: cyclopts>=4.20.0
19
+ Requires-Dist: docker>=7.2.0
20
+ Requires-Dist: ptsandbox>=5.1.0
21
+ Requires-Dist: rich>=15.0.0
23
22
  Requires-Dist: zstandard>=0.25.0
24
23
  Description-Content-Type: text/markdown
25
24
 
@@ -1,22 +1,21 @@
1
1
  [project]
2
2
  name = "sandbox-cli"
3
- version = "0.2.49"
3
+ version = "0.3.0"
4
4
  description = "Command line tool for interaction with sandboxes"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
7
7
  license = "MIT"
8
8
  license-files = ["LICENSE", "NOTICE"]
9
- authors = [{ name = "Alexey Kolesnikov" }]
9
+ authors = [{ name = "Alexey Kolesnikov", email = "delvinru@gmail.com" }]
10
10
  dependencies = [
11
11
  "aiofiles>=25.1.0",
12
- "asyncssh>=2.22.0",
12
+ "asyncssh>=2.24.0",
13
13
  "colorama>=0.4.6",
14
- "cryptography>=46.0.5",
15
- "cyclopts>=4.10.2",
16
- "docker>=7.1.0",
17
- "ptsandbox>=5.0.11",
18
- "pyzipper>=0.3.6",
19
- "rich>=14.3.3",
14
+ "cryptography>=49.0.0",
15
+ "cyclopts>=4.20.0",
16
+ "docker>=7.2.0",
17
+ "ptsandbox>=5.1.0",
18
+ "rich>=15.0.0",
20
19
  "zstandard>=0.25.0",
21
20
  ]
22
21
 
@@ -28,13 +27,15 @@ Issues = "https://github.com/Security-Experts-Community/sandbox-cli/issues"
28
27
 
29
28
  [dependency-groups]
30
29
  dev = [
31
- "black>=24.10.0",
32
- "isort>=5.13.2",
33
- "mypy>=1.14.1",
34
- "types-aiofiles>=24.1.0.20241221",
35
- "types-docker>=7.1.0.20241229",
30
+ "black==26.5.1",
31
+ "isort==8.0.1",
32
+ "mypy==2.3.0",
33
+ "ruff==0.14.0",
34
+ "types-aiofiles==25.1.0.20260518",
35
+ "types-docker==7.2.0.20260728",
36
+ "types-colorama==0.4.15.20260508",
36
37
  ]
37
- docs = ["mkdocs>=1.6.1", "mkdocs-material>=9.6.7", "termynal>=0.13.0"]
38
+ docs = ["mkdocs==1.6.1", "mkdocs-material==9.7.7", "termynal==0.14.0"]
38
39
 
39
40
  [project.scripts]
40
41
  sandbox-cli = "sandbox_cli.__main__:main"
@@ -82,24 +83,40 @@ exclude = '''
82
83
 
83
84
  [[tool.mypy.overrides]]
84
85
  strict = true
85
- module = ["ptsandbox.*", "docker.*", "pyzipper.*"]
86
+ module = ["ptsandbox.*", "docker.*"]
86
87
  ignore_missing_imports = true
87
88
 
89
+ [tool.mypy]
90
+ python_version = "3.11"
91
+ warn_return_any = true
92
+ warn_unused_configs = true
93
+ disallow_untyped_defs = false
94
+ check_untyped_defs = true
95
+ warn_redundant_casts = true
96
+ warn_unused_ignores = true
97
+ no_implicit_optional = true
98
+ show_error_codes = true
99
+ namespace_packages = true
100
+ explicit_package_bases = true
101
+ exclude = ["\\.venv/", "\\.mypy_cache/", "build/", "dist/"]
102
+
88
103
  [tool.ruff]
89
104
  lint.select = [
90
- "E", # pycodestyle errors
91
- "W", # pycodestyle warnings
92
- "F", # pyflakes
93
- "I", # isort
94
- "B", # flake8-bugbear
95
- "C4", # flake8-comprehensions
96
- "UP", # pyupgrade
97
- "F401", # unused-imports
105
+ "E", # pycodestyle errors
106
+ "W", # pycodestyle warnings
107
+ "F", # pyflakes
108
+ "I", # isort
109
+ "B", # flake8-bugbear
110
+ "C4", # flake8-comprehensions
111
+ "UP", # pyupgrade
112
+ "SIM", # flake8-simplify
113
+ "RUF", # ruff-specific
98
114
  ]
99
115
  lint.ignore = [
100
- "E501", # line too long, handled by black
101
- "B008", # do not perform function calls in argument defaults
102
- "W191", # indentation contains tabs
116
+ "E501", # line too long, handled by black
117
+ "B008", # do not perform function calls in argument defaults
118
+ "W191", # indentation contains tabs
119
+ "SIM117", # nested with (readability)
103
120
  ]
104
121
  line-length = 120
105
122
  target-version = "py311"
@@ -0,0 +1,41 @@
1
+ import os
2
+ import sys
3
+ from http import HTTPStatus
4
+
5
+ import aiohttp
6
+ import aiohttp.client_exceptions
7
+
8
+ from sandbox_cli.cli import app
9
+ from sandbox_cli.console import console
10
+ from sandbox_cli.core.exceptions import SandboxCliError
11
+
12
+
13
+ def main() -> None:
14
+ if sys.platform == "win32":
15
+ import asyncio
16
+
17
+ asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
18
+
19
+ try:
20
+ app()
21
+ except aiohttp.client_exceptions.ClientResponseError as e:
22
+ # global handler for 401 error
23
+ if e.status == HTTPStatus.UNAUTHORIZED:
24
+ console.error(f"The specified token is not valid. {e}")
25
+ except SandboxCliError as e:
26
+ console.error(str(e))
27
+ except KeyboardInterrupt:
28
+ console.warning("Operation cancelled")
29
+ sys.exit(130)
30
+ except Exception as e:
31
+ # Show a concise error by default; full traceback only with DEBUG env var
32
+ # (see https://no-color.org/ style conventions for CLI tools).
33
+ if os.environ.get("DEBUG"):
34
+ console.print_exception()
35
+ else:
36
+ console.error(f"{type(e).__name__}: {e}")
37
+ console.info("Set DEBUG=1 for a full traceback.")
38
+
39
+
40
+ if __name__ == "__main__":
41
+ main()
@@ -0,0 +1,135 @@
1
+ from __future__ import annotations
2
+
3
+ import importlib.metadata
4
+ from typing import Annotated, Literal
5
+
6
+ from colorama import init
7
+ from cyclopts import App, Parameter
8
+
9
+ from sandbox_cli.console import console
10
+ from sandbox_cli.core.config import configpath, settings
11
+
12
+ init() # enable ANSI colors on Windows
13
+
14
+ __all__ = ["app"]
15
+
16
+
17
+ def get_version() -> str:
18
+ version = importlib.metadata.version("sandbox-cli")
19
+ return f"sandbox-cli {version}"
20
+
21
+
22
+ app = App(
23
+ name="sandbox-cli",
24
+ help="Work with sandbox like a pro"
25
+ + (
26
+ f"\n\nTo access other commands, specify at least one sandbox in the config at **{configpath}**"
27
+ if len(settings.sandbox_keys) == 0
28
+ else ""
29
+ ),
30
+ help_format="markdown",
31
+ version=get_version,
32
+ console=console,
33
+ )
34
+
35
+
36
+ def completion(
37
+ shell: Annotated[
38
+ Literal["bash", "zsh", "fish"],
39
+ Parameter(
40
+ help="Shell to generate completion for",
41
+ ),
42
+ ],
43
+ /,
44
+ *,
45
+ install: Annotated[
46
+ bool,
47
+ Parameter(
48
+ name=["--install", "-i"],
49
+ help="Install the completion script to the default location for the current shell and add it to the RC file",
50
+ negative="",
51
+ ),
52
+ ] = False,
53
+ ) -> None:
54
+ """
55
+ Generate or install shell completion.
56
+ """
57
+ if install:
58
+ path = app.install_completion(shell=shell)
59
+ console.info(f"Completion installed to {path}")
60
+ else:
61
+ console.print(app.generate_completion(shell=shell), end="")
62
+
63
+
64
+ app.command(name="completion")(completion)
65
+
66
+ # Lazy-loaded commands — modules are imported only when the command is invoked,
67
+ # keeping ``--help`` and ``--version`` fast by avoiding heavy imports
68
+ app.command(
69
+ "sandbox_cli.cli.unpack:unpack_logs",
70
+ name=["conv", "unpack"],
71
+ help="Convert sandbox logs into an analysis-friendly format.",
72
+ )
73
+ app.command(
74
+ "sandbox_cli.cli.report:generate_report",
75
+ name="report",
76
+ help="Generate short report from sandbox scans.",
77
+ )
78
+ app.command(
79
+ "sandbox_cli.cli.browser:open_browser",
80
+ name="browser",
81
+ help="Open sandbox link in the default browser.",
82
+ )
83
+
84
+ if len(settings.sandbox_keys) > 0:
85
+ scanner_app = App(name="scanner", help="Scan with the sandbox.", help_format="markdown", console=console)
86
+ scanner_app.command(
87
+ "sandbox_cli.cli.scanner.scan:scan",
88
+ name="scan",
89
+ help="Send files to scan with the sandbox.",
90
+ )
91
+ scanner_app.command(
92
+ "sandbox_cli.cli.scanner.scan_new:scan_new",
93
+ name="scan-new",
94
+ help="Send files to scan with the sandbox (advanced scan).",
95
+ )
96
+ scanner_app.command(
97
+ "sandbox_cli.cli.scanner.re_scan:re_scan",
98
+ name="re-scan",
99
+ help="Send traces to re-scan.",
100
+ )
101
+ app.command(scanner_app)
102
+
103
+ rules_app = App(
104
+ name="rules",
105
+ help="Working with raw sandbox rules.",
106
+ help_format="markdown",
107
+ console=console,
108
+ )
109
+ rules_app.command(
110
+ "sandbox_cli.cli.rules.compile:compile_rules",
111
+ name="compile",
112
+ help="Get compiled rules for working with third-party services.",
113
+ )
114
+ rules_app.command(
115
+ "sandbox_cli.cli.rules.test:test_rules",
116
+ name="test",
117
+ help="Testing written rules.",
118
+ )
119
+ app.command(rules_app)
120
+
121
+ app.command(
122
+ "sandbox_cli.cli.images:get_images",
123
+ name="images",
124
+ help="Get available images in the sandbox.",
125
+ )
126
+ app.command(
127
+ "sandbox_cli.cli.download:download_command",
128
+ name="download",
129
+ help="Download any artifact from the sandbox.",
130
+ )
131
+ app.command(
132
+ "sandbox_cli.cli.download:download_email",
133
+ name="email",
134
+ help="Upload an email and get its headers.",
135
+ )
@@ -0,0 +1,131 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ from collections import deque
5
+ from collections.abc import Sequence
6
+ from pathlib import Path
7
+ from typing import TYPE_CHECKING, Any
8
+
9
+ from cyclopts import validators
10
+
11
+ from sandbox_cli.console import console
12
+ from sandbox_cli.core.config import Platform, VMImage, parse_image, settings
13
+ from sandbox_cli.core.exceptions import ConfigError
14
+
15
+ if TYPE_CHECKING:
16
+ from cyclopts import Token
17
+
18
+ __all__ = [
19
+ "files_path_resolver",
20
+ "image_converter",
21
+ "out_dir_converter",
22
+ "out_dir_validator",
23
+ "rules_path_resolver",
24
+ "trace_converter",
25
+ "trace_validator",
26
+ ]
27
+
28
+
29
+ def image_converter(_: Any, tokens: Sequence[Token]) -> set[VMImage | str]:
30
+ """
31
+ Convert ``--image`` tokens into a set of ``VMImage`` or custom image ids.
32
+ """
33
+ return {parse_image(token.value) for token in tokens}
34
+
35
+
36
+ def out_dir_converter(_: Any, tokens: Sequence[Token]) -> Path:
37
+ """
38
+ Resolve ``--out`` value: expand ``~``, create the directory, return absolute path.
39
+ """
40
+ path = Path(tokens[0].value).expanduser()
41
+ path.mkdir(exist_ok=True, parents=True)
42
+ return path.resolve()
43
+
44
+
45
+ def out_dir_validator(_: Any, value: Any) -> None:
46
+ """
47
+ Ensure the output directory exists (also covers default values).
48
+ """
49
+ if isinstance(value, Path):
50
+ value.mkdir(exist_ok=True, parents=True)
51
+
52
+
53
+ def trace_converter(_: Any, tokens: Sequence[Token]) -> list[Path]:
54
+ """
55
+ Resolve trace paths: expand ``~`` and return absolute paths.
56
+ """
57
+ return [Path(t.value).expanduser().resolve() for t in tokens]
58
+
59
+
60
+ def trace_validator(_: Any, value: Any) -> None:
61
+ """
62
+ Validate that all trace paths exist.
63
+ """
64
+ path_validator = validators.Path(exists=True)
65
+ if isinstance(value, list):
66
+ for v in value:
67
+ path_validator(_, v)
68
+ elif isinstance(value, Path):
69
+ path_validator(_, value)
70
+
71
+
72
+ def rules_path_resolver(_: Any, tokens: Sequence[Token]) -> Path | None:
73
+ """
74
+ Resolve ``--rules`` tokens into a rules directory path.
75
+
76
+ Accepts either a platform alias (``windows``/``linux``) resolved
77
+ against ``settings.rules_path``, or an explicit path.
78
+ """
79
+ path: Path | None = None
80
+ for token in tokens:
81
+ if token.value in {Platform.LINUX, Platform.WINDOWS}:
82
+ if not settings.rules_path:
83
+ raise ConfigError("You can't use aliases without specifying the path in the config")
84
+
85
+ path = settings.rules_path / token.value
86
+ break
87
+ else:
88
+ # no platform alias — use the specified value as a path
89
+ path = Path(token.value)
90
+
91
+ return path
92
+
93
+
94
+ def files_path_resolver(files: list[Path]) -> list[Path] | None:
95
+ """
96
+ Expand a list of file/folder paths into a flat list of files.
97
+
98
+ Handles wildcards on Windows and recurses into directories. Returns
99
+ ``None`` when one or more inputs are invalid so the caller can bail out.
100
+ """
101
+ files_queue: deque[Path] = deque(files)
102
+ files_for_analysis: set[Path] = set()
103
+
104
+ is_ok = True
105
+ while files_queue:
106
+ file = files_queue.popleft()
107
+
108
+ # process wildcards
109
+ if sys.platform == "win32" and "*" in str(file):
110
+ for f in Path.cwd().glob(str(file)):
111
+ if f.is_file():
112
+ files_queue.append(f)
113
+ continue
114
+
115
+ file = file.expanduser().resolve()
116
+
117
+ if not file.exists():
118
+ console.error(f"{file!s} doesn't exist")
119
+ is_ok = False
120
+
121
+ if file.is_dir():
122
+ files_for_analysis.update(file.glob("**/*"))
123
+ continue
124
+
125
+ files_for_analysis.add(file)
126
+
127
+ if len(files_for_analysis) == 0:
128
+ console.error("Nothing to scan")
129
+ is_ok = False
130
+
131
+ return list(files_for_analysis) if is_ok else None
@@ -0,0 +1,47 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import Annotated
5
+
6
+ from cyclopts import Parameter
7
+
8
+ from sandbox_cli.console import console
9
+ from sandbox_cli.core.browser import open_link
10
+ from sandbox_cli.core.sandbox import (
11
+ format_link,
12
+ get_key_by_name,
13
+ )
14
+ from sandbox_cli.core.scan import load_report, load_scan_arguments
15
+
16
+ __all__ = ["open_browser"]
17
+
18
+
19
+ def open_browser(
20
+ path: Annotated[
21
+ Path,
22
+ Parameter(
23
+ help="Folder with sandbox report (report.json and scan_config.json)",
24
+ ),
25
+ ] = Path(),
26
+ ) -> None:
27
+ """
28
+ Open sandbox link in the default browser.
29
+ """
30
+
31
+ report_file = path / "report.json"
32
+ if not report_file.exists():
33
+ console.error(f"Can't find report.json: {path}")
34
+ return
35
+
36
+ scan_config_file = path / "scan_config.json"
37
+ if not scan_config_file.exists():
38
+ console.error(f"Can't find scan_config.json: {path}")
39
+ return
40
+
41
+ report = load_report(report_file)
42
+ scan_config = load_scan_arguments(scan_config_file)
43
+
44
+ key = get_key_by_name(scan_config.sandbox_key_name)
45
+ link = format_link(report, key=key)
46
+
47
+ open_link(link)
@@ -0,0 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+ from sandbox_cli.cli.download.artifacts import download_command
4
+ from sandbox_cli.cli.download.email import download_email
5
+
6
+ __all__ = ["download_command", "download_email"]