prefect-client 2.20.10__py3-none-any.whl → 2.20.11__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.
- prefect/blocks/notifications.py +21 -0
- prefect/blocks/webhook.py +8 -1
- prefect/utilities/urls.py +49 -0
- prefect/workers/base.py +6 -0
- {prefect_client-2.20.10.dist-info → prefect_client-2.20.11.dist-info}/METADATA +2 -2
- {prefect_client-2.20.10.dist-info → prefect_client-2.20.11.dist-info}/RECORD +9 -8
- {prefect_client-2.20.10.dist-info → prefect_client-2.20.11.dist-info}/WHEEL +1 -1
- {prefect_client-2.20.10.dist-info → prefect_client-2.20.11.dist-info}/LICENSE +0 -0
- {prefect_client-2.20.10.dist-info → prefect_client-2.20.11.dist-info}/top_level.txt +0 -0
prefect/blocks/notifications.py
CHANGED
@@ -4,6 +4,7 @@ from typing import Dict, List, Optional
|
|
4
4
|
|
5
5
|
from prefect._internal.pydantic import HAS_PYDANTIC_V2
|
6
6
|
from prefect.logging import LogEavesdropper
|
7
|
+
from prefect.utilities.urls import validate_restricted_url
|
7
8
|
|
8
9
|
if HAS_PYDANTIC_V2:
|
9
10
|
from pydantic.v1 import AnyHttpUrl, Field, SecretStr
|
@@ -88,6 +89,26 @@ class AppriseNotificationBlock(AbstractAppriseNotificationBlock, ABC):
|
|
88
89
|
description="Incoming webhook URL used to send notifications.",
|
89
90
|
examples=["https://hooks.example.com/XXX"],
|
90
91
|
)
|
92
|
+
allow_private_urls: bool = Field(
|
93
|
+
default=True,
|
94
|
+
description="Whether to allow notifications to private URLs. Defaults to True.",
|
95
|
+
)
|
96
|
+
|
97
|
+
@sync_compatible
|
98
|
+
async def notify(
|
99
|
+
self,
|
100
|
+
body: str,
|
101
|
+
subject: Optional[str] = None,
|
102
|
+
):
|
103
|
+
if not self.allow_private_urls:
|
104
|
+
try:
|
105
|
+
validate_restricted_url(self.url.get_secret_value())
|
106
|
+
except ValueError as exc:
|
107
|
+
if self._raise_on_failure:
|
108
|
+
raise NotificationError(str(exc))
|
109
|
+
raise
|
110
|
+
|
111
|
+
await super().notify(body, subject)
|
91
112
|
|
92
113
|
|
93
114
|
# TODO: Move to prefect-slack once collection block auto-registration is
|
prefect/blocks/webhook.py
CHANGED
@@ -3,6 +3,7 @@ from typing import Optional
|
|
3
3
|
from httpx import AsyncClient, AsyncHTTPTransport, Response
|
4
4
|
|
5
5
|
from prefect._internal.pydantic import HAS_PYDANTIC_V2
|
6
|
+
from prefect.utilities.urls import validate_restricted_url
|
6
7
|
|
7
8
|
if HAS_PYDANTIC_V2:
|
8
9
|
from pydantic.v1 import Field, SecretStr
|
@@ -38,7 +39,10 @@ class Webhook(Block):
|
|
38
39
|
description="The webhook URL.",
|
39
40
|
examples=["https://hooks.slack.com/XXX"],
|
40
41
|
)
|
41
|
-
|
42
|
+
allow_private_urls: bool = Field(
|
43
|
+
default=True,
|
44
|
+
description="Whether to allow notifications to private URLs. Defaults to True.",
|
45
|
+
)
|
42
46
|
headers: SecretDict = Field(
|
43
47
|
default_factory=lambda: SecretDict(dict()),
|
44
48
|
title="Webhook Headers",
|
@@ -55,6 +59,9 @@ class Webhook(Block):
|
|
55
59
|
Args:
|
56
60
|
payload: an optional payload to send when calling the webhook.
|
57
61
|
"""
|
62
|
+
if not self.allow_private_urls:
|
63
|
+
validate_restricted_url(self.url.get_secret_value())
|
64
|
+
|
58
65
|
async with self._client:
|
59
66
|
return await self._client.request(
|
60
67
|
method=self.method,
|
@@ -0,0 +1,49 @@
|
|
1
|
+
import ipaddress
|
2
|
+
import socket
|
3
|
+
from urllib.parse import urlparse
|
4
|
+
|
5
|
+
|
6
|
+
def validate_restricted_url(url: str):
|
7
|
+
"""
|
8
|
+
Validate that the provided URL is safe for outbound requests. This prevents
|
9
|
+
attacks like SSRF (Server Side Request Forgery), where an attacker can make
|
10
|
+
requests to internal services (like the GCP metadata service, localhost addresses,
|
11
|
+
or in-cluster Kubernetes services)
|
12
|
+
Args:
|
13
|
+
url: The URL to validate.
|
14
|
+
Raises:
|
15
|
+
ValueError: If the URL is a restricted URL.
|
16
|
+
"""
|
17
|
+
|
18
|
+
try:
|
19
|
+
parsed_url = urlparse(url)
|
20
|
+
except ValueError:
|
21
|
+
raise ValueError(f"{url!r} is not a valid URL.")
|
22
|
+
|
23
|
+
if parsed_url.scheme not in ("http", "https"):
|
24
|
+
raise ValueError(
|
25
|
+
f"{url!r} is not a valid URL. Only HTTP and HTTPS URLs are allowed."
|
26
|
+
)
|
27
|
+
|
28
|
+
hostname = parsed_url.hostname or ""
|
29
|
+
|
30
|
+
# Remove IPv6 brackets if present
|
31
|
+
if hostname.startswith("[") and hostname.endswith("]"):
|
32
|
+
hostname = hostname[1:-1]
|
33
|
+
|
34
|
+
if not hostname:
|
35
|
+
raise ValueError(f"{url!r} is not a valid URL.")
|
36
|
+
|
37
|
+
try:
|
38
|
+
ip_address = socket.gethostbyname(hostname)
|
39
|
+
ip = ipaddress.ip_address(ip_address)
|
40
|
+
except socket.gaierror:
|
41
|
+
try:
|
42
|
+
ip = ipaddress.ip_address(hostname)
|
43
|
+
except ValueError:
|
44
|
+
raise ValueError(f"{url!r} is not a valid URL. It could not be resolved.")
|
45
|
+
|
46
|
+
if ip.is_private:
|
47
|
+
raise ValueError(
|
48
|
+
f"{url!r} is not a valid URL. It resolves to the private address {ip}."
|
49
|
+
)
|
prefect/workers/base.py
CHANGED
@@ -137,6 +137,12 @@ class BaseJobConfiguration(BaseModel):
|
|
137
137
|
variables = cls._get_base_config_defaults(
|
138
138
|
variables_schema.get("properties", {})
|
139
139
|
)
|
140
|
+
|
141
|
+
# copy variable defaults for `env` to job config before they're replaced by
|
142
|
+
# deployment overrides
|
143
|
+
if variables.get("env"):
|
144
|
+
job_config["env"] = variables.get("env")
|
145
|
+
|
140
146
|
variables.update(values)
|
141
147
|
|
142
148
|
# deep merge `env`
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: prefect-client
|
3
|
-
Version: 2.20.
|
3
|
+
Version: 2.20.11
|
4
4
|
Summary: Workflow orchestration and management.
|
5
5
|
Home-page: https://www.prefect.io
|
6
6
|
Author: Prefect Technologies, Inc.
|
@@ -22,7 +22,7 @@ Classifier: Topic :: Software Development :: Libraries
|
|
22
22
|
Requires-Python: >=3.8
|
23
23
|
Description-Content-Type: text/markdown
|
24
24
|
License-File: LICENSE
|
25
|
-
Requires-Dist: anyio<
|
25
|
+
Requires-Dist: anyio<4.6.1,>=4.4.0
|
26
26
|
Requires-Dist: asgi-lifespan<3.0,>=1.0
|
27
27
|
Requires-Dist: cachetools<6.0,>=5.3
|
28
28
|
Requires-Dist: cloudpickle<4.0,>=2.0
|
@@ -159,9 +159,9 @@ prefect/blocks/abstract.py,sha256=AiAs0MC5JKCf0Xg0yofC5Qu2TZ52AjDMP1ntMGuP2dY,16
|
|
159
159
|
prefect/blocks/core.py,sha256=_NKdrHvN2f0LdgqX9MYXp0PJQW36e6cs2Za7hVfrCIw,43870
|
160
160
|
prefect/blocks/fields.py,sha256=ANOzbNyDCBIvm6ktgbLTMs7JW2Sf6CruyATjAW61ks0,1607
|
161
161
|
prefect/blocks/kubernetes.py,sha256=IN-hZkzIRvqjd_dzPZby3q8p7m2oUWqArBq24BU9cDg,4071
|
162
|
-
prefect/blocks/notifications.py,sha256=
|
162
|
+
prefect/blocks/notifications.py,sha256=EMW38ZGkQERQHJ32zkqod_GkFIJssq5ra5_rpRZOc_c,29487
|
163
163
|
prefect/blocks/system.py,sha256=aIRiFKlXIQ1sMaqniMXYolFsx2IVN3taBMH3KCThB2I,3089
|
164
|
-
prefect/blocks/webhook.py,sha256=
|
164
|
+
prefect/blocks/webhook.py,sha256=iyPkhgTL04oqz7R2I0jddS2oBWVdp7qvBlRxEbbwueo,2327
|
165
165
|
prefect/client/__init__.py,sha256=yJ5FRF9RxNUio2V_HmyKCKw5G6CZO0h8cv6xA_Hkpcc,477
|
166
166
|
prefect/client/base.py,sha256=YSPeE7hV0NCuD6WzoAACDYGFK4Yq35d26pITZ3elNyY,24669
|
167
167
|
prefect/client/cloud.py,sha256=E54OAFr7bY5IXhhMBdjGwLQiR0eU-WWFoEEiOq2l53I,4104
|
@@ -278,18 +278,19 @@ prefect/utilities/slugify.py,sha256=57Vb14t13F3zm1P65KAu8nVeAz0iJCd1Qc5eMG-R5y8,
|
|
278
278
|
prefect/utilities/templating.py,sha256=t32Gcsvvm8ibzdqXwcWzY7JkwftPn73FiiLYEnQWyKM,13237
|
279
279
|
prefect/utilities/text.py,sha256=eXGIsCcZ7h_6hy8T5GDQjL8GiKyktoOqavYub0QjgO4,445
|
280
280
|
prefect/utilities/timeout.py,sha256=nxmuPxROIT-i8gPffpARuxnxu58H0vkmbjTVIgef0_0,805
|
281
|
+
prefect/utilities/urls.py,sha256=sxv2lG8u3tX_zPZp66qEAFgZmGreG_D_9jGAqqYqaKc,1522
|
281
282
|
prefect/utilities/visualization.py,sha256=9Pc8ImgnBpnszWTFxYm42cmtHjNEAsGZ8ugkn8w_dJk,6501
|
282
283
|
prefect/utilities/schema_tools/__init__.py,sha256=KsFsTEHQqgp89TkDpjggkgBBywoHQPxbx-m6YQhiNEI,322
|
283
284
|
prefect/utilities/schema_tools/hydration.py,sha256=RNuJK4Vd__V69gdQbaWSVhSkV0AUISfGzH_xd0p6Zh0,8291
|
284
285
|
prefect/utilities/schema_tools/validation.py,sha256=zZHL_UFxAlgaUzi-qsEOrhWtZ7EkFQvPkX_YN1EJNTo,8414
|
285
286
|
prefect/workers/__init__.py,sha256=6el2Q856CuRPa5Hdrbm9QyAWB_ovcT2bImSFsoWI46k,66
|
286
|
-
prefect/workers/base.py,sha256=
|
287
|
+
prefect/workers/base.py,sha256=r0kM2EbT69yQsd3L7Lq99IqaBHhZgc6tcpdh3YekSJY,45779
|
287
288
|
prefect/workers/block.py,sha256=aYY__uq3v1eq1kkbVukxyhQNbkknaKYo6-_3tcrfKKA,8067
|
288
289
|
prefect/workers/process.py,sha256=pPtCdA7fKQ4OsvoitT-cayZeh5HgLX4xBUYlb2Zad-Q,9475
|
289
290
|
prefect/workers/server.py,sha256=WVZJxR8nTMzK0ov0BD0xw5OyQpT26AxlXbsGQ1OrxeQ,1551
|
290
291
|
prefect/workers/utilities.py,sha256=VfPfAlGtTuDj0-Kb8WlMgAuOfgXCdrGAnKMapPSBrwc,2483
|
291
|
-
prefect_client-2.20.
|
292
|
-
prefect_client-2.20.
|
293
|
-
prefect_client-2.20.
|
294
|
-
prefect_client-2.20.
|
295
|
-
prefect_client-2.20.
|
292
|
+
prefect_client-2.20.11.dist-info/LICENSE,sha256=MCxsn8osAkzfxKC4CC_dLcUkU8DZLkyihZ8mGs3Ah3Q,11357
|
293
|
+
prefect_client-2.20.11.dist-info/METADATA,sha256=YMdpw_0mas-4W0mXLs8FpuoyxKW-PlSvCxwdHPi0ArA,7392
|
294
|
+
prefect_client-2.20.11.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
295
|
+
prefect_client-2.20.11.dist-info/top_level.txt,sha256=MJZYJgFdbRc2woQCeB4vM6T33tr01TmkEhRcns6H_H4,8
|
296
|
+
prefect_client-2.20.11.dist-info/RECORD,,
|
File without changes
|
File without changes
|