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.
Files changed (58) hide show
  1. soar_sdk/abstract.py +38 -41
  2. soar_sdk/action_results.py +41 -18
  3. soar_sdk/actions_manager.py +5 -7
  4. soar_sdk/apis/utils.py +3 -3
  5. soar_sdk/apis/vault.py +10 -10
  6. soar_sdk/app.py +58 -51
  7. soar_sdk/app_cli_runner.py +8 -8
  8. soar_sdk/app_client.py +10 -10
  9. soar_sdk/asset.py +45 -33
  10. soar_sdk/async_utils.py +2 -2
  11. soar_sdk/cli/init/cli.py +7 -9
  12. soar_sdk/cli/manifests/deserializers.py +15 -15
  13. soar_sdk/cli/manifests/processors.py +4 -10
  14. soar_sdk/cli/manifests/serializers.py +16 -8
  15. soar_sdk/cli/package/cli.py +6 -6
  16. soar_sdk/cli/package/utils.py +1 -1
  17. soar_sdk/code_renderers/action_renderer.py +35 -18
  18. soar_sdk/code_renderers/app_renderer.py +1 -2
  19. soar_sdk/code_renderers/asset_renderer.py +4 -5
  20. soar_sdk/code_renderers/renderer.py +2 -2
  21. soar_sdk/code_renderers/templates/pyproject.toml.jinja +1 -1
  22. soar_sdk/compat.py +6 -6
  23. soar_sdk/decorators/action.py +14 -15
  24. soar_sdk/decorators/make_request.py +4 -3
  25. soar_sdk/decorators/on_poll.py +5 -4
  26. soar_sdk/decorators/test_connectivity.py +2 -2
  27. soar_sdk/decorators/view_handler.py +11 -17
  28. soar_sdk/decorators/webhook.py +1 -2
  29. soar_sdk/exceptions.py +1 -4
  30. soar_sdk/field_utils.py +8 -0
  31. soar_sdk/input_spec.py +13 -17
  32. soar_sdk/logging.py +3 -3
  33. soar_sdk/meta/actions.py +6 -22
  34. soar_sdk/meta/app.py +10 -7
  35. soar_sdk/meta/dependencies.py +48 -42
  36. soar_sdk/meta/webhooks.py +12 -12
  37. soar_sdk/models/artifact.py +20 -23
  38. soar_sdk/models/container.py +30 -33
  39. soar_sdk/models/finding.py +54 -0
  40. soar_sdk/models/vault_attachment.py +6 -6
  41. soar_sdk/models/view.py +10 -13
  42. soar_sdk/params.py +57 -39
  43. soar_sdk/shims/phantom/action_result.py +4 -4
  44. soar_sdk/shims/phantom/base_connector.py +5 -5
  45. soar_sdk/shims/phantom/ph_ipc.py +3 -3
  46. soar_sdk/shims/phantom/vault.py +35 -34
  47. soar_sdk/types.py +3 -2
  48. soar_sdk/views/template_filters.py +4 -4
  49. soar_sdk/views/template_renderer.py +2 -2
  50. soar_sdk/views/view_parser.py +3 -4
  51. soar_sdk/webhooks/models.py +7 -6
  52. soar_sdk/webhooks/routing.py +4 -3
  53. {splunk_soar_sdk-2.3.7.dist-info → splunk_soar_sdk-3.1.0.dist-info}/METADATA +5 -6
  54. splunk_soar_sdk-3.1.0.dist-info/RECORD +105 -0
  55. splunk_soar_sdk-2.3.7.dist-info/RECORD +0 -103
  56. {splunk_soar_sdk-2.3.7.dist-info → splunk_soar_sdk-3.1.0.dist-info}/WHEEL +0 -0
  57. {splunk_soar_sdk-2.3.7.dist-info → splunk_soar_sdk-3.1.0.dist-info}/entry_points.txt +0 -0
  58. {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, validator
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: Optional[WebhookMeta]
47
+ webhook: WebhookMeta | None = None
49
48
 
50
- @validator("python_version", pre=True)
51
- def convert_python_version_to_csv(cls, v: Union[list, str]) -> str:
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
- return self.dict(exclude_none=True)
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
@@ -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 Optional
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: Optional[int] = None
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
- @property
73
+ @functools.cached_property
72
74
  def basename(self) -> str:
73
75
  """The base name of the wheel file."""
74
- remove_when_soar_newer_than(
75
- "6.4.0",
76
- "We should be able to adopt pydantic 2 now, and turn this into a cached property.",
77
- )
78
- filename = self.url.split("/")[-1]
79
- return filename.removesuffix(".whl")
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) -> Optional[str]:
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: Optional[int] = None
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: Optional[str] = None,
162
- extra_environ: Optional[Mapping[str, str]] = None,
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: Optional[str] = None
200
+ input_file_aarch64: str | None = None
195
201
 
