splunk-soar-sdk 2.3.7__py3-none-any.whl → 3.1.0__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.
- soar_sdk/abstract.py +38 -41
- soar_sdk/action_results.py +41 -18
- soar_sdk/actions_manager.py +5 -7
- soar_sdk/apis/utils.py +3 -3
- soar_sdk/apis/vault.py +10 -10
- soar_sdk/app.py +58 -51
- soar_sdk/app_cli_runner.py +8 -8
- soar_sdk/app_client.py +10 -10
- soar_sdk/asset.py +45 -33
- soar_sdk/async_utils.py +2 -2
- soar_sdk/cli/init/cli.py +7 -9
- soar_sdk/cli/manifests/deserializers.py +15 -15
- soar_sdk/cli/manifests/processors.py +4 -10
- soar_sdk/cli/manifests/serializers.py +16 -8
- soar_sdk/cli/package/cli.py +6 -6
- soar_sdk/cli/package/utils.py +1 -1
- soar_sdk/code_renderers/action_renderer.py +35 -18
- soar_sdk/code_renderers/app_renderer.py +1 -2
- soar_sdk/code_renderers/asset_renderer.py +4 -5
- soar_sdk/code_renderers/renderer.py +2 -2
- soar_sdk/code_renderers/templates/pyproject.toml.jinja +1 -1
- soar_sdk/compat.py +6 -6
- soar_sdk/decorators/action.py +14 -15
- soar_sdk/decorators/make_request.py +4 -3
- soar_sdk/decorators/on_poll.py +5 -4
- soar_sdk/decorators/test_connectivity.py +2 -2
- soar_sdk/decorators/view_handler.py +11 -17
- soar_sdk/decorators/webhook.py +1 -2
- soar_sdk/exceptions.py +1 -4
- soar_sdk/field_utils.py +8 -0
- soar_sdk/input_spec.py +13 -17
- soar_sdk/logging.py +3 -3
- soar_sdk/meta/actions.py +6 -22
- soar_sdk/meta/app.py +10 -7
- soar_sdk/meta/dependencies.py +48 -42
- soar_sdk/meta/webhooks.py +12 -12
- soar_sdk/models/artifact.py +20 -23
- soar_sdk/models/container.py +30 -33
- soar_sdk/models/finding.py +54 -0
- soar_sdk/models/vault_attachment.py +6 -6
- soar_sdk/models/view.py +10 -13
- soar_sdk/params.py +57 -39
- soar_sdk/shims/phantom/action_result.py +4 -4
- soar_sdk/shims/phantom/base_connector.py +5 -5
- soar_sdk/shims/phantom/ph_ipc.py +3 -3
- soar_sdk/shims/phantom/vault.py +35 -34
- soar_sdk/types.py +3 -2
- soar_sdk/views/template_filters.py +4 -4
- soar_sdk/views/template_renderer.py +2 -2
- soar_sdk/views/view_parser.py +3 -4
- soar_sdk/webhooks/models.py +7 -6
- soar_sdk/webhooks/routing.py +4 -3
- {splunk_soar_sdk-2.3.7.dist-info → splunk_soar_sdk-3.1.0.dist-info}/METADATA +5 -6
- splunk_soar_sdk-3.1.0.dist-info/RECORD +105 -0
- splunk_soar_sdk-2.3.7.dist-info/RECORD +0 -103
- {splunk_soar_sdk-2.3.7.dist-info → splunk_soar_sdk-3.1.0.dist-info}/WHEEL +0 -0
- {splunk_soar_sdk-2.3.7.dist-info → splunk_soar_sdk-3.1.0.dist-info}/entry_points.txt +0 -0
- {splunk_soar_sdk-2.3.7.dist-info → splunk_soar_sdk-3.1.0.dist-info}/licenses/LICENSE +0 -0
soar_sdk/meta/app.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
from pydantic import BaseModel, Field,
|
|
2
|
-
from typing import Optional, Union
|
|
1
|
+
from pydantic import BaseModel, Field, field_validator
|
|
3
2
|
|
|
4
3
|
from soar_sdk.asset import AssetFieldSpecification
|
|
5
4
|
from soar_sdk.compat import PythonVersion
|
|
@@ -42,13 +41,14 @@ class AppMeta(BaseModel):
|
|
|
42
41
|
configuration: dict[str, AssetFieldSpecification] = Field(default_factory=dict)
|
|
43
42
|
actions: list[ActionMeta] = Field(default_factory=list)
|
|
44
43
|
|
|
45
|
-
pip39_dependencies: DependencyList = Field(default_factory=DependencyList)
|
|
46
44
|
pip313_dependencies: DependencyList = Field(default_factory=DependencyList)
|
|
45
|
+
pip314_dependencies: DependencyList = Field(default_factory=DependencyList)
|
|
47
46
|
|
|
48
|
-
webhook:
|
|
47
|
+
webhook: WebhookMeta | None = None
|
|
49
48
|
|
|
50
|
-
@
|
|
51
|
-
|
|
49
|
+
@field_validator("python_version", mode="before")
|
|
50
|
+
@classmethod
|
|
51
|
+
def convert_python_version_to_csv(cls, v: list | str) -> str:
|
|
52
52
|
"""Converts python_version to a comma-separated string if it's a list and validates versions."""
|
|
53
53
|
if isinstance(v, list):
|
|
54
54
|
# Validate each version in the list and convert to CSV
|
|
@@ -64,4 +64,7 @@ class AppMeta(BaseModel):
|
|
|
64
64
|
|
|
65
65
|
def to_json_manifest(self) -> dict:
|
|
66
66
|
"""Converts the AppMeta instance to a JSON-compatible dictionary."""
|
|
67
|
-
|
|
67
|
+
data = self.model_dump(exclude_none=True)
|
|
68
|
+
# In Pydantic v2 nested model_dump() overrides aren't automatically called
|
|
69
|
+
data["actions"] = [action.model_dump() for action in self.actions]
|
|
70
|
+
return data
|
soar_sdk/meta/dependencies.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import functools
|
|
1
2
|
import io
|
|
2
3
|
import os
|
|
3
4
|
from pathlib import Path
|
|
@@ -6,7 +7,7 @@ import tarfile
|
|
|
6
7
|
from tempfile import TemporaryDirectory
|
|
7
8
|
import build
|
|
8
9
|
|
|
9
|
-
from typing import
|
|
10
|
+
from typing import ClassVar
|
|
10
11
|
from collections.abc import Mapping, Sequence, AsyncGenerator
|
|
11
12
|
from pydantic import BaseModel, Field
|
|
12
13
|
|
|
@@ -61,22 +62,23 @@ DEPENDENCIES_TO_BUILD = {
|
|
|
61
62
|
class UvWheel(BaseModel):
|
|
62
63
|
"""Represents a Python wheel file with metadata and methods to fetch and validate it."""
|
|
63
64
|
|
|
64
|
-
url: str
|
|
65
|
+
url: str | None = None
|
|
66
|
+
filename: str | None = None
|
|
65
67
|
hash: str
|
|
66
|
-
size:
|
|
68
|
+
size: int | None = None
|
|
67
69
|
|
|
68
70
|
# The wheel file name is specified by PEP427. It's either a 5- or 6-tuple:
|
|
69
71
|
# {distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl
|
|
70
72
|
# We can parse this to determine which configurations it supports.
|
|
71
|
-
@
|
|
73
|
+
@functools.cached_property
|
|
72
74
|
def basename(self) -> str:
|
|
73
75
|
"""The base name of the wheel file."""
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
if self.filename:
|
|
77
|
+
return self.filename.removesuffix(".whl")
|
|
78
|
+
if self.url:
|
|
79
|
+
filename = self.url.split("/")[-1]
|
|
80
|
+
return filename.removesuffix(".whl")
|
|
81
|
+
raise ValueError("UvWheel must have either url or filename")
|
|
80
82
|
|
|
81
83
|
@property
|
|
82
84
|
def distribution(self) -> str:
|
|
@@ -89,7 +91,7 @@ class UvWheel(BaseModel):
|
|
|
89
91
|
return self.basename.split("-")[1]
|
|
90
92
|
|
|
91
93
|
@property
|
|
92
|
-
def build_tag(self) ->
|
|
94
|
+
def build_tag(self) -> str | None:
|
|
93
95
|
"""An optional build tag for the wheel."""
|
|
94
96
|
split = self.basename.split("-")
|
|
95
97
|
if len(split) == 6:
|
|
@@ -122,6 +124,10 @@ class UvWheel(BaseModel):
|
|
|
122
124
|
|
|
123
125
|
async def fetch(self) -> bytes:
|
|
124
126
|
"""Download the wheel file from the specified URL."""
|
|
127
|
+
if self.url is None:
|
|
128
|
+
raise ValueError(
|
|
129
|
+
f"Cannot fetch wheel {self.filename or 'unknown'}: no URL provided (local file reference?)"
|
|
130
|
+
)
|
|
125
131
|
async with httpx.AsyncClient() as client:
|
|
126
132
|
response = await client.get(self.url, timeout=10)
|
|
127
133
|
response.raise_for_status()
|
|
@@ -135,7 +141,7 @@ class UvSourceDistribution(BaseModel):
|
|
|
135
141
|
|
|
136
142
|
url: str
|
|
137
143
|
hash: str
|
|
138
|
-
size:
|
|
144
|
+
size: int | None = None
|
|
139
145
|
|
|
140
146
|
def validate_hash(self, sdist: bytes) -> None:
|
|
141
147
|
"""Validate the hash of the downloaded sdist against the expected hash."""
|
|
@@ -158,8 +164,8 @@ class UvSourceDistribution(BaseModel):
|
|
|
158
164
|
@staticmethod
|
|
159
165
|
def _builder_runner(
|
|
160
166
|
cmd: Sequence[str],
|
|
161
|
-
cwd:
|
|
162
|
-
extra_environ:
|
|
167
|
+
cwd: str | None = None,
|
|
168
|
+
extra_environ: Mapping[str, str] | None = None,
|
|
163
169
|
) -> None:
|
|
164
170
|
"""Run a command in a subprocess and return its exit code, stdout, and stderr."""
|
|
165
171
|
proc = subprocess.run( # noqa: S603
|
|
@@ -191,13 +197,13 @@ class DependencyWheel(BaseModel):
|
|
|
191
197
|
|
|
192
198
|
module: str
|
|
193
199
|
input_file: str = ""
|
|
194
|
-
input_file_aarch64:
|
|
200
|
+
input_file_aarch64: str | None = None
|
|
195
201
|
|
|
196
|
-
wheel:
|
|
197
|
-
wheel_aarch64:
|
|
198
|
-
sdist:
|
|
202
|
+
wheel: UvWheel | None = Field(exclude=True, default=None)
|
|
203
|
+
wheel_aarch64: UvWheel | None = Field(exclude=True, default=None)
|
|
204
|
+
sdist: UvSourceDistribution | None = Field(exclude=True, default=None)
|
|
199
205
|
|
|
200
|
-
async def collect_wheels(self) -> AsyncGenerator[tuple[str, bytes]
|
|
206
|
+
async def collect_wheels(self) -> AsyncGenerator[tuple[str, bytes]]:
|
|
201
207
|
"""Collect a list of wheel files to fetch for this dependency across all platforms."""
|
|
202
208
|
if self.wheel is None and self.sdist is not None:
|
|
203
209
|
logger.info(f"Building sdist for {self.input_file}")
|
|
@@ -229,7 +235,7 @@ class DependencyWheel(BaseModel):
|
|
|
229
235
|
|
|
230
236
|
def __hash__(self) -> int:
|
|
231
237
|
"""Compute a hash for the dependency wheel so we can dedupe wheel files in a later step."""
|
|
232
|
-
return hash((type(self), *tuple(self.
|
|
238
|
+
return hash((type(self), *tuple(self.model_dump().items())))
|
|
233
239
|
|
|
234
240
|
|
|
235
241
|
class DependencyList(BaseModel):
|
|
@@ -254,7 +260,7 @@ class UvPackage(BaseModel):
|
|
|
254
260
|
default_factory=dict, alias="optional-dependencies"
|
|
255
261
|
)
|
|
256
262
|
wheels: list[UvWheel] = []
|
|
257
|
-
sdist:
|
|
263
|
+
sdist: UvSourceDistribution | None = None
|
|
258
264
|
|
|
259
265
|
def _find_wheel(
|
|
260
266
|
self,
|
|
@@ -287,21 +293,21 @@ class UvPackage(BaseModel):
|
|
|
287
293
|
f"Could not find a suitable wheel for {self.name=}, {self.version=}, {abi_precedence=}, {python_precedence=}, {platform_precedence=}"
|
|
288
294
|
)
|
|
289
295
|
|
|
290
|
-
_manylinux_precedence = [
|
|
296
|
+
_manylinux_precedence: ClassVar[list[str]] = [
|
|
291
297
|
"_2_28", # glibc 2.28, latest stable version, supports Ubuntu 18.10+ and RHEL/Oracle 8+
|
|
292
298
|
"_2_17", # glibc 2.17, LTS-ish, supports Ubuntu 13.10+ and RHEL/Oracle 7+
|
|
293
299
|
"2014", # Synonym for _2_17
|
|
294
300
|
]
|
|
295
|
-
platform_precedence_x86_64 = [
|
|
301
|
+
platform_precedence_x86_64: ClassVar[list[str]] = [
|
|
296
302
|
*[f"manylinux{version}_x86_64" for version in _manylinux_precedence],
|
|
297
303
|
"any",
|
|
298
304
|
]
|
|
299
|
-
platform_precedence_aarch64 = [
|
|
305
|
+
platform_precedence_aarch64: ClassVar[list[str]] = [
|
|
300
306
|
*[f"manylinux{version}_aarch64" for version in _manylinux_precedence],
|
|
301
307
|
"any",
|
|
302
308
|
]
|
|
303
309
|
|
|
304
|
-
build_from_source_warning_triggered = False
|
|
310
|
+
build_from_source_warning_triggered: bool = False
|
|
305
311
|
|
|
306
312
|
def _resolve(
|
|
307
313
|
self, abi_precedence: list[str], python_precedence: list[str]
|
|
@@ -348,32 +354,32 @@ class UvPackage(BaseModel):
|
|
|
348
354
|
|
|
349
355
|
return wheel
|
|
350
356
|
|
|
351
|
-
def
|
|
352
|
-
"""Resolve the dependency wheel for Python 3.
|
|
357
|
+
def resolve_py313(self) -> DependencyWheel:
|
|
358
|
+
"""Resolve the dependency wheel for Python 3.13."""
|
|
353
359
|
return self._resolve(
|
|
354
360
|
abi_precedence=[
|
|
355
|
-
"
|
|
361
|
+
"cp313", # Python 3.13-specific ABI
|
|
356
362
|
"abi3", # Python 3 stable ABI
|
|
357
363
|
"none", # Source wheels -- no ABI
|
|
358
364
|
],
|
|
359
365
|
python_precedence=[
|
|
360
|
-
"
|
|
361
|
-
"
|
|
366
|
+
"cp313", # Binary wheel for Python 3.13
|
|
367
|
+
"pp313", # Source wheel for Python 3.13
|
|
362
368
|
"py3", # Source wheel for any Python 3.x
|
|
363
369
|
],
|
|
364
370
|
)
|
|
365
371
|
|
|
366
|
-
def
|
|
367
|
-
"""Resolve the dependency wheel for Python 3.
|
|
372
|
+
def resolve_py314(self) -> DependencyWheel:
|
|
373
|
+
"""Resolve the dependency wheel for Python 3.14."""
|
|
368
374
|
return self._resolve(
|
|
369
375
|
abi_precedence=[
|
|
370
|
-
"
|
|
376
|
+
"cp314", # Python 3.14-specific ABI
|
|
371
377
|
"abi3", # Python 3 stable ABI
|
|
372
378
|
"none", # Source wheels -- no ABI
|
|
373
379
|
],
|
|
374
380
|
python_precedence=[
|
|
375
|
-
"
|
|
376
|
-
"
|
|
381
|
+
"cp314", # Binary wheel for Python 3.14
|
|
382
|
+
"pp314", # Source wheel for Python 3.14
|
|
377
383
|
"py3", # Source wheel for any Python 3.x
|
|
378
384
|
],
|
|
379
385
|
)
|
|
@@ -446,21 +452,21 @@ class UvLock(BaseModel):
|
|
|
446
452
|
packages: list[UvPackage],
|
|
447
453
|
) -> tuple[DependencyList, DependencyList]:
|
|
448
454
|
"""Resolve the dependencies for the given packages."""
|
|
449
|
-
py39_wheels: list[DependencyWheel] = []
|
|
450
455
|
py313_wheels: list[DependencyWheel] = []
|
|
456
|
+
py314_wheels: list[DependencyWheel] = []
|
|
451
457
|
|
|
452
458
|
for package in packages:
|
|
453
|
-
wheel_39 = package.resolve_py39()
|
|
454
459
|
wheel_313 = package.resolve_py313()
|
|
460
|
+
wheel_314 = package.resolve_py314()
|
|
455
461
|
|
|
456
|
-
if
|
|
457
|
-
wheel_39.add_platform_prefix("shared")
|
|
462
|
+
if wheel_313 == wheel_314:
|
|
458
463
|
wheel_313.add_platform_prefix("shared")
|
|
464
|
+
wheel_314.add_platform_prefix("shared")
|
|
459
465
|
else:
|
|
460
|
-
wheel_39.add_platform_prefix("python39")
|
|
461
466
|
wheel_313.add_platform_prefix("python313")
|
|
467
|
+
wheel_314.add_platform_prefix("python314")
|
|
462
468
|
|
|
463
|
-
py39_wheels.append(wheel_39)
|
|
464
469
|
py313_wheels.append(wheel_313)
|
|
470
|
+
py314_wheels.append(wheel_314)
|
|
465
471
|
|
|
466
|
-
return DependencyList(wheel=
|
|
472
|
+
return DependencyList(wheel=py313_wheels), DependencyList(wheel=py314_wheels)
|
soar_sdk/meta/webhooks.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
from pydantic import BaseModel, Field,
|
|
1
|
+
from pydantic import BaseModel, Field, field_validator
|
|
2
2
|
from ipaddress import ip_network
|
|
3
|
-
from typing import Optional
|
|
4
3
|
|
|
5
4
|
|
|
6
5
|
class WebhookRouteMeta(BaseModel):
|
|
@@ -8,25 +7,26 @@ class WebhookRouteMeta(BaseModel):
|
|
|
8
7
|
|
|
9
8
|
url_pattern: str
|
|
10
9
|
allowed_methods: list[str] = Field(default_factory=lambda: ["GET", "POST"])
|
|
11
|
-
declaration_path:
|
|
12
|
-
declaration_lineno:
|
|
10
|
+
declaration_path: str | None = None
|
|
11
|
+
declaration_lineno: int | None = None
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
class WebhookMeta(BaseModel):
|
|
16
15
|
"""Metadata for a complex webhook definition which may contain multiple routes."""
|
|
17
16
|
|
|
18
|
-
handler:
|
|
17
|
+
handler: str | None
|
|
19
18
|
requires_auth: bool = True
|
|
20
19
|
allowed_headers: list[str] = Field(default_factory=list)
|
|
21
20
|
ip_allowlist: list[str] = Field(default_factory=lambda: ["0.0.0.0/0", "::/0"])
|
|
22
21
|
routes: list[WebhookRouteMeta] = Field(default_factory=list)
|
|
23
22
|
|
|
24
|
-
@
|
|
25
|
-
|
|
23
|
+
@field_validator("ip_allowlist")
|
|
24
|
+
@classmethod
|
|
25
|
+
def validate_ip_allowlist(cls, value: list[str]) -> list[str]:
|
|
26
26
|
"""Enforces all values of the 'ip_allowlist' field are valid IPv4 or IPv6 CIDRs."""
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
for item in value:
|
|
28
|
+
try:
|
|
29
|
+
ip_network(item)
|
|
30
|
+
except ValueError as e:
|
|
31
|
+
raise ValueError(f"{item} is not a valid IPv4 or IPv6 CIDR") from e
|
|
32
32
|
return value
|
soar_sdk/models/artifact.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from typing import
|
|
2
|
-
from pydantic import BaseModel
|
|
1
|
+
from typing import Any
|
|
2
|
+
from pydantic import BaseModel, ConfigDict
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class Artifact(BaseModel):
|
|
@@ -8,29 +8,26 @@ class Artifact(BaseModel):
|
|
|
8
8
|
This class allows users to create artifacts when yielding from an 'on poll' action.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
"""Pydantic config. Unknown keys are disallowed in this model."""
|
|
11
|
+
model_config = ConfigDict(extra="forbid")
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
container_id: Optional[int] = None
|
|
23
|
-
data: Optional[dict[str, Any]] = None
|
|
13
|
+
name: str | None = None
|
|
14
|
+
label: str | None = None
|
|
15
|
+
description: str | None = None
|
|
16
|
+
type: str | None = None
|
|
17
|
+
severity: str | None = None
|
|
18
|
+
source_data_identifier: str | None = None
|
|
19
|
+
container_id: int | None = None
|
|
20
|
+
data: dict[str, Any] | None = None
|
|
24
21
|
run_automation: bool = False
|
|
25
|
-
owner_id:
|
|
26
|
-
cef:
|
|
27
|
-
cef_types:
|
|
28
|
-
ingest_app_id:
|
|
29
|
-
tags:
|
|
30
|
-
start_time:
|
|
31
|
-
end_time:
|
|
32
|
-
kill_chain:
|
|
22
|
+
owner_id: int | str | None = None
|
|
23
|
+
cef: dict[str, Any] | None = None
|
|
24
|
+
cef_types: dict[str, list[str]] | None = None
|
|
25
|
+
ingest_app_id: int | str | None = None
|
|
26
|
+
tags: list[str] | str | None = None
|
|
27
|
+
start_time: str | None = None
|
|
28
|
+
end_time: str | None = None
|
|
29
|
+
kill_chain: str | None = None
|
|
33
30
|
|
|
34
31
|
def to_dict(self) -> dict[str, Any]:
|
|
35
32
|
"""Convert the artifact to a dictionary (needed for save_artifact)."""
|
|
36
|
-
return self.
|
|
33
|
+
return self.model_dump(exclude_none=True)
|
soar_sdk/models/container.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from pydantic import BaseModel
|
|
2
|
-
from typing import
|
|
1
|
+
from pydantic import BaseModel, ConfigDict
|
|
2
|
+
from typing import Any
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class Container(BaseModel):
|
|
@@ -8,40 +8,37 @@ class Container(BaseModel):
|
|
|
8
8
|
This class allows users to specify container properties when yielding from an on_poll function.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
"""Pydantic config."""
|
|
13
|
-
|
|
14
|
-
extra = "forbid"
|
|
11
|
+
model_config = ConfigDict(extra="forbid")
|
|
15
12
|
|
|
16
13
|
name: str
|
|
17
|
-
label:
|
|
18
|
-
description:
|
|
19
|
-
source_data_identifier:
|
|
20
|
-
severity:
|
|
21
|
-
status:
|
|
22
|
-
tags:
|
|
23
|
-
owner_id:
|
|
24
|
-
sensitivity:
|
|
25
|
-
artifacts:
|
|
26
|
-
asset_id:
|
|
27
|
-
close_time:
|
|
28
|
-
custom_fields:
|
|
29
|
-
data:
|
|
30
|
-
due_time:
|
|
31
|
-
end_time:
|
|
32
|
-
ingest_app_id:
|
|
33
|
-
kill_chain:
|
|
34
|
-
role_id:
|
|
14
|
+
label: str | None = None
|
|
15
|
+
description: str | None = None
|
|
16
|
+
source_data_identifier: str | None = None
|
|
17
|
+
severity: str | None = None
|
|
18
|
+
status: str | None = None
|
|
19
|
+
tags: list[str] | str | None = None
|
|
20
|
+
owner_id: int | str | None = None
|
|
21
|
+
sensitivity: str | None = None
|
|
22
|
+
artifacts: list[dict[str, Any]] | None = None
|
|
23
|
+
asset_id: int | None = None
|
|
24
|
+
close_time: str | None = None
|
|
25
|
+
custom_fields: dict[str, Any] | None = None
|
|
26
|
+
data: dict[str, Any] | None = None
|
|
27
|
+
due_time: str | None = None
|
|
28
|
+
end_time: str | None = None
|
|
29
|
+
ingest_app_id: int | None = None
|
|
30
|
+
kill_chain: str | None = None
|
|
31
|
+
role_id: int | str | None = None
|
|
35
32
|
run_automation: bool = False
|
|
36
|
-
start_time:
|
|
37
|
-
open_time:
|
|
38
|
-
tenant_id:
|
|
39
|
-
container_type:
|
|
40
|
-
template_id:
|
|
41
|
-
authorized_users:
|
|
42
|
-
artifact_count:
|
|
43
|
-
container_id:
|
|
33
|
+
start_time: str | None = None
|
|
34
|
+
open_time: str | None = None
|
|
35
|
+
tenant_id: int | str | None = None
|
|
36
|
+
container_type: str | None = None
|
|
37
|
+
template_id: int | None = None
|
|
38
|
+
authorized_users: list[int] | None = None
|
|
39
|
+
artifact_count: int | None = None
|
|
40
|
+
container_id: str | None = None
|
|
44
41
|
|
|
45
42
|
def to_dict(self) -> dict[str, Any]:
|
|
46
43
|
"""Convert the container to a dictionary (needed for save_container)."""
|
|
47
|
-
return self.
|
|
44
|
+
return self.model_dump(exclude_none=True)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class DrilldownSearch(BaseModel):
|
|
6
|
+
"""Represents a drilldown search in a finding."""
|
|
7
|
+
|
|
8
|
+
name: str
|
|
9
|
+
search: str
|
|
10
|
+
earliest: str
|
|
11
|
+
latest: str
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DrilldownDashboard(BaseModel):
|
|
15
|
+
"""Represents a drilldown dashboard in a finding."""
|
|
16
|
+
|
|
17
|
+
dashboard: str
|
|
18
|
+
name: str
|
|
19
|
+
tokens: list[str] | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Finding(BaseModel):
|
|
23
|
+
"""Represents a finding to be created during on_finding.
|
|
24
|
+
|
|
25
|
+
Findings are stored in ES and can be associated with SOAR containers/artifacts
|
|
26
|
+
for investigation workflow.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
class Config:
|
|
30
|
+
"""Pydantic config."""
|
|
31
|
+
|
|
32
|
+
extra = "forbid"
|
|
33
|
+
|
|
34
|
+
rule_title: str
|
|
35
|
+
rule_description: str
|
|
36
|
+
security_domain: str
|
|
37
|
+
risk_object: str
|
|
38
|
+
risk_object_type: str
|
|
39
|
+
risk_score: float
|
|
40
|
+
status: str | None = None
|
|
41
|
+
urgency: str | None = None
|
|
42
|
+
owner: str | None = None
|
|
43
|
+
disposition: str | None = None
|
|
44
|
+
drilldown_searches: list[DrilldownSearch] | None = None
|
|
45
|
+
drilldown_dashboards: list[DrilldownDashboard] | None = None
|
|
46
|
+
annotations: dict[str, list[str]] | None = None
|
|
47
|
+
risk_event_count: int | None = None
|
|
48
|
+
all_risk_objects: list[str] | None = None
|
|
49
|
+
source: list[str] | None = None
|
|
50
|
+
exclude_map_fields: list[str] | None = None
|
|
51
|
+
|
|
52
|
+
def to_dict(self) -> dict[str, Any]:
|
|
53
|
+
"""Convert the finding to a dictionary."""
|
|
54
|
+
return self.dict(exclude_none=True)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import IO
|
|
1
|
+
from typing import IO
|
|
2
2
|
from pydantic import BaseModel
|
|
3
3
|
|
|
4
4
|
|
|
@@ -11,15 +11,15 @@ class VaultAttachment(BaseModel):
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
id: int
|
|
14
|
-
created_via:
|
|
14
|
+
created_via: str | None = None
|
|
15
15
|
container: str
|
|
16
|
-
task:
|
|
16
|
+
task: str | None = None
|
|
17
17
|
create_time: str
|
|
18
18
|
name: str
|
|
19
19
|
user: str
|
|
20
20
|
vault_document: int
|
|
21
|
-
mime_type:
|
|
22
|
-
es_attachment_id:
|
|
21
|
+
mime_type: str | None = None
|
|
22
|
+
es_attachment_id: str | None = None
|
|
23
23
|
hash: str
|
|
24
24
|
vault_id: str
|
|
25
25
|
size: int
|
|
@@ -29,7 +29,7 @@ class VaultAttachment(BaseModel):
|
|
|
29
29
|
container_id: int
|
|
30
30
|
contains: list[str] = []
|
|
31
31
|
|
|
32
|
-
def open(self, mode: str = "r") ->
|
|
32
|
+
def open(self, mode: str = "r") -> IO[str] | IO[bytes]:
|
|
33
33
|
"""Open the vault attachment file.
|
|
34
34
|
|
|
35
35
|
Args:
|
soar_sdk/models/view.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
from pydantic import BaseModel
|
|
1
|
+
from typing import Any
|
|
2
|
+
from pydantic import BaseModel, ConfigDict
|
|
3
3
|
from soar_sdk.action_results import ActionResult
|
|
4
4
|
|
|
5
5
|
|
|
@@ -10,17 +10,14 @@ class ViewContext(BaseModel):
|
|
|
10
10
|
container: int
|
|
11
11
|
app: int
|
|
12
12
|
no_connection: bool
|
|
13
|
-
google_maps_key:
|
|
14
|
-
dark_title_logo:
|
|
15
|
-
title_logo:
|
|
16
|
-
app_name:
|
|
17
|
-
results:
|
|
18
|
-
html_content:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"""Pydantic config."""
|
|
22
|
-
|
|
23
|
-
extra = "allow"
|
|
13
|
+
google_maps_key: bool | str
|
|
14
|
+
dark_title_logo: str | None = None
|
|
15
|
+
title_logo: str | None = None
|
|
16
|
+
app_name: str | None = None
|
|
17
|
+
results: list[dict[str, Any]] | None = None
|
|
18
|
+
html_content: str | None = None
|
|
19
|
+
|
|
20
|
+
model_config = ConfigDict(extra="allow")
|
|
24
21
|
|
|
25
22
|
|
|
26
23
|
class ResultSummary(BaseModel):
|