splunk-soar-sdk 3.1.0__py3-none-any.whl → 3.2.1__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.
@@ -154,6 +154,13 @@ class ActionsManager(BaseConnector):
154
154
  # For non-broker just proceed as we did before
155
155
  return super().get_app_dir()
156
156
 
157
+ def send_finding_to_es(self, finding: dict[str, Any]) -> str:
158
+ """Send finding to ES.
159
+
160
+ Returns finding_id: ID for the finding in ES.
161
+ """
162
+ return ""
163
+
157
164
  @classmethod
158
165
  def get_soar_base_url(cls) -> str:
159
166
  """Get the base URL of the Splunk SOAR instance this app is running on."""
soar_sdk/app.py CHANGED
@@ -38,6 +38,7 @@ from soar_sdk.decorators import (
38
38
  ActionDecorator,
39
39
  ViewHandlerDecorator,
40
40
  OnPollDecorator,
41
+ OnESPollDecorator,
41
42
  WebhookDecorator,
42
43
  MakeRequestDecorator,
43
44
  )
@@ -495,6 +496,31 @@ class App:
495
496
  """
496
497
  return OnPollDecorator(self)
497
498
 
499
+ def on_es_poll(self) -> OnESPollDecorator:
500
+ """Decorator for the on_es_poll action.
501
+
502
+ The decorated function must be a generator (using yield) or return an Iterator that yields tuples of (Finding, list[AttachmentInput]). Only one on_es_poll action is allowed per app.
503
+
504
+ Example:
505
+ >>> @app.on_es_poll()
506
+ ... def on_es_poll(
507
+ ... params: OnESPollParams, soar: SOARClient, asset: Asset
508
+ ... ) -> Iterator[tuple[Finding, list[AttachmentInput]]]:
509
+ ... yield (
510
+ ... Finding(
511
+ ... rule_title="Risk threshold exceeded for user",
512
+ ... rule_description="Risk Threshold Exceeded for an object over a 24 hour period",
513
+ ... security_domain="threat",
514
+ ... risk_object="bad_user@splunk.com",
515
+ ... risk_object_type="user",
516
+ ... risk_score=100.0,
517
+ ... status="New",
518
+ ... ),
519
+ ... [],
520
+ ... )
521
+ """
522
+ return OnESPollDecorator(self)
523
+
498
524
  def view_handler(
499
525
  self,
500
526
  *,
@@ -103,6 +103,62 @@ class ActionRenderer(AstRenderer[ActionMeta]):
103
103
  ctx=ast.Load(),
104
104
  ),
105
105
  ),
106
+ "on es poll": ast.FunctionDef(
107
+ name="on_es_poll",
108
+ args=ast.arguments(
109
+ posonlyargs=[],
110
+ args=[
111
+ ast.arg(
112
+ arg="soar", annotation=ast.Name(id="SOARClient", ctx=ast.Load())
113
+ ),
114
+ ast.arg(
115
+ arg="asset", annotation=ast.Name(id="Asset", ctx=ast.Load())
116
+ ),
117
+ ast.arg(
118
+ arg="params",
119
+ annotation=ast.Name(id="OnESPollParams", ctx=ast.Load()),
120
+ ),
121
+ ],
122
+ vararg=None,
123
+ kwonlyargs=[],
124
+ kw_defaults=[],
125
+ kwarg=None,
126
+ defaults=[],
127
+ ),
128
+ body=[
129
+ ast.Raise(
130
+ ast.Call(
131
+ func=ast.Name(id="NotImplementedError"), args=[], keywords=[]
132
+ )
133
+ )
134
+ ],
135
+ decorator_list=[
136
+ ast.Call(
137
+ func=ast.Name(id="app.on_es_poll", ctx=ast.Load()),
138
+ args=[],
139
+ keywords=[],
140
+ )
141
+ ],
142
+ returns=ast.Subscript(
143
+ value=ast.Name(id="Iterator", ctx=ast.Load()),
144
+ slice=ast.Subscript(
145
+ value=ast.Name(id="tuple", ctx=ast.Load()),
146
+ slice=ast.Tuple(
147
+ elts=[
148
+ ast.Name(id="Finding", ctx=ast.Load()),
149
+ ast.Subscript(
150
+ value=ast.Name(id="list", ctx=ast.Load()),
151
+ slice=ast.Name(id="AttachmentInput", ctx=ast.Load()),
152
+ ctx=ast.Load(),
153
+ ),
154
+ ],
155
+ ctx=ast.Load(),
156
+ ),
157
+ ctx=ast.Load(),
158
+ ),
159
+ ctx=ast.Load(),
160
+ ),
161
+ ),
106
162
  }
107
163
 
108
164
  @property
@@ -80,6 +80,7 @@ class AppRenderer:
80
80
  ast.alias(name="Param", asname=None),
81
81
  ast.alias(name="Params", asname=None),
82
82
  ast.alias(name="OnPollParams", asname=None),
83
+ ast.alias(name="OnESPollParams", asname=None),
83
84
  ],
84
85
  level=0,
85
86
  )
@@ -114,6 +115,16 @@ class AppRenderer:
114
115
  names=[ast.alias(name="Artifact", asname=None)],
115
116
  level=0,
116
117
  )
118
+ yield ast.ImportFrom(
119
+ module="soar_sdk.models.finding",
120
+ names=[ast.alias(name="Finding", asname=None)],
121
+ level=0,
122
+ )
123
+ yield ast.ImportFrom(
124
+ module="soar_sdk.models.attachment_input",
125
+ names=[ast.alias(name="AttachmentInput", asname=None)],
126
+ level=0,
127
+ )
117
128
 
118
129
  def create_app_constructor(self) -> ast.Assign:
119
130
  """Create the App class constructor.
