langgraph-api 0.9.0.dev3__py3-none-any.whl → 0.9.0.dev4__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.
- langgraph_api/__init__.py +1 -1
- langgraph_api/config/schemas.py +26 -2
- langgraph_api/webhook.py +25 -3
- {langgraph_api-0.9.0.dev3.dist-info → langgraph_api-0.9.0.dev4.dist-info}/METADATA +1 -1
- {langgraph_api-0.9.0.dev3.dist-info → langgraph_api-0.9.0.dev4.dist-info}/RECORD +8 -8
- {langgraph_api-0.9.0.dev3.dist-info → langgraph_api-0.9.0.dev4.dist-info}/WHEEL +0 -0
- {langgraph_api-0.9.0.dev3.dist-info → langgraph_api-0.9.0.dev4.dist-info}/entry_points.txt +0 -0
- {langgraph_api-0.9.0.dev3.dist-info → langgraph_api-0.9.0.dev4.dist-info}/licenses/LICENSE +0 -0
langgraph_api/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.9.0.
|
|
1
|
+
__version__ = "0.9.0.dev4"
|
langgraph_api/config/schemas.py
CHANGED
|
@@ -386,7 +386,26 @@ class WebhookUrlPolicy(TypedDict, total=False):
|
|
|
386
386
|
max_url_length: int
|
|
387
387
|
"""Maximum permitted URL length in characters; longer inputs are rejected early."""
|
|
388
388
|
disable_loopback: bool
|
|
389
|
-
"""Disallow
|
|
389
|
+
"""Disallow loopback-flavored webhook targets when true. Defaults to true.
|
|
390
|
+
|
|
391
|
+
Covers all of:
|
|
392
|
+
- Relative URLs (e.g. ``/internal/hook``) that would dispatch via the
|
|
393
|
+
in-process ASGI loopback transport at ``root_path="/noauth"``. These
|
|
394
|
+
bypass authentication and were the auth-bypass primitive behind
|
|
395
|
+
GHSA-q3v5-r5ch-p57j.
|
|
396
|
+
- Absolute URLs whose hostname is ``localhost`` / ``localhost.localdomain``
|
|
397
|
+
/ ``host.docker.internal``.
|
|
398
|
+
- Absolute URLs that DNS-resolve into the IPv4 loopback range
|
|
399
|
+
(``127.0.0.0/8``) or IPv6 loopback (``::1/128``) — including
|
|
400
|
+
DNS-rebinding-style attacks where an arbitrary hostname resolves to
|
|
401
|
+
a loopback IP.
|
|
402
|
+
|
|
403
|
+
Operators who intentionally route webhooks to in-process routes
|
|
404
|
+
(e.g. for local development with ``langgraph dev``, or single-host
|
|
405
|
+
docker-compose setups dispatching to ``host.docker.internal``) can
|
|
406
|
+
set this to ``false`` to restore the pre-GHSA behavior. Only do this
|
|
407
|
+
when you control all routes that loopback webhooks can reach.
|
|
408
|
+
"""
|
|
390
409
|
disable_private_ips: bool
|
|
391
410
|
"""Block RFC 1918 / CGN private IP ranges as webhook targets when true.
|
|
392
411
|
|
|
@@ -426,7 +445,12 @@ def _validate_url_policy(
|
|
|
426
445
|
if "max_url_length" not in policy:
|
|
427
446
|
policy["max_url_length"] = 2048
|
|
428
447
|
if "disable_loopback" not in policy:
|
|
429
|
-
|
|
448
|
+
# Secure default — covers GHSA-q3v5-r5ch-p57j (relative-URL ASGI
|
|
449
|
+
# loopback auth bypass) and the broader SSRF surface that the same
|
|
450
|
+
# flag governs via SSRFPolicy.block_localhost. Mirrored at the
|
|
451
|
+
# webhook.py read site so deployments without any webhooks block
|
|
452
|
+
# also get the safe default.
|
|
453
|
+
policy["disable_loopback"] = True
|
|
430
454
|
if "disable_private_ips" not in policy:
|
|
431
455
|
policy["disable_private_ips"] = False
|
|
432
456
|
return policy
|
langgraph_api/webhook.py
CHANGED
|
@@ -79,7 +79,14 @@ def _get_webhook_config() -> _WebhookConfig:
|
|
|
79
79
|
exact_allowed = frozenset(exact_hosts)
|
|
80
80
|
|
|
81
81
|
disable_private_ips = bool(policy_cfg.get("disable_private_ips", False))
|
|
82
|
-
|
|
82
|
+
# Loopback webhooks are denied by default (covers relative URLs that
|
|
83
|
+
# would dispatch through the in-process ASGI client at root_path=/noauth,
|
|
84
|
+
# plus localhost / 127.x / ::1 / host.docker.internal absolute URLs,
|
|
85
|
+
# plus any hostname that DNS-resolves into the loopback range). This is
|
|
86
|
+
# the fix for GHSA-q3v5-r5ch-p57j: relative-URL webhooks were the auth
|
|
87
|
+
# bypass primitive, and the localhost/loopback IP variants are the
|
|
88
|
+
# broader SSRF surface that the same flag governs via SSRFPolicy.
|
|
89
|
+
disable_loopback = bool(policy_cfg.get("disable_loopback", True))
|
|
83
90
|
|
|
84
91
|
return _WebhookConfig(
|
|
85
92
|
allowed_domains=allowed_domains,
|
|
@@ -111,11 +118,26 @@ async def validate_webhook_url_or_raise(url: str) -> None:
|
|
|
111
118
|
if len(url) > wh.max_url_length:
|
|
112
119
|
raise HTTPException(status_code=422, detail="Webhook URL too long")
|
|
113
120
|
|
|
114
|
-
# Relative loopback URL (internal route) —
|
|
121
|
+
# Relative loopback URL (internal route) — dispatched via an in-process
|
|
122
|
+
# ASGI client that mounts under root_path="/noauth", which the auth
|
|
123
|
+
# middleware treats as an auth-bypass marker. Denied by default so a
|
|
124
|
+
# user-supplied webhook URL cannot be turned into an unauthenticated
|
|
125
|
+
# call against the server's own routes (GHSA-q3v5-r5ch-p57j). Operators
|
|
126
|
+
# who intentionally route webhooks to in-process routes can opt back in
|
|
127
|
+
# by setting webhooks.url.disable_loopback to false.
|
|
115
128
|
if url.startswith("/"):
|
|
116
129
|
if wh.disable_loopback:
|
|
117
130
|
raise HTTPException(
|
|
118
|
-
status_code=422,
|
|
131
|
+
status_code=422,
|
|
132
|
+
detail=(
|
|
133
|
+
"Loopback webhooks (relative URLs and localhost) are "
|
|
134
|
+
"disabled by default. They bypass authentication via the "
|
|
135
|
+
"in-process ASGI transport and can be abused to invoke "
|
|
136
|
+
"internal routes as an unauthenticated caller. Set "
|
|
137
|
+
"webhooks.url.disable_loopback to false (in langgraph.json "
|
|
138
|
+
"or via LANGGRAPH_WEBHOOKS) to opt back in — only do so "
|
|
139
|
+
"when you control the mounted routes."
|
|
140
|
+
),
|
|
119
141
|
)
|
|
120
142
|
return
|
|
121
143
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
langgraph_api/__init__.py,sha256=
|
|
1
|
+
langgraph_api/__init__.py,sha256=etrMz_KrjnLluOQExvb90dAxp4ASQ-MKozOsxgHwg8A,27
|
|
2
2
|
langgraph_api/_factory_utils.py,sha256=5JsiJbg_YocVSryN2jwoZTg03-eyymlWMK6sKCmXwz0,5756
|
|
3
3
|
langgraph_api/asgi_transport.py,sha256=XApY3lIWBZTMbbsl8dDJzl0cLGirmAGE0SifqZUnXvs,11896
|
|
4
4
|
langgraph_api/asyncio.py,sha256=c-YE-14N7_AP1GzifsbP14XnhLsmxT2P916KXruerpI,10573
|
|
@@ -32,7 +32,7 @@ langgraph_api/stream.py,sha256=W9lrfDWXmCRDLyF_7s2846DNG_b9_-ijoHEUmW-x990,30346
|
|
|
32
32
|
langgraph_api/stream_v2.py,sha256=gW98dq9HKsCJ2NxmXzsYGcqBwFH2bSgmoFVNN3aVPOs,9897
|
|
33
33
|
langgraph_api/traceblock.py,sha256=GhgAtLhAQ8Z8LHtRQq3par-rfPpRBQp2Q7WBACJq1NI,677
|
|
34
34
|
langgraph_api/validation.py,sha256=XyeKyt7jAICmIlT_b0J0mv2YbwIbNoe4m6zEmfk9gOA,14657
|
|
35
|
-
langgraph_api/webhook.py,sha256=
|
|
35
|
+
langgraph_api/webhook.py,sha256=qXEtkE6orek2POeOQmPRsEarJgXIYp-LBrZB-OwITxc,9572
|
|
36
36
|
langgraph_api/worker.py,sha256=gixTe6SLYqSEARri_Lbii6cT69dMM0iwDC7YKgYgx5E,21160
|
|
37
37
|
langgraph_api/_checkpointer/__init__.py,sha256=ofJTJLGy7Hsuzhj-2dpfDvrDloM0BzlhTzvZOdR9K8U,2223
|
|
38
38
|
langgraph_api/_checkpointer/_adapter.py,sha256=1Mdb3B_bg6EQ_3uJfk6ImiobIiAPdaMQVaruDBzVkE8,20833
|
|
@@ -65,7 +65,7 @@ langgraph_api/auth/langsmith/backend.py,sha256=Y6-VxD7zfV1jzGdjmQ66CgNa3SenLbo3d
|
|
|
65
65
|
langgraph_api/auth/langsmith/client.py,sha256=79kwCVeHU64nsHsxWipfZhf44lM6vfs2nlfTxlJF6LU,4142
|
|
66
66
|
langgraph_api/config/__init__.py,sha256=hGqZ3_B4wPm3ZuH5wrDFHHBmtOH_qYnFvX9ChmsBNZ8,25159
|
|
67
67
|
langgraph_api/config/_parse.py,sha256=VXQPKzqtIsZrRy-nUEBMDESBxXzqFRQNiqsvAZeX3HU,3921
|
|
68
|
-
langgraph_api/config/schemas.py,sha256=
|
|
68
|
+
langgraph_api/config/schemas.py,sha256=rYqu67fZxmtCOU-Zc1s3265KbRbqK8PmfvfwvrAmd-Q,20863
|
|
69
69
|
langgraph_api/encryption/__init__.py,sha256=gaCZ00CocSbqSqrDn6XJHaSp2CZCnC8qnrD9G4fbzyI,363
|
|
70
70
|
langgraph_api/encryption/aes_json.py,sha256=ExLgrthzVYGP4X51TStU2Td_Sn_3-kXHyk6UKTFmzW4,6139
|
|
71
71
|
langgraph_api/encryption/context.py,sha256=twnDvH3FcC8rlnPOAhI09dLZcxGgMVlplIH2ZpJ-8P4,1121
|
|
@@ -228,8 +228,8 @@ langgraph_grpc_common/proto/errors_pb2.py,sha256=JI6x-vBK1AE7DHZ5DQwN1mZWF6C4xTR
|
|
|
228
228
|
langgraph_grpc_common/proto/errors_pb2.pyi,sha256=rd3-BYUH8V-aO66taL7OOblaLgdrDtf1Vcd38GUoVVM,2181
|
|
229
229
|
langgraph_grpc_common/proto/errors_pb2_grpc.py,sha256=2-LwQ0OPGo-NtC0269q7Fw6GPBxnTLYWq3xP5Eq0_YA,886
|
|
230
230
|
langgraph_grpc_common/proto/errors_pb2_grpc.pyi,sha256=uC9Wnq6uyg488QiONpJ0ba1s_iouQCOYsjd_FDd1XUM,495
|
|
231
|
-
langgraph_api-0.9.0.
|
|
232
|
-
langgraph_api-0.9.0.
|
|
233
|
-
langgraph_api-0.9.0.
|
|
234
|
-
langgraph_api-0.9.0.
|
|
235
|
-
langgraph_api-0.9.0.
|
|
231
|
+
langgraph_api-0.9.0.dev4.dist-info/METADATA,sha256=g_Fq02B-NpB42hrXkLYufVw6c2jlw3s-u-7GE3tfNog,4516
|
|
232
|
+
langgraph_api-0.9.0.dev4.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
233
|
+
langgraph_api-0.9.0.dev4.dist-info/entry_points.txt,sha256=hGedv8n7cgi41PypMfinwS_HfCwA7xJIfS0jAp8htV8,78
|
|
234
|
+
langgraph_api-0.9.0.dev4.dist-info/licenses/LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
|
|
235
|
+
langgraph_api-0.9.0.dev4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|