196
- wheel: Optional[UvWheel] = Field(exclude=True, default=None)
197
- wheel_aarch64: Optional[UvWheel] = Field(exclude=True, default=None)
198
- sdist: Optional[UvSourceDistribution] = Field(exclude=True, default=None)
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], None]:
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.dict().items())))
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: Optional[UvSourceDistribution] = None
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 resolve_py39(self) -> DependencyWheel:
352
- """Resolve the dependency wheel for Python 3.9."""
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
- "cp39", # Python 3.9-specific ABI
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
- "cp39", # Binary wheel for Python 3.9
361
- "pp39", # Source wheel for Python 3.9
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 resolve_py313(self) -> DependencyWheel:
367
- """Resolve the dependency wheel for Python 3.13."""
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
- "cp313", # Python 3.13-specific ABI
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
- "cp313", # Binary wheel for Python 3.13
376
- "pp313", # Source wheel for Python 3.13
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 wheel_39 == wheel_313:
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=py39_wheels), DependencyList(wheel=py313_wheels)
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, validator
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: Optional[str] = None
12
- declaration_lineno: Optional[int] = None
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: Optional[str]
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
- @validator("ip_allowlist", each_item=True)
25
- def validate_ip_allowlist(cls, value: str) -> str:
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
- try:
28
- ip_network(value)
29
- except ValueError as e:
30
- raise ValueError(f"{value} is not a valid IPv4 or IPv6 CIDR") from e
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
@@ -1,5 +1,5 @@
1
- from typing import Optional, Any, Union
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
- class Config:
12
- """Pydantic config. Unknown keys are disallowed in this model."""
11
+ model_config = ConfigDict(extra="forbid")
13
12
 
14
- extra = "forbid"
15
-
16
- name: Optional[str] = None
17
- label: Optional[str] = None
18
- description: Optional[str] = None
19
- type: Optional[str] = None
20
- severity: Optional[str] = None
21
- source_data_identifier: Optional[str] = None
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: Optional[Union[int, str]] = None
26
- cef: Optional[dict[str, Any]] = None
27
- cef_types: Optional[dict[str, list[str]]] = None
28
- ingest_app_id: Optional[Union[int, str]] = None
29
- tags: Optional[Union[list[str], str]] = None
30
- start_time: Optional[str] = None
31
- end_time: Optional[str] = None
32
- kill_chain: Optional[str] = None
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.dict(exclude_none=True)
33
+ return self.model_dump(exclude_none=True)
@@ -1,5 +1,5 @@
1
- from pydantic import BaseModel
2
- from typing import Optional, Any, Union
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
- class Config:
12
- """Pydantic config."""
13
-
14
- extra = "forbid"
11
+ model_config = ConfigDict(extra="forbid")
15
12
 
16
13
  name: str
17
- label: Optional[str] = None
18
- description: Optional[str] = None
19
- source_data_identifier: Optional[str] = None
20
- severity: Optional[str] = None
21
- status: Optional[str] = None
22
- tags: Optional[Union[list[str], str]] = None
23
- owner_id: Optional[Union[int, str]] = None
24
- sensitivity: Optional[str] = None
25
- artifacts: Optional[list[dict[str, Any]]] = None
26
- asset_id: Optional[int] = None
27
- close_time: Optional[str] = None
28
- custom_fields: Optional[dict[str, Any]] = None
29
- data: Optional[dict[str, Any]] = None
30
- due_time: Optional[str] = None
31
- end_time: Optional[str] = None
32
- ingest_app_id: Optional[int] = None
33
- kill_chain: Optional[str] = None
34
- role_id: Optional[Union[int, str]] = None
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: Optional[str] = None
37
- open_time: Optional[str] = None
38
- tenant_id: Optional[Union[int, str]] = None
39
- container_type: Optional[str] = None
40
- template_id: Optional[int] = None
41
- authorized_users: Optional[list[int]] = None
42
- artifact_count: Optional[int] = None
43
- container_id: Optional[str] = None
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.dict(exclude_none=True)
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, Optional, Union
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: Optional[str] = None
14
+ created_via: str | None = None
15
15
  container: str
16
- task: Optional[str] = None
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: Optional[str] = None
22
- es_attachment_id: Optional[str] = None
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") -> Union[IO[str], IO[bytes]]:
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, Optional, Union
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: Union[bool, str]
14
- dark_title_logo: Optional[str] = None
15
- title_logo: Optional[str] = None
16
- app_name: Optional[str] = None
17
- results: Optional[list[dict[str, Any]]] = None
18
- html_content: Optional[str] = None
19
-
20
- class Config:
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):