@@ -4,6 +4,7 @@ from .action import ActionDecorator
4
4
  from .test_connectivity import ConnectivityTestDecorator
5
5
  from .view_handler import ViewHandlerDecorator
6
6
  from .on_poll import OnPollDecorator
7
+ from .on_es_poll import OnESPollDecorator
7
8
  from .webhook import WebhookDecorator
8
9
  from .make_request import MakeRequestDecorator
9
10
 
@@ -11,6 +12,7 @@ __all__ = [
11
12
  "ActionDecorator",
12
13
  "ConnectivityTestDecorator",
13
14
  "MakeRequestDecorator",
15
+ "OnESPollDecorator",
14
16
  "OnPollDecorator",
15
17
  "ViewHandlerDecorator",
16
18
  "WebhookDecorator",
@@ -0,0 +1,218 @@
1
+ import inspect
2
+ from functools import wraps
3
+ from typing import Any
4
+ from collections.abc import Callable
5
+ from collections.abc import Iterator
6
+
7
+ from soar_sdk.abstract import SOARClient
8
+ from soar_sdk.action_results import ActionResult
9
+ from soar_sdk.params import OnESPollParams
10
+ from soar_sdk.meta.actions import ActionMeta
11
+ from soar_sdk.types import Action, action_protocol
12
+ from soar_sdk.exceptions import ActionFailure
13
+ from soar_sdk.async_utils import run_async_if_needed
14
+ from soar_sdk.logging import getLogger
15
+ from soar_sdk.models.finding import Finding
16
+ from soar_sdk.models.attachment_input import AttachmentInput
17
+ from soar_sdk.models.container import Container
18
+
19
+ from typing import TYPE_CHECKING
20
+
21
+ if TYPE_CHECKING:
22
+ from soar_sdk.app import App
23
+
24
+
25
+ class OnESPollDecorator:
26
+ """Class-based decorator for tagging a function as the special 'on es poll' action."""
27
+
28
+ def __init__(self, app: "App") -> None:
29
+ self.app = app
30
+
31
+ def __call__(self, function: Callable) -> Action:
32
+ """Decorator for the 'on es poll' action.
33
+
34
+ The decorated function must be a generator (using yield) or return an Iterator that yields tuples of (Finding, list[AttachmentInput]). Only one on_es_poll action is allowed per app.
35
+
36
+ Usage:
37
+ Each yielded tuple creates a Container from the Finding metadata. All AttachmentInput items in the list are added as vault attachments to that container.
38
+ """
39
+ if self.app.actions_manager.get_action("on_es_poll"):
40
+ raise TypeError(
41
+ "The 'on_es_poll' decorator can only be used once per App instance."
42
+ )
43
+
44
+ is_generator = inspect.isgeneratorfunction(function)
45
+ is_async_generator = inspect.isasyncgenfunction(function)
46
+ signature = inspect.signature(function)
47
+
48
+ has_iterator_return = (
49
+ signature.return_annotation != inspect.Signature.empty
50
+ and getattr(signature.return_annotation, "__origin__", None) is Iterator
51
+ )
52
+
53
+ if not (is_generator or is_async_generator or has_iterator_return):
54
+ raise TypeError(
55
+ "The on_es_poll function must be a generator (use 'yield') or return an Iterator."
56
+ )
57
+
58
+ action_identifier = "on_es_poll"
59
+ action_name = "on es poll"
60
+
61
+ validated_params_class = OnESPollParams
62
+ logger = getLogger()
63
+
64
+ @action_protocol
65
+ @wraps(function)
66
+ def inner(
67
+ params: OnESPollParams,
68
+ soar: SOARClient = self.app.soar_client,
69
+ *args: Any, # noqa: ANN401
70
+ **kwargs: Any, # noqa: ANN401
71
+ ) -> bool:
72
+ try:
73
+ try:
74
+ action_params = validated_params_class.parse_obj(params)
75
+ except Exception as e:
76
+ logger.info(f"Parameter validation error: {e!s}")
77
+ return self.app._adapt_action_result(
78
+ ActionResult(
79
+ status=False, message=f"Invalid parameters: {e!s}"
80
+ ),
81
+ self.app.actions_manager,
82
+ )
83
+
84
+ kwargs = self.app._build_magic_args(function, soar=soar, **kwargs)
85
+
86
+ result = function(action_params, *args, **kwargs)
87
+ result = run_async_if_needed(result)
88
+
89
+ for item in result:
90
+ if not isinstance(item, tuple) or len(item) != 2:
91
+ logger.info(
92
+ f"Warning: Expected tuple of (Finding, list[AttachmentInput]), got: {type(item)}"
93
+ )
94
+ continue
95
+
96
+ finding, attachments = item
97
+
98
+ if not isinstance(finding, Finding):
99
+ logger.info(
100
+ f"Warning: First element must be Finding, got: {type(finding)}"
101
+ )
102
+ continue
103
+
104
+ if not isinstance(attachments, list):
105
+ logger.info(
106
+ f"Warning: Second element must be list[AttachmentInput], got: {type(attachments)}"
107
+ )
108
+ continue
109
+
110
+ for attachment in attachments:
111
+ if not isinstance(attachment, AttachmentInput):
112
+ logger.info(
113
+ f"Warning: Attachment must be AttachmentInput, got: {type(attachment)}"
114
+ )
115
+ break
116
+ else:
117
+ finding_dict = finding.to_dict()
118
+ logger.info(
119
+ f"Processing finding: {finding_dict.get('rule_title', 'Unnamed finding')}"
120
+ )
121
+
122
+ # Send finding to ES and get finding_id back
123
+ finding_id = self.app.actions_manager.send_finding_to_es(
124
+ finding_dict
125
+ )
126
+
127
+ container = Container(
128
+ name=finding.rule_title,
129
+ description=finding.rule_description,
130
+ severity=finding.urgency or "medium",
131
+ status=finding.status,
132
+ owner_id=finding.owner,
133
+ sensitivity=finding.disposition,
134
+ tags=finding.source,
135
+ external_id=finding_id,
136
+ data={
137
+ "security_domain": finding.security_domain,
138
+ "risk_score": finding.risk_score,
139
+ "risk_object": finding.risk_object,
140
+ "risk_object_type": finding.risk_object_type,
141
+ },
142
+ )
143
+
144
+ ret_val, message, container_id = (
145
+ self.app.actions_manager.save_container(container.to_dict())
146
+ )
147
+ logger.info(
148
+ f"Creating container for finding: {finding.rule_title}"
149
+ )
150
+
151
+ if not ret_val:
152
+ logger.info(f"Failed to create container: {message}")
153
+ continue
154
+
155
+ for attachment in attachments:
156
+ try:
157
+ if attachment.file_content is not None:
158
+ vault_id = soar.vault.create_attachment(
159
+ container_id=container_id,
160
+ file_content=attachment.file_content,
161
+ file_name=attachment.file_name,
162
+ metadata=attachment.metadata,
163
+ )
164
+ else:
165
+ vault_id = soar.vault.add_attachment(
166
+ container_id=container_id,
167
+ file_location=attachment.file_location,
168
+ file_name=attachment.file_name,
169
+ metadata=attachment.metadata,
170
+ )
171
+ logger.info(
172
+ f"Added attachment {attachment.file_name} with vault_id: {vault_id}"
173
+ )
174
+ except Exception as e:
175
+ logger.info(
176
+ f"Failed to add attachment {attachment.file_name}: {e!s}"
177
+ )
178
+
179
+ return self.app._adapt_action_result(
180
+ ActionResult(status=True, message="Finding processing complete"),
181
+ self.app.actions_manager,
182
+ )
183
+ except ActionFailure as e:
184
+ e.set_action_name(action_name)
185
+ return self.app._adapt_action_result(
186
+ ActionResult(status=False, message=str(e)),
187
+ self.app.actions_manager,
188
+ )
189
+ except Exception as e:
190
+ self.app.actions_manager.add_exception(e)
191
+ logger.info(f"Error during finding processing: {e!s}")
192
+ return self.app._adapt_action_result(
193
+ ActionResult(status=False, message=str(e)),
194
+ self.app.actions_manager,
195
+ )
196
+
197
+ inner.params_class = validated_params_class
198
+
199
+ class OnESPollActionMeta(ActionMeta):
200
+ def model_dump(self, *args: object, **kwargs: object) -> dict[str, Any]:
201
+ data = super().model_dump(*args, **kwargs)
202
+ data["output"] = []
203
+ return data
204
+
205
+ inner.meta = OnESPollActionMeta(
206
+ action=action_name,
207
+ identifier=action_identifier,
208
+ description=inspect.getdoc(function) or action_name,
209
+ verbose="Callback action for the on_es_poll ingest functionality",
210
+ type="ingest",
211
+ read_only=True,
212
+ parameters=validated_params_class,
213
+ versions="EQ(*)",
214
+ )
215
+
216
+ self.app.actions_manager.set_action(action_identifier, inner)
217
+ self.app._dev_skip_in_pytest(function, inner)
218
+ return inner
@@ -277,9 +277,7 @@ class UvPackage(BaseModel):
277
277
  for abi in abi_precedence:
278
278
  abi_wheels = [wheel for wheel in self.wheels if abi in wheel.abi_tags]
279
279
  for python in python_precedence:
280
- python_wheels = [
281
- wheel for wheel in abi_wheels if python in wheel.python_tags
282
- ]
280
+ python_wheels = self._filter_python_wheels(abi_wheels, python, abi)
283
281
  for platform in platform_precedence:
284
282
  platform_wheels = [
285
283
  wheel
@@ -293,6 +291,54 @@ class UvPackage(BaseModel):
293
291
  f"Could not find a suitable wheel for {self.name=}, {self.version=}, {abi_precedence=}, {python_precedence=}, {platform_precedence=}"
294
292
  )
295
293
 
294
+ def _filter_python_wheels(
295
+ self, wheels: list[UvWheel], target_python: str, abi: str
296
+ ) -> list[UvWheel]:
297
+ """Filter and sort wheels by Python version compatibility.
298
+
299
+ For abi3 wheels, prefers the highest compatible minimum version
300
+ (e.g., cp311-abi3 over cp38-abi3 for Python 3.13).
301
+ """
302
+ compatible = [
303
+ wheel
304
+ for wheel in wheels
305
+ if self._is_python_compatible(wheel, target_python, abi)
306
+ ]
307
+
308
+ # For abi3 wheels, prefer highest minimum version (closest to target)
309
+ if abi == "abi3" and compatible:
310
+ compatible = sorted(
311
+ compatible,
312
+ key=lambda w: max(
313
+ (int(tag[2:]) for tag in w.python_tags if tag.startswith("cp")),
314
+ default=0,
315
+ ),
316
+ reverse=True,
317
+ )
318
+
319
+ return compatible
320
+
321
+ def _is_python_compatible(
322
+ self, wheel: UvWheel, target_python: str, abi: str
323
+ ) -> bool:
324
+ """Check if a wheel is compatible with the target Python version.
325
+
326
+ For abi3 wheels, the Python tag indicates minimum version (e.g., cp311-abi3 works with Python ≥3.11).
327
+ For non-abi3 wheels, exact tag matching is required.
328
+ """
329
+ if target_python in wheel.python_tags:
330
+ return True
331
+
332
+ # For abi3 wheels, check if target >= minimum version (e.g., cp313 >= cp311)
333
+ if abi == "abi3":
334
+ return any(
335
+ int(tag[2:]) <= int(target_python[2:])
336
+ for tag in wheel.python_tags
337
+ if tag.startswith("cp") and target_python.startswith("cp")
338
+ )
339
+
340
+ return False
341
+
296
342
  _manylinux_precedence: ClassVar[list[str]] = [
297
343
  "_2_28", # glibc 2.28, latest stable version, supports Ubuntu 18.10+ and RHEL/Oracle 8+
298
344
  "_2_17", # glibc 2.17, LTS-ish, supports Ubuntu 13.10+ and RHEL/Oracle 7+
@@ -0,0 +1,15 @@
1
+ from .artifact import Artifact
2
+ from .attachment_input import AttachmentInput
3
+ from .container import Container
4
+ from .finding import Finding, DrilldownSearch, DrilldownDashboard
5
+ from .vault_attachment import VaultAttachment
6
+
7
+ __all__ = [
8
+ "Artifact",
9
+ "AttachmentInput",
10
+ "Container",
11
+ "DrilldownDashboard",
12
+ "DrilldownSearch",
13
+ "Finding",
14
+ "VaultAttachment",
15
+ ]
@@ -0,0 +1,27 @@
1
+ from pydantic import BaseModel, field_validator, ConfigDict
2
+ from pydantic_core.core_schema import ValidationInfo
3
+
4
+
5
+ class AttachmentInput(BaseModel):
6
+ """Represents a vault attachment to be created during on_es_poll.
7
+
8
+ Specify either file_content OR file_location, not both.
9
+ """
10
+
11
+ model_config = ConfigDict(extra="forbid")
12
+
13
+ file_content: str | bytes | None = None
14
+ file_location: str | None = None
15
+ file_name: str
16
+ metadata: dict[str, str] | None = None
17
+
18
+ @field_validator("file_location")
19
+ @classmethod
20
+ def validate_one_source(cls, v: str | None, info: ValidationInfo) -> str | None:
21
+ """Validate that exactly one of file_content or file_location is provided."""
22
+ file_content = info.data.get("file_content")
23
+ if v is None and file_content is None:
24
+ raise ValueError("Must provide either file_content or file_location")
25
+ if v is not None and file_content is not None:
26
+ raise ValueError("Cannot provide both file_content and file_location")
27
+ return v
@@ -14,6 +14,7 @@ class Container(BaseModel):
14
14
  label: str | None = None
15
15
  description: str | None = None
16
16
  source_data_identifier: str | None = None
17
+ external_id: str | None = None
17
18
  severity: str | None = None
18
19
  status: str | None = None
19
20
  tags: list[str] | str | None = None
soar_sdk/params.py CHANGED
@@ -193,6 +193,25 @@ class OnPollParams(Params):
193
193
  )
194
194
 
195
195
 
196
+ class OnESPollParams(Params):
197
+ """Canonical parameters for the special 'on es poll' action."""
198
+
199
+ start_time: int = Param(
200
+ description="Start of time range, in epoch time (milliseconds).",
201
+ required=False,
202
+ )
203
+
204
+ end_time: int = Param(
205
+ description="End of time range, in epoch time (milliseconds).",
206
+ required=False,
207
+ )
208
+
209
+ container_count: int = Param(
210
+ description="Maximum number of container records to query for.",
211
+ required=False,
212
+ )
213
+
214
+
196
215
  class MakeRequestParams(Params):
197
216
  """Canonical parameters for the special make request action."""
198
217
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splunk-soar-sdk
3
- Version: 3.1.0
3
+ Version: 3.2.1
4
4
  Summary: The official framework for developing and testing Splunk SOAR Apps
5
5
  Project-URL: Homepage, https://github.com/phantomcyber/splunk-soar-sdk
6
6
  Project-URL: Documentation, https://github.com/phantomcyber/splunk-soar-sdk
@@ -1,8 +1,8 @@
1
1
  soar_sdk/__init__.py,sha256=RzAng-ARqpK01SY82lNy4uYJFVG0yW6Q3CccEqbToJ4,726
2
2
  soar_sdk/abstract.py,sha256=3oJvWljqDTr2nucf-9IdBRMDJwRnDe6nISYKyGuLMxw,7752
3
3
  soar_sdk/action_results.py,sha256=ajlsed6ZkCyOTra4SFy31iYZO2a6mlvBK2-zIyO09q4,11928
4
- soar_sdk/actions_manager.py,sha256=Hx1Uh2AhlDJpgPIYXlIypY6RQ5yZWmsrIsX6avo65IQ,6480
5
- soar_sdk/app.py,sha256=fFbs4-ZWQK8N-Rz59sayIDEKRy9ccVtj6UhORe0MCF4,33343
4
+ soar_sdk/actions_manager.py,sha256=Ce4zU06QJlbTP3TONfxsS4CWuykcZpQ0mw3ir8ThLsw,6663
5
+ soar_sdk/app.py,sha256=xCdBHQgzdIwfyYRHSGFRYpEu8nKPMsULZCrdm5CbRrY,34512
6
6
  soar_sdk/app_cli_runner.py,sha256=tdglXCIRZS3dc3P18Tha7yUJQX9zIDxJFdST02LL9xY,11644
7
7
  soar_sdk/app_client.py,sha256=p5dsgxmMK61qLFIn7pATEQkVWldTtQhJPcMs4Idv2Lw,6197
8
8
  soar_sdk/asset.py,sha256=b4-f7r3JSfR-nUzalpgJnoXAIEHQ34yvIuoyoAFvye0,11055
@@ -14,7 +14,7 @@ soar_sdk/exceptions.py,sha256=bzydqdpzwG5VaVkZBiUEcf9Wp0clMdzqdUBZHzpfqT0,1830
14
14
  soar_sdk/field_utils.py,sha256=Jb0HteUPd-CtuDM7rNXVLy4uRxl419zeDxY_oOpU8GM,287
15
15
  soar_sdk/input_spec.py,sha256=79MFGF2IkAAoWpHmZYP0VqUBu10SkvmF_RPmXkf8bQ4,4677
16
16
  soar_sdk/logging.py,sha256=30cUxU7Tjf_OY4rUOrvjDoqPH7pcFydrdDbcG4ppY_s,11424
17
- soar_sdk/params.py,sha256=qKj3CkT6vPdQK8Aqxc1fYa974b-j7vtclcWMIALvWGI,9806
17
+ soar_sdk/params.py,sha256=I-XL_5b7XEZEpnqAG05dll7rdscRnzbBInypO8miD_U,10308
18
18
  soar_sdk/paths.py,sha256=XhpanQCAiTXaulRx440oKu36mnll7P05TethHXgMpgQ,239
19
19
  soar_sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  soar_sdk/types.py,sha256=-mOu8_sphcgJictMTn4Dl_6ijd2elLAlU2nuXF74zQc,616
@@ -43,15 +43,16 @@ soar_sdk/cli/manifests/serializers.py,sha256=hSfOcBNhv7s437A7t5BM1NXG5dfjKmh_xbH
43
43
  soar_sdk/cli/package/cli.py,sha256=8AWItAAXzfjJMRLwDIRbrN9cQhy7clBX7WrOmdcGClw,9499
44
44
  soar_sdk/cli/package/utils.py,sha256=zZmpWg76Vb3rtGn28aPhPHt1JUWR6eIByU6DTTRC3ng,1584
45
45
  soar_sdk/code_renderers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
- soar_sdk/code_renderers/action_renderer.py,sha256=iPEPUVmZfm0FgQwlvKf4JwYlwcslH_qjoHSx6zSaVA8,14439
47
- soar_sdk/code_renderers/app_renderer.py,sha256=4EiGuMF9navKY7zXXe1ioG_4AaQKahOTwoh7kwfhi5U,7377
46
+ soar_sdk/code_renderers/action_renderer.py,sha256=nPbB3SJLm4VDu_zuwY6vO_LgVg1tvW5ZTrzeS7JdyF8,16489
47
+ soar_sdk/code_renderers/app_renderer.py,sha256=OCNNhXc36Amcg1kUUayyyzWx_M2RY8W8wvy0ppujLaE,7791
48
48
  soar_sdk/code_renderers/asset_renderer.py,sha256=eZS-VaV6wn_zgKth4Sieqo2fyPLF1o-k3qN7UjNUycE,3833
49
49
  soar_sdk/code_renderers/renderer.py,sha256=82FP89024ZP-J_Ebzsvrso1hlCFSLllR1U9GR-wB_Pc,1229
50
50
  soar_sdk/code_renderers/toml_renderer.py,sha256=-zP8UzlYMCVVA5ex9slaNLeFTu4xLjkv88YLmRNLrTM,1505
51
51
  soar_sdk/code_renderers/templates/pyproject.toml.jinja,sha256=uH7SJhFD9_-kYzGHobwv04aV4Nfru1bquL30GoOirfg,3918
52
- soar_sdk/decorators/__init__.py,sha256=ttvapTczeQpReZVYgjTw4qnEqKd7b8pR7lNaCpO0npQ,513
52
+ soar_sdk/decorators/__init__.py,sha256=-eM9ueHvdOq8Vd2C7Y0H3EJajVdV_sVfFJGQngbGRhg,580
53
53
  soar_sdk/decorators/action.py,sha256=als7cKWozZnobylBXsY5aQ2Y4js_2Y4n8UmxI-pKy1w,6642
54
54
  soar_sdk/decorators/make_request.py,sha256=NWPI5NhmvnI7VMKI-tmhsQ34kegGxWyXC9_PVW49EJc,5978
55
+ soar_sdk/decorators/on_es_poll.py,sha256=5wiAcboG45ut7dLr0ZRcKOU01yIBfBrq00laGtv1k84,9437
55
56
  soar_sdk/decorators/on_poll.py,sha256=H_aijFWKeZKwiS72Vsa9uolmj8sziGW2lZF6EhjEDjs,8276
56
57
  soar_sdk/decorators/test_connectivity.py,sha256=tZt7w-BnZUpxCyixblXts4tsUp8z-Kmz-JGJ5i5LQs8,3564
57
58
  soar_sdk/decorators/view_handler.py,sha256=_qjfk2nQxPwraldjRIl4DWdW-tvANJfdVDU_lLA3UvE,7075
@@ -61,11 +62,12 @@ soar_sdk/meta/actions.py,sha256=YadG8Zkc0SWmcG5-Dj4NyWlYYAh4CyJksLYp_GmNZNM,1818
61
62
  soar_sdk/meta/adapters.py,sha256=KjSYIUtkCz2eesA_vhsNCjfi5C-Uz71tbSuDIjhuB8U,1112
62
63
  soar_sdk/meta/app.py,sha256=eZlM8GIY1B_o-RzJrRNCNVEQSx0sFupxZqCM7sIWGv4,2777
63
64
  soar_sdk/meta/datatypes.py,sha256=piR-oBVAATiRciXSdVE7XaqjUZTgSaOvTEqcOcNvCS0,795
64
- soar_sdk/meta/dependencies.py,sha256=DZr38K_1rKh7OzlIb5nLbdDH8xOHArr48H8Ra6rcB4k,18259
65
+ soar_sdk/meta/dependencies.py,sha256=e5PQQMpnz1_XJIvLPGeMQh8iE3RguqlCsag1CD5vXz8,19901
65
66
  soar_sdk/meta/webhooks.py,sha256=E5pdoD9j7FDeM2DBTO2h9Yw6-5flzp-NfhM_M1oPAUU,1216
66
- soar_sdk/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
67
+ soar_sdk/models/__init__.py,sha256=ql7RRFAxmSdzU-IsoU1OFQ53E6WxZJ7YQQAhHl4Ssbk,380
67
68
  soar_sdk/models/artifact.py,sha256=OlYp150sf9sYvTz6tC7PaYV1qbBvQYrz1bXmC2xNj8Q,1086
68
- soar_sdk/models/container.py,sha256=SYdDiyeUGm8dFpOdPl5BGVM1UO2onJsJCiax-1-hZGo,1492
69
+ soar_sdk/models/attachment_input.py,sha256=dE0Z8CaKSYkNP1-NvOWz4O044-UKtqRoxdJhc471M5k,1043
70
+ soar_sdk/models/container.py,sha256=2uLwFDDcsJ7cURYsd6Vt3mtbqEszlIHXshYL-BUGUEA,1527
69
71
  soar_sdk/models/finding.py,sha256=Yhw4mOJLeZj7ylDgxbjJs9wm_xzK6Sbkr4r3XOtL0PM,1414
70
72
  soar_sdk/models/vault_attachment.py,sha256=IQPX239OFClVfOKr9nHIu9Is55cXWBaOgM2lG5Lt-b4,1072
71
73
  soar_sdk/models/view.py,sha256=frfbNdWfzc0XjiU3CY79zBJxvzUsgLdFmphVeZ6QqTc,777
@@ -98,8 +100,8 @@ soar_sdk/views/components/pie_chart.py,sha256=LVTeHVJN6nf2vjUs9y7PDBhS0U1fKW750l
98
100
  soar_sdk/webhooks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
99
101
  soar_sdk/webhooks/models.py,sha256=PG9SDs5xXqtFndm5C8AsJOTYXU5v_UTY7SpYosWT_CA,4542
100
102
  soar_sdk/webhooks/routing.py,sha256=MnzbnIDy2uG_5mJzsTeX-NsE6QYvzyqEGbHmEFj-DG8,6900
101
- splunk_soar_sdk-3.1.0.dist-info/METADATA,sha256=AyJBAA3lbCNV4tHHtHb4ca7u4MCHj9_C2exQfMeydik,7334
102
- splunk_soar_sdk-3.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
103
- splunk_soar_sdk-3.1.0.dist-info/entry_points.txt,sha256=CgBjo2ZWpYNkt9TgvToL26h2Tg1yt8FbvYTb5NVgNuc,51
104
- splunk_soar_sdk-3.1.0.dist-info/licenses/LICENSE,sha256=gNCGrGhrSQb1PUzBOByVUN1tvaliwLZfna-QU2r2hQ8,11345
105
- splunk_soar_sdk-3.1.0.dist-info/RECORD,,
103
+ splunk_soar_sdk-3.2.1.dist-info/METADATA,sha256=u9g2bI4CP5vO9AKJkPD0GswmLc-V48SGesjL_e3kZsU,7334
104
+ splunk_soar_sdk-3.2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
105
+ splunk_soar_sdk-3.2.1.dist-info/entry_points.txt,sha256=CgBjo2ZWpYNkt9TgvToL26h2Tg1yt8FbvYTb5NVgNuc,51
106
+ splunk_soar_sdk-3.2.1.dist-info/licenses/LICENSE,sha256=gNCGrGhrSQb1PUzBOByVUN1tvaliwLZfna-QU2r2hQ8,11345
107
+ splunk_soar_sdk-3.2.1.dist-info/RECORD,,