splunk-soar-sdk 3.6.1__py3-none-any.whl → 3.8.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/auth/flows.py ADDED
@@ -0,0 +1,172 @@
1
+ from __future__ import annotations
2
+
3
+ import time
4
+ from abc import ABC, abstractmethod
5
+ from collections.abc import Callable
6
+ from typing import TYPE_CHECKING, Any
7
+
8
+ from soar_sdk.auth.client import (
9
+ AuthorizationRequiredError,
10
+ OAuthClientError,
11
+ SOARAssetOAuthClient,
12
+ TokenExpiredError,
13
+ )
14
+ from soar_sdk.auth.models import OAuthConfig, OAuthGrantType, OAuthToken
15
+ from soar_sdk.logging import getLogger
16
+
17
+ if TYPE_CHECKING:
18
+ from soar_sdk.asset_state import AssetState
19
+
20
+ logger = getLogger()
21
+
22
+
23
+ class OAuthFlow(ABC):
24
+ """Abstract base class for OAuth authentication flows."""
25
+
26
+ @abstractmethod
27
+ def authenticate(self) -> OAuthToken:
28
+ """Execute the authentication flow and return a valid token."""
29
+
30
+ @abstractmethod
31
+ def get_token(self) -> OAuthToken:
32
+ """Get a valid token, refreshing if necessary."""
33
+
34
+
35
+ class ClientCredentialsFlow(OAuthFlow):
36
+ """OAuth 2.0 Client Credentials flow."""
37
+
38
+ def __init__(
39
+ self,
40
+ auth_state: AssetState,
41
+ *,
42
+ client_id: str,
43
+ client_secret: str,
44
+ token_endpoint: str,
45
+ scope: str | list[str] | None = None,
46
+ extra_params: dict[str, Any] | None = None,
47
+ ) -> None:
48
+ self._auth_state = auth_state
49
+ self._extra_params = extra_params
50
+
51
+ self._config = OAuthConfig(
52
+ client_id=client_id,
53
+ client_secret=client_secret,
54
+ token_endpoint=token_endpoint,
55
+ scope=scope,
56
+ grant_type=OAuthGrantType.CLIENT_CREDENTIALS,
57
+ )
58
+
59
+ self._client = SOARAssetOAuthClient(self._config, auth_state)
60
+
61
+ def authenticate(self) -> OAuthToken:
62
+ """Authenticate using client credentials."""
63
+ return self._client.fetch_token_with_client_credentials(
64
+ extra_params=self._extra_params,
65
+ )
66
+
67
+ def get_token(self) -> OAuthToken:
68
+ """Get a valid token, fetching a new one if expired."""
69
+ try:
70
+ return self._client.get_valid_token(auto_refresh=False)
71
+ except (AuthorizationRequiredError, TokenExpiredError):
72
+ return self.authenticate()
73
+
74
+
75
+ class AuthorizationCodeFlow(OAuthFlow):
76
+ """OAuth 2.0 Authorization Code flow."""
77
+
78
+ def __init__(
79
+ self,
80
+ auth_state: AssetState,
81
+ asset_id: str,
82
+ *,
83
+ client_id: str,
84
+ client_secret: str | None = None,
85
+ authorization_endpoint: str,
86
+ token_endpoint: str,
87
+ redirect_uri: str,
88
+ scope: str | list[str] | None = None,
89
+ use_pkce: bool = False,
90
+ extra_auth_params: dict[str, Any] | None = None,
91
+ extra_token_params: dict[str, Any] | None = None,
92
+ poll_timeout: int = 300,
93
+ poll_interval: int = 3,
94
+ ) -> None:
95
+ self._auth_state = auth_state
96
+ self._asset_id = asset_id
97
+ self._use_pkce = use_pkce
98
+ self._extra_auth_params = extra_auth_params
99
+ self._extra_token_params = extra_token_params
100
+ self._poll_timeout = poll_timeout
101
+ self._poll_interval = poll_interval
102
+
103
+ self._config = OAuthConfig(
104
+ client_id=client_id,
105
+ client_secret=client_secret,
106
+ authorization_endpoint=authorization_endpoint,
107
+ token_endpoint=token_endpoint,
108
+ redirect_uri=redirect_uri,
109
+ scope=scope,
110
+ grant_type=OAuthGrantType.AUTHORIZATION_CODE,
111
+ )
112
+
113
+ self._client = SOARAssetOAuthClient(self._config, auth_state)
114
+
115
+ @property
116
+ def client(self) -> SOARAssetOAuthClient:
117
+ """Return the OAuth client."""
118
+ return self._client
119
+
120
+ def get_authorization_url(self) -> str:
121
+ """Generate the authorization URL."""
122
+ auth_url, _ = self._client.create_authorization_url(
123
+ self._asset_id,
124
+ use_pkce=self._use_pkce,
125
+ extra_params=self._extra_auth_params,
126
+ )
127
+ return auth_url
128
+
129
+ def set_authorization_code(self, code: str) -> None:
130
+ """Store authorization code in state (called by webhook)."""
131
+ self._client.set_authorization_code(code)
132
+
133
+ def exchange_code_for_token(self, code: str) -> OAuthToken:
134
+ """Exchange an authorization code for tokens."""
135
+ return self._client.fetch_token_with_authorization_code(
136
+ code,
137
+ extra_params=self._extra_token_params,
138
+ )
139
+
140
+ def wait_for_authorization(
141
+ self,
142
+ on_progress: Callable[[int], None] | None = None,
143
+ ) -> OAuthToken:
144
+ """Wait for user authorization to complete by polling state."""
145
+ start_time = time.time()
146
+ iteration = 0
147
+
148
+ while time.time() - start_time < self._poll_timeout:
149
+ time.sleep(self._poll_interval)
150
+ iteration += 1
151
+
152
+ if on_progress:
153
+ on_progress(iteration)
154
+
155
+ code = self._client.get_authorization_code(force_reload=True)
156
+ if code:
157
+ return self.exchange_code_for_token(code)
158
+
159
+ raise OAuthClientError(
160
+ f"Authorization timed out after {self._poll_timeout} seconds"
161
+ )
162
+
163
+ def authenticate(self) -> OAuthToken:
164
+ """Execute the full authorization code flow."""
165
+ auth_url = self.get_authorization_url()
166
+ raise AuthorizationRequiredError(
167
+ f"User authorization required. Please visit: {auth_url}"
168
+ )
169
+
170
+ def get_token(self) -> OAuthToken:
171
+ """Get a valid token, refreshing if necessary."""
172
+ return self._client.get_valid_token(auto_refresh=True)
@@ -0,0 +1,97 @@
1
+ from __future__ import annotations
2
+
3
+ import base64
4
+ from collections.abc import Generator
5
+
6
+ import httpx
7
+
8
+ from soar_sdk.auth.client import SOARAssetOAuthClient
9
+ from soar_sdk.auth.models import OAuthToken
10
+
11
+
12
+ class BasicAuth(httpx.Auth):
13
+ """HTTPX authentication using HTTP Basic Authentication."""
14
+
15
+ def __init__(self, username: str, password: str) -> None:
16
+ self._username = username
17
+ self._password = password
18
+
19
+ def auth_flow(
20
+ self,
21
+ request: httpx.Request,
22
+ ) -> Generator[httpx.Request, httpx.Response]:
23
+ """Add Basic authentication header to the request."""
24
+ credentials = f"{self._username}:{self._password}"
25
+ encoded = base64.b64encode(credentials.encode()).decode("ascii")
26
+ request.headers["Authorization"] = f"Basic {encoded}"
27
+ yield request
28
+
29
+
30
+ class StaticTokenAuth(httpx.Auth):
31
+ """HTTPX authentication using a static token."""
32
+
33
+ def __init__(
34
+ self,
35
+ token: OAuthToken | str,
36
+ *,
37
+ token_type: str = "Bearer", # noqa: S107
38
+ header_name: str = "Authorization",
39
+ ) -> None:
40
+ if isinstance(token, str):
41
+ self._access_token = token
42
+ else:
43
+ self._access_token = token.access_token
44
+ token_type = token.token_type or token_type
45
+ self._token_type = token_type
46
+ self._header_name = header_name
47
+
48
+ def auth_flow(
49
+ self,
50
+ request: httpx.Request,
51
+ ) -> Generator[httpx.Request, httpx.Response]:
52
+ """Add authentication header to the request."""
53
+ if self._token_type:
54
+ request.headers[self._header_name] = (
55
+ f"{self._token_type} {self._access_token}"
56
+ )
57
+ else:
58
+ request.headers[self._header_name] = self._access_token
59
+ yield request
60
+
61
+
62
+ class OAuthBearerAuth(httpx.Auth):
63
+ """HTTPX authentication using OAuth Bearer tokens."""
64
+
65
+ requires_response_body = True
66
+
67
+ def __init__(
68
+ self,
69
+ oauth_client: SOARAssetOAuthClient,
70
+ *,
71
+ auto_refresh: bool = True,
72
+ ) -> None:
73
+ self._oauth_client = oauth_client
74
+ self._auto_refresh = auto_refresh
75
+ self._token: OAuthToken | None = None
76
+
77
+ def auth_flow(
78
+ self,
79
+ request: httpx.Request,
80
+ ) -> Generator[httpx.Request, httpx.Response]:
81
+ """Handle authentication flow for a request."""
82
+ if self._token is None or self._token.is_expired():
83
+ self._token = self._oauth_client.get_valid_token(
84
+ auto_refresh=self._auto_refresh
85
+ )
86
+
87
+ request.headers["Authorization"] = f"Bearer {self._token.access_token}"
88
+ response = yield request
89
+
90
+ if (
91
+ response.status_code == 401
92
+ and self._auto_refresh
93
+ and self._token.refresh_token
94
+ ):
95
+ self._token = self._oauth_client.refresh_token(self._token.refresh_token)
96
+ request.headers["Authorization"] = f"Bearer {self._token.access_token}"
97
+ yield request
@@ -0,0 +1,101 @@
1
+ from __future__ import annotations
2
+
3
+ import time
4
+ from enum import StrEnum
5
+
6
+ from pydantic import BaseModel, ConfigDict, Field
7
+
8
+
9
+ class OAuthGrantType(StrEnum):
10
+ """Supported OAuth 2.0 grant types."""
11
+
12
+ AUTHORIZATION_CODE = "authorization_code"
13
+ CLIENT_CREDENTIALS = "client_credentials"
14
+ REFRESH_TOKEN = "refresh_token" # noqa: S105
15
+
16
+
17
+ class OAuthToken(BaseModel):
18
+ """OAuth 2.0 token response."""
19
+
20
+ model_config = ConfigDict(extra="allow")
21
+
22
+ _DEFAULT_TOKEN_TYPE = "Bearer" # noqa: S105
23
+
24
+ access_token: str
25
+ token_type: str = _DEFAULT_TOKEN_TYPE
26
+ expires_in: int | None = None
27
+ refresh_token: str | None = None
28
+ scope: str | None = None
29
+ expires_at: float | None = None
30
+
31
+ def model_post_init(self, __context: object) -> None:
32
+ """Calculate expires_at if not provided but expires_in is available."""
33
+ if self.expires_at is None and self.expires_in is not None:
34
+ self.expires_at = time.time() + self.expires_in
35
+
36
+ def is_expired(self, leeway: int = 30) -> bool:
37
+ """Check if the token is expired."""
38
+ if self.expires_at is None:
39
+ return False
40
+ return time.time() >= (self.expires_at - leeway)
41
+
42
+
43
+ class OAuthConfig(BaseModel):
44
+ """Configuration for OAuth 2.0 authentication."""
45
+
46
+ model_config = ConfigDict(extra="forbid")
47
+
48
+ client_id: str
49
+ client_secret: str | None = None
50
+ authorization_endpoint: str | None = None
51
+ token_endpoint: str
52
+ redirect_uri: str | None = None
53
+ scope: str | list[str] | None = None
54
+ grant_type: OAuthGrantType = OAuthGrantType.AUTHORIZATION_CODE
55
+
56
+ def get_scope_string(self) -> str | None:
57
+ """Return scope as a space-separated string."""
58
+ if self.scope is None:
59
+ return None
60
+ if isinstance(self.scope, list):
61
+ return " ".join(self.scope)
62
+ return self.scope
63
+
64
+
65
+ class OAuthSession(BaseModel):
66
+ """Represents an active OAuth authentication session."""
67
+
68
+ model_config = ConfigDict(extra="forbid")
69
+
70
+ session_id: str
71
+ asset_id: str
72
+ auth_pending: bool = True
73
+ auth_complete: bool = False
74
+ auth_code: str | None = None
75
+ error: str | None = None
76
+ error_description: str | None = None
77
+ state: str | None = None
78
+ code_verifier: str | None = None
79
+
80
+
81
+ class OAuthState(BaseModel):
82
+ """State persisted in auth_state for OAuth authentication."""
83
+
84
+ model_config = ConfigDict(extra="forbid")
85
+
86
+ token: OAuthToken | None = None
87
+ session: OAuthSession | None = None
88
+ client_id: str | None = Field(
89
+ default=None,
90
+ description="Stored client_id to detect credential changes",
91
+ )
92
+
93
+
94
+ class CertificateCredentials(BaseModel):
95
+ """Certificate-based authentication credentials."""
96
+
97
+ model_config = ConfigDict(extra="forbid")
98
+
99
+ certificate_thumbprint: str
100
+ private_key: str
101
+ tenant_id: str | None = None
@@ -66,10 +66,12 @@ async def collect_all_wheels(wheels: list[DependencyWheel]) -> list[tuple[str, b
66
66
  cache = dict(gathered_results)
67
67
 
68
68
  for key, wheel_group in dedupe_map.items():
69
- for path, _ in cache[key]:
70
- wheel_name = Path(path).name
71
- for wheel in wheel_group:
72
- wheel._record_built_wheel(wheel_name)
69
+ representative = wheel_group[0]
70
+ if representative.sdist is not None or representative.source_dir is not None:
71
+ for path, _ in cache[key]:
72
+ wheel_name = Path(path).name
73
+ for wheel in wheel_group:
74
+ wheel._record_built_wheel(wheel_name)
73
75
 
74
76
  return list(chain.from_iterable(cache.values()))
75
77
 
@@ -121,6 +121,13 @@ if TYPE_CHECKING or not _soar_is_available:
121
121
  def get_config(self) -> dict:
122
122
  return self.config
123
123
 
124
+ def get_app_id(self) -> str:
125
+ return self.__app_json.get("appid", "")
126
+
127
+ def get_state_dir(self) -> str:
128
+ phantom_home = os.getenv("PHANTOM_HOME", "/opt/phantom")
129
+ return f"{phantom_home}/local_data/app_states/{self.get_app_id()}/"
130
+
124
131
  def save_state(self, state: dict) -> None:
125
132
  self.__state = state
126
133
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splunk-soar-sdk
3
- Version: 3.6.1
3
+ Version: 3.8.0
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
@@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3
17
17
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
18
  Classifier: Typing :: Typed
19
19
  Requires-Python: <3.15,>=3.13
20
+ Requires-Dist: authlib>=1.3.0
20
21
  Requires-Dist: beautifulsoup4>=4.10.0
21
22
  Requires-Dist: bleach>=6.2.0
22
23
  Requires-Dist: build>=1.3.0
@@ -29,6 +30,7 @@ Requires-Dist: humanize>=4.12.2
29
30
  Requires-Dist: jinja2>=3.1.0
30
31
  Requires-Dist: packaging>=25.0
31
32
  Requires-Dist: pydantic<3,>=2
33
+ Requires-Dist: pyjwt[crypto]>=2.8.0
32
34
  Requires-Dist: requests<3
33
35
  Requires-Dist: setuptools>=80.9.0
34
36
  Requires-Dist: toml<1,>=0.10.2
@@ -1,12 +1,12 @@
1
1
  soar_sdk/__init__.py,sha256=RzAng-ARqpK01SY82lNy4uYJFVG0yW6Q3CccEqbToJ4,726
2
2
  soar_sdk/abstract.py,sha256=GycJhTrSNDa7eDg8hOD7hJjIt5eHEykZhpza-jh_Veo,7787
3
3
  soar_sdk/action_results.py,sha256=eL4qBj2nXDKurzs733z_nnpNREc0SLLYJP2lPTpMKf0,11911
4
- soar_sdk/actions_manager.py,sha256=jfTTa8Rq06GXpJ_UHCA0MFli5bLgYFbcjpgbAW-ZSKo,5684
5
- soar_sdk/app.py,sha256=1tPd1bFe9abSzNJCqAmw7sexeuSHwSKGdggyrPjkVQA,35122
4
+ soar_sdk/actions_manager.py,sha256=8IYOi2k8i9LHXEhQVZ0Ig3IS1gD3iAmOJ9q0bi14g-o,7179
5
+ soar_sdk/app.py,sha256=2bUWx1BgWS9Kwkp0aUzVWM8LTdVUq1JI2eXR0LhwEMU,37092
6
6
  soar_sdk/app_cli_runner.py,sha256=K1ATWyGs0iNgPfIjMthsN72laOXqXCFZNEXfuzAMOM4,11645
7
7
  soar_sdk/app_client.py,sha256=hbe1R2QwXDmoS4959a-ay9oylD1Qk-oPJvJRnxvICz0,6281
8
- soar_sdk/asset.py,sha256=au_P8YS5SUJaZm98DJVpmHlX5BhBhAPnpoYnmj095cU,12236
9
- soar_sdk/asset_state.py,sha256=Qj9Ku9OyAr5rL7N_ifpaEdpA27eu5qggSIbUob2L_VI,2101
8
+ soar_sdk/asset.py,sha256=CUCFjUVAawrk3hyGvQn_qNApqJx8J4VxzD--iGEE2pc,12123
9
+ soar_sdk/asset_state.py,sha256=qh4n8IoabVObIZXRPyM0zznwC5LcJpbADcybmDdQABc,2318
10
10
  soar_sdk/async_utils.py,sha256=Dz7RagIRjyIagA9vivHWSb18S96J2WOuDB8B5Zy64AE,1428
11
11
  soar_sdk/colors.py,sha256=--i_iXqfyITUz4O95HMjfZQGbwFZ34bLmBhtfpXXqlQ,1095
12
12
  soar_sdk/compat.py,sha256=N4bG1wqISICV92K1jLx7v5JGrHC08Bdn3Gx3Cx1lEmE,3062
@@ -32,6 +32,12 @@ soar_sdk/app_templates/basic_app/logo.svg,sha256=_JTop6spn5oPWPk-w6Tzumx_FTSBanO
32
32
  soar_sdk/app_templates/basic_app/logo_dark.svg,sha256=PTxIs_1CKK9ZY3v-K1QoGwaUng9ZUL2MhUeO2jeHu_0,291
33
33
  soar_sdk/app_templates/basic_app/uv.lock,sha256=AfgaIBg88KH-0iyXpCXacXAwHYKm0c-on2gWXjV9L-Y,80216
34
34
  soar_sdk/app_templates/basic_app/src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
+ soar_sdk/auth/__init__.py,sha256=Z6pSzWMd49aY4H-9IxdCeWxCEC8_K4kT7i4buyv03sw,896
36
+ soar_sdk/auth/client.py,sha256=QmQ2dcTpke7hI7aff4usgbM_LQ0Prw2PILEb56jQFII,17077
37
+ soar_sdk/auth/factories.py,sha256=lNDxhrJtQpEEVV_dGEarwHHdU64oNPCxcZTrv0L4GEY,3994
38
+ soar_sdk/auth/flows.py,sha256=ahSOdr129s-GQjGUK8e2kNaLmjLTRzkxEJG3IwUws78,5457
39
+ soar_sdk/auth/httpx_auth.py,sha256=b_ldXjRxqZZiCjCnR3eZvUiDWTqYZShZKhu9NBn1Vns,3009
40
+ soar_sdk/auth/models.py,sha256=vSgRqHxyga2W8FGQZLjqHtk0yEQHkbEREshDePfJXgo,2856
35
41
  soar_sdk/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
42
  soar_sdk/cli/cli.py,sha256=9OqjVFPoIBdsO-Zmijcf0yBnjk3ggBPEOaCxKfvvw6g,998
37
43
  soar_sdk/cli/path_utils.py,sha256=aJy3fzw2MIU-WHZn21H021B4sjGkKxW61l02QIFVGJ8,1087
@@ -43,7 +49,7 @@ soar_sdk/cli/manifests/cli.py,sha256=cly5xVdj4bBIdZVMQPIWTXRgUfd1ON3qKO-76Fwql18
43
49
  soar_sdk/cli/manifests/deserializers.py,sha256=kwgPAMgUEXtIn4AuQOh1nkLfWFqe4qnYPZ1czB-FQTU,16516
44
50
  soar_sdk/cli/manifests/processors.py,sha256=soiRTbfLQuetstt1Xk7vKmWzOUhgVON5JxjWMvnGN7w,5141
45
51
  soar_sdk/cli/manifests/serializers.py,sha256=ulpq3nS8g1YrIP371XoQC3_kpz-9v2Ln_mqPyMtpWn8,3632
46
- soar_sdk/cli/package/cli.py,sha256=_SlMNfqEyjAAWQy8AU4KhDpqxbV0jnjTQX1c3uULAtk,9945
52
+ soar_sdk/cli/package/cli.py,sha256=hdpVBRvVGWaYDYhpDILlA7LRhunfElLbNdh21jviKrM,10087
47
53
  soar_sdk/cli/package/utils.py,sha256=fl6PMcrdC2zA7A16byQuxxPyAI2Z-BqBLfLlF2ZNnQ4,1712
48
54
  soar_sdk/cli/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
55
  soar_sdk/cli/test/cli.py,sha256=iDrthN8L7B1RplLhq0EI69MndaOhvAXn7bqv3XzlfpM,7655
@@ -83,7 +89,7 @@ soar_sdk/models/vault_attachment.py,sha256=sdRnQdPiwgaZDojpap4ohH7u1Q5TYGP-drs8K
83
89
  soar_sdk/models/view.py,sha256=BUuz6VVVe78hg7irGgZCbvBcycOmuPqplkagdi3T4Dg,779
84
90
  soar_sdk/shims/phantom/action_result.py,sha256=Nddc9oswAfHU7I2q0pLm3HZ2YiLUQZUEIqqAjToZWnM,1606
85
91
  soar_sdk/shims/phantom/app.py,sha256=uvE7Hsz5KudARpwaye7cx9lEOTMmPsJlZdunmkV8_lY,303
86
- soar_sdk/shims/phantom/base_connector.py,sha256=59Eh9D6cFOfVNf6OfN6Q8ds-QnnUp8zO_S3HlgDmQo8,4872
92
+ soar_sdk/shims/phantom/base_connector.py,sha256=2Tbh5nHnP6euilRiQL9NaVtmrgQIiY0eIkkzu-ah8_Y,5152
87
93
  soar_sdk/shims/phantom/connector_result.py,sha256=b6yrR1uUXBhfwpUf8HzESItPgfaiHxNTXF8FaGdQNsk,640
88
94
  soar_sdk/shims/phantom/consts.py,sha256=eq6AIuDhb2Z-CJORwv98D3JbcIOW8CC673zx5dNPFKU,404
89
95
  soar_sdk/shims/phantom/encryption_helper.py,sha256=20VqqSFuftjB8bMriP6mjgvYWpYqYZyokYzq_aydkqU,1503
@@ -110,8 +116,8 @@ soar_sdk/views/components/pie_chart.py,sha256=LVTeHVJN6nf2vjUs9y7PDBhS0U1fKW750l
110
116
  soar_sdk/webhooks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
111
117
  soar_sdk/webhooks/models.py,sha256=j3kbvYmcOlcj3gQYKtrv7iS-lDavMKYNLdCNMy_I2Hc,4542
112
118
  soar_sdk/webhooks/routing.py,sha256=OjezhuAb8wzW0MnbGSnIWeAH3uJcu-Sb7s3w9zoiPVM,6873
113
- splunk_soar_sdk-3.6.1.dist-info/METADATA,sha256=1Y4gKBFsYMfCL1vPPxCCLCDvz16iQ9EmjhdjY0prJg0,7478
114
- splunk_soar_sdk-3.6.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
115
- splunk_soar_sdk-3.6.1.dist-info/entry_points.txt,sha256=CgBjo2ZWpYNkt9TgvToL26h2Tg1yt8FbvYTb5NVgNuc,51
116
- splunk_soar_sdk-3.6.1.dist-info/licenses/LICENSE,sha256=gNCGrGhrSQb1PUzBOByVUN1tvaliwLZfna-QU2r2hQ8,11345
117
- splunk_soar_sdk-3.6.1.dist-info/RECORD,,
119
+ splunk_soar_sdk-3.8.0.dist-info/METADATA,sha256=Sulxm0L7rnkY_17tCaITHItSmYiHA2J26kYLZ2ymxcw,7544
120
+ splunk_soar_sdk-3.8.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
121
+ splunk_soar_sdk-3.8.0.dist-info/entry_points.txt,sha256=CgBjo2ZWpYNkt9TgvToL26h2Tg1yt8FbvYTb5NVgNuc,51
122
+ splunk_soar_sdk-3.8.0.dist-info/licenses/LICENSE,sha256=gNCGrGhrSQb1PUzBOByVUN1tvaliwLZfna-QU2r2hQ8,11345
123
+ splunk_soar_sdk-3.8.0.dist-info/RECORD,,