ó
    Ú°›j\8  ã                   ó$  • S r SSKrSSKrSSKrSSKJr  SSKJrJrJ	r	  SSK
JrJr  SSKJr  SSKJr  SSKJrJr  SS	KJr  S
r\" S5      rSrSrSr\R:                  " S5      /rSr " S S\ 5      r!S\"4S jr#S\$S\"4S jr%S\$S\&S\\$   4S jr'S\&4S jr(S\$SS4S jr)S\$S\$4S jr* " S S\5      r+S \$S\4S! jr,S"\SS4S# jr-S"\SS4S$ jr.S%\	\&   SS4S& jr/S'\&S%\	\&   S\\0   4S( jr1S\$4S) jr2S*S+SS\S,.S\$S-\"S.\&S/\	\3   S%\	\&   S0\&4S1 jjr4g)2a’  
PodWorker | serverless | utils | rp_ssrf.py

SSRF-safe fetching of user-supplied (job-input) URLs.

Job input carries arbitrary URLs that the worker downloads. Without restriction
a job can point the worker at cloud instance-metadata (169.254.169.254) or other
non-public addresses (CWE-918). This module blocks any destination that is not a
globally-routable IP, pins connections to a pre-validated address to defeat DNS
rebinding, re-validates redirect hops, and caps download size.

Pinning requires this process to open the socket, so a URL that the environment
routes through a proxy is refused rather than fetched unprotected; see
_refuse_if_proxied.
é    N)Úsuppress)ÚIteratorÚListÚOptional)ÚurljoinÚurlparse)ÚConnectionError)ÚHTTPAdapter)Úget_environ_proxiesÚselect_proxy)ÚSyncClientSession)ÚhttpÚhttps)i-  i.  i/  i3  i4  é   ÚRUNPOD_MAX_DOWNLOAD_BYTESl        z100.64.0.0/10Ú"RUNPOD_ALLOW_PRIVATE_DOWNLOAD_URLSc                   ó   • \ rS rSrSrSrg)Ú	SSRFErroré0   aq  
Raised when a URL is rejected as unsafe (non-public destination, blocked
scheme, or oversized body).

Subclasses ValueError and deliberately NOT requests.RequestException so it
bypasses the download path's backoff retry and `except RequestException`
handler: a blocked internal URL must fail loudly, not be retried or silently
reported as an ordinary failed download.
© N)Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Ú__static_attributes__r   ó    Ú\/home/mande/repo/quber/.venv/lib/python3.13/site-packages/runpod/serverless/utils/rp_ssrf.pyr   r   0   s   † ôr   r   Úreturnc                  óˆ   • [         R                  R                  [        S5      R	                  5       R                  5       S;  $ )NÚ )Ú1ÚtrueÚyes)ÚosÚenvironÚgetÚ_ALLOW_PRIVATE_ENVÚstripÚlowerr   r   r   Ú_ssrf_protection_enabledr+   <   s7   € Ü�:‰:�>‰>Ô,¨bÓ1×7Ñ7Ó9×?Ñ?ÓAð Jñ ð r   Úipc                 óš  ^•  [         R                  " U 5      m[        TSS5      nUb  Um[	        U4S j[
         5       5      (       a  gTR                  (       dU  TR                  (       dD  TR                  (       d3  TR                  (       d"  TR                  (       d  TR                  (       a  gTR                  $ ! [         a     gf = f)a  
Return True only for a globally-routable unicast IP address.

Blocks loopback, link-local (incl. 169.254.169.254 metadata), RFC1918,
CGNAT, reserved, multicast, unspecified, IPv6 unique-local/link-local, and
IPv4-mapped IPv6 forms of any of the above. Unparseable input is unsafe.
FÚipv4_mappedNc              3   ó.   >#   • U  H
  nTU;   v •  M     g 7f©Nr   )Ú.0ÚnetworkÚaddresss     €r   Ú	<genexpr>Ú"is_safe_address.<locals>.<genexpr>V   s   øé € Ð
EÒ-D 'ˆ7�gÖÒ-Dùs   ƒ)Ú	ipaddressÚ
ip_addressÚ
ValueErrorÚgetattrÚanyÚ_EXTRA_BLOCKED_NETWORKSÚ
is_privateÚis_loopbackÚis_link_localÚis_reservedÚis_multicastÚis_unspecifiedÚ	is_global)r,   Úmappedr3   s     @r   Úis_safe_addressrD   D   s£   ø€ ðÜ×&Ò& rÓ*ˆô
 �W˜m¨TÓ2€FØÑØˆä
Ô
EÕ-DÓ
E×EÑEØð 	××Ø××Ø× × Ø××Ø××Ø×!×!àà×ÑÐøô+ ó Ùðús   ƒB= Â=
C
Ã	C
ÚhostÚportc                 ó¢  •  [         R                  " X[         R                  S9n/ nU H#  nUS   S   nXd;  d  M  UR                  U5        M%     U(       d  [	        SU < 35      e[        5       (       a+  U H%  n[        U5      (       a  M  [	        SU < S	U 35      e   U$ ! [         R                   a  n[	        SU < SU 35      UeSnAff = f)
ao  
Resolve `host` and return its IPs, failing closed on any unsafe address.

Raises SSRFError if the host cannot be resolved, resolves to nothing, or
resolves to any non-global address (a host resolving to both a public and a
private IP is treated as hostile). Validation is skipped when the escape
hatch env var is set, but resolution still occurs so callers can pin.
)Útypezcould not resolve host ú: Né   r   zno addresses resolved for host zhost z  resolves to non-public address )ÚsocketÚgetaddrinfoÚSOCK_STREAMÚgaierrorr   Úappendr+   rD   )rE   rF   ÚinfosÚerrÚipsÚinfor,   s          r   Úresolve_and_validaterT   f   sÜ   € ðLÜ×"Ò" 4´F×4FÑ4FÑGˆð €CÛˆØ�!‰W�Q‰ZˆØ�=Ø�J‰J�rŽNñ ö
 ÜÐ9¸$¹ÐBÓCÐCä×!Ñ!ÛˆBÜ" 2×&Ó&ÜØ˜D™8Ð#CÀBÀ4ÐHóð ñ ð €Jøô' �?‰?ó LÜÐ1°$±¸¸C¸5ÐAÓBÈÐKûðLús   ‚#B" Â"CÂ6C	Ã	Cc                  óº   • [         R                  R                  [        5      n U c  [        $  [        U 5      nUS:”  a  U$ [        $ ! [         a	    [        s $ f = f)z4Configured per-download byte cap; defaults to 5 GiB.r   )r%   r&   r'   Ú_MAX_BYTES_ENVÚ_DEFAULT_MAX_BYTESÚintr8   )ÚrawÚvalues     r   Úmax_download_bytesr[   ‡   sX   € ä
�*‰*�.‰.œÓ
(€CØ
�{Ü!Ð!ð"Ü�C“ˆð ˜A“Iˆ5Ð5Ô#5Ð5øô ó "Ü!Ò!ð"ús   ®A ÁAÁAÚurlc           	      óŽ   • [        5       (       d  g[        U [        U 5      5      nU(       a  [        SU  SU S[         S35      eg)a:  
Reject `url` when the environment routes it through a proxy.

Pinning only holds if this process opens the socket: through a proxy the
request carries the hostname and the proxy resolves it, so the validated IP
is not what gets dialed and the rebind protection silently stops applying.
Failing here turns that into a loud, actionable error instead of a false
sense of protection.

NO_PROXY exclusions are honored (such a host is fetched directly, so pinning
still holds), and the escape hatch env var allows the proxied fetch for
deployments that accept the trade-off.
Nzrefusing to fetch z through proxy z1: IP pinning cannot be enforced via a proxy; set z=true to allow it)r+   r   r   r   r(   )r\   Úproxys     r   Ú_refuse_if_proxiedr_   “   sX   € ô $×%Ñ%Øä˜Ô1°#Ó6Ó7€EÞÜØ    _°U°Gð <)Ü);Ð(<Ð<MðOó
ð 	
ð r   c                 óš   • [        U 5      nUR                  =(       d    SnSU;   a  SU S3nUR                  b  U SUR                   3$ U$ )aq  
Build the Host header authority for `url`.

The pinned adapter dials an IP but must present the original authority, so
this reconstructs it from the URL rather than the socket. urlparse strips
the brackets from an IPv6 literal, and RFC 7230 requires them in Host, so
they are restored: without this an IPv6-literal URL yields a malformed
header such as "2606::1:443".
r!   Ú:Ú[Ú])r   ÚhostnamerF   )r\   ÚparsedrE   s      r   Ú_host_header_forrf   ¬   sS   € ô �c‹]€FØ�?‰?× ˜b€DØ
ˆdƒ{Ø�4�&˜ˆ{ˆØ‡{�{ÑØ��q˜Ÿ™˜Ð&Ð&Ø€Kr   c                   ón   ^ • \ rS rSrSrS\4U 4S jjrU 4S jrS\4S jrSU 4S jjr	SU 4S	 jjr
S
rU =r$ )ÚPinnedIPAdapteré¿   a�  
Requests adapter that dials a pre-validated IP instead of re-resolving the
URL host, defeating DNS rebinding.

The superclass builds the connection pool with its normal TLS/verification
configuration; this adapter only redirects the socket to the pinned IP and
keeps the TLS SNI, certificate hostname check, and Host header bound to the
original URL host. It never alters certificate verification.
Ú	pinned_ipc                 ó2   >• Xl         [        TU ]  " S0 UD6  g )Nr   )Ú
_pinned_ipÚsuperÚ__init__)Úselfrj   ÚkwargsÚ	__class__s      €r   rn   ÚPinnedIPAdapter.__init__Ê   s   ø€ Ø#ŒÜ‰ÒÑ"˜6Ó"r   c                 ój   >• [        UR                  5      UR                  S'   [        TU ]  " U40 UD6$ )NÚHost)rf   r\   Úheadersrm   Úsend)ro   Úrequestrp   rq   s      €r   rv   ÚPinnedIPAdapter.sendÎ   s.   ø€ Ü"2°7·;±;Ó"?ˆ�‰˜ÑÜ‰wŠ|˜GÑ. vÑ.Ð.r   r\   c                 óø   • [        U5      nUR                  S:X  aM  UR                  Ul        [	        UR
                  =(       d    0 5      Ul        UR                  UR
                  S'   U R                  Ul        U$ )Nr   Úserver_hostname)r   Úschemerd   Úassert_hostnameÚdictÚconn_kwrl   rE   )ro   Úpoolr\   re   s       r   Ú_pinÚPinnedIPAdapter._pinÒ   s[   € Ü˜#“ˆØ�=‰=˜GÓ#Ø#)§?¡?ˆDÔ Ü §¡× 2°Ó3ˆDŒLØ.4¯o©oˆD�L‰LÐ*Ñ+Ø—O‘OˆŒ	Øˆr   c                 óV   >• [         TU ]  XX4S9nU R                  XQR                  5      $ )N)ÚproxiesÚcert)rm   Úget_connection_with_tls_contextr€   r\   )ro   rw   Úverifyrƒ   r„   r   rq   s         €r   r…   Ú/PinnedIPAdapter.get_connection_with_tls_contextÛ   s1   ø€ Ü‰wÑ6Ø Wð 7ð 
ˆð �y‰y˜Ÿ{™{Ó+Ð+r   c                 ó@   >• [         TU ]  XS9nU R                  X15      $ )N)rƒ   )rm   Úget_connectionr€   )ro   r\   rƒ   r   rq   s       €r   r‰   ÚPinnedIPAdapter.get_connectioná   s$   ø€ Ü‰wÑ% cÐ%Ð;ˆØ�y‰y˜Ó#Ð#r   )rl   )NNr0   )r   r   r   r   r   Ústrrn   rv   r€   r…   r‰   r   Ú__classcell__)rq   s   @r   rh   rh   ¿   s4   ø† ñð# #÷ #õ/ð˜cô ÷,÷$õ $r   rh   rj   c                 óx   • [        5       n[        U 5      nUR                  SU5        UR                  SU5        U$ )z:A session whose http(s) connections dial only `pinned_ip`.zhttp://zhttps://)r   rh   Úmount)rj   ÚsessionÚadapters      r   Ú_build_pinned_sessionr‘   æ   s5   € äÓ!€GÜ˜iÓ(€GØ‡M�M�)˜WÔ%Ø‡M�M�*˜gÔ&Ø€Nr   r�   c                 óv   • [        [        5         U R                  5         SSS5        g! , (       d  f       g= f)a¡  
Close a pinned session best-effort, tolerating teardown errors.

This runs only on cleanup paths (a failed request, a closing response) where
the caller's original exception is the meaningful one. A failure while
closing the session's connection pools must not mask that exception, so it is
suppressed rather than raised. Session.close() is idempotent and does no
actionable I/O, so nothing worth surfacing is lost.
N)r   Ú	ExceptionÚclose)r�   s    r   Ú_close_session_quietlyr•   ï   s!   € ô 
”)Õ	Ø�‰Œ÷ 
×	Ö	ús   �*ª
8c                 ó:   ^^• U R                   mUU4S jnX l         g)a¹  
Tie `session` cleanup to `response.close()`.

Each safe_get() call owns a single-use session; the caller only holds the
response. Closing a requests.Response releases its connection but not the
session (its adapters and connection pools), so without this the session
leaks one set of pooled sockets/FDs per download in a long-lived worker.
Wrapping close() makes `with safe_get(...) as r:` (and any direct
response.close()) tear down both.
c                  óN   >•  T" U 0 UD6  [        T5        g ! [        T5        f = fr0   )r•   )Úargsrp   Úoriginal_closer�   s     €€r   Ú
close_bothÚ-_bind_session_to_response.<locals>.close_both
  s'   ø€ ð	,Ù˜DÐ+ FÒ+ä" 7Õ+øÔ" 7Õ+ús   ƒ —$N)r”   )Úresponser�   rš   r™   s    ` @r   Ú_bind_session_to_responser�   ý   s   ù€ ð —^‘^€Nö,ð  …Nr   Ú	max_bytesc                 óÐ   • Uc  g U R                   R                  S5      nUc  g  [        U5      nX1:”  a"  U R	                  5         [        SU SU S35      eg ! [         a     g f = f)NzContent-Lengthzdownload exceeds size cap: z > ú bytes)ru   r'   rX   r8   r”   r   )rœ   rž   ÚdeclaredÚsizes       r   Ú_enforce_content_lengthr£     s   € ØÑØØ×Ñ×#Ñ#Ð$4Ó5€HØÑØðÜ�8‹}ˆð ÓØ�‰ÔÜÐ5°d°V¸3¸y¸kÈÐPÓQÐQð øô ó Ùðús   ¥A Á
A%Á$A%Ú
chunk_sizec              #   óÀ   #   • SnU R                  US9 HE  nU(       d  M  U[        U5      -  nUb$  X2:”  a  U R                  5         [        SU S35      eUv •  MG     g7f)z”
Yield the response body in chunks, aborting with SSRFError if the running
total exceeds `max_bytes`. Guards against a lying/absent Content-Length.
r   )r¤   Nzdownload exceeds size cap: >r    )Úiter_contentÚlenr”   r   )rœ   r¤   rž   ÚtotalÚchunks        r   Úiter_content_cappedrª   "  sg   é € ð
 €EØ×&Ñ&°*Ð&Ó=ˆÞÙØ”�U“ÑˆØÑ  UÓ%6Ø�N‰NÔÜÐ:¸9¸+ÀVÐLÓMÐMØŒò >ùs   ‚AAc          	      óö   • SnU  H%  n[        U5      n UR                  UUSUUS9nXx4s  $    Uc  [        SU 35      eUe! [         a  n	[        U5        U	n Sn	A	MY  Sn	A	f[         a    [        U5        e f = f)uÎ  
GET `url` pinned to each validated IP in turn, returning the first that connects.

A hostname commonly resolves to several addresses (dual-stack AAAA + A), and
the first one may be unroutable from the worker â€” e.g. an IPv6 address on a
pod with no IPv6 route. Plain requests walks the address list; pinning to a
single IP would turn that into a hard download failure, so this preserves
the fallback while keeping every candidate pre-validated.

Only connection-level errors advance to the next address; an HTTP response
(any status) or any other exception is returned/raised immediately. Returns
the (session, response) pair so the caller owns teardown. Raises the last
connection error if no address is reachable.
NF)ru   Úallow_redirectsÚstreamÚtimeoutz&no candidate addresses to connect to: )r‘   r'   ÚRequestsConnectionErrorr•   ÚBaseExceptionr   )
rR   r\   ru   r­   r®   Ú
last_errorr,   r�   rœ   rQ   s
             r   Ú_get_via_first_reachable_ipr²   2  s¥   € ð €JÛˆÜ'¨Ó+ˆð	Ø—{‘{ØØØ %ØØð #ð ˆHð Ð Ò ñ# ð& ÑÜÐ@ÀÀÐFÓGÐGØ
Ðøô 'ó 	Ü" 7Ô+ØˆJÝûÜó 	Ü" 7Ô+Øð	ús   ”AÁ
A8ÁAÁA8Té   )r­   r®   ru   rž   Úmax_redirectsr­   r®   ru   r´   c          	      óø  • Uc
  [        5       nU n[        US-   5       GH8  n[        U5      nUR                  [        ;  a  [        SUR                  < SU 35      eUR                  (       d  [        SU 35      e[        U5        UR                  =(       d    UR                  S:X  a  SOSn	[        UR                  U	5      n
[        X¦X1US9u  p¼UR                  [        ;   aZ  UR                  R                  S	5      n UR                  5         [!        U5        U(       d  [        S
U 35      e[#        Xm5      nGM!  [%        XË5        ['        XÄ5        Us  $    [        SU SU  35      e! [!        U5        f = f)aŸ  
Fetch a user-supplied URL with SSRF protections.

Enforces an http(s) scheme allowlist, resolves and validates the host to a
public IP, pins the connection to that IP, and re-validates every redirect
hop. Rejects a body whose declared size exceeds the cap. Returns the
(streamed) requests.Response for the caller to consume; use
iter_content_capped to enforce the cap while reading. Raises SSRFError on
any block.
é   zblocked URL scheme rI   zURL has no host: r   i»  éP   )ru   r­   r®   ÚLocationz"redirect without Location header: zexceeded maximum redirects (z): )r[   Úranger   r{   Ú_ALLOWED_SCHEMESr   rd   r_   rF   rT   r²   Ústatus_codeÚ_REDIRECT_STATUSru   r'   r”   r•   r   r�   r£   )r\   r­   r®   ru   rž   r´   ÚcurrentÚ_re   rF   rR   r�   rœ   Úlocations                 r   Úsafe_getrÀ   Z  sa  € ð& ÑÜ&Ó(ˆ	à€GÜ�= 1Ñ$×%ˆÜ˜'Ó"ˆØ�=‰=Ô 0Ó0ÜÐ1°&·-±-Ñ1BÀ"ÀWÀIÐNÓOÐOØ��ÜÐ/°¨yÐ9Ó:Ð:Ü˜7Ô#à�{‰{×G f§m¡m°wÓ&>™sÀBˆÜ" 6§?¡?°DÓ9ˆä7Ø 'À'ñ
Ñˆð ×ÑÔ#3Ó3Ø×'Ñ'×+Ñ+¨JÓ7ˆHð0Ø—‘Ô ä& wÔ/ÞÜÐ"DÀWÀIÐ NÓOÐOÜ˜gÓ0ˆGÚô 	" (Ô4Ü Ô4ØŠñ= &ô@ Ð2°=°/ÀÀSÀEÐJÓ
KÐKøô ' wÕ/ús   ÄE,Å,E9)5r   r6   r%   rK   Ú
contextlibr   Útypingr   r   r   Úurllib.parser   r   Úrequestsr	   r¯   Úrequests.adaptersr
   Úrequests.utilsr   r   Úrunpod.http_clientr   rº   Ú	frozensetr¼   Ú_DEFAULT_MAX_REDIRECTSrV   rW   Ú
ip_networkr;   r(   r8   r   Úboolr+   r‹   rD   rX   rT   r[   r_   rf   rh   r‘   r•   r�   r£   Úbytesrª   r²   r}   rÀ   r   r   r   Ú<module>rÍ      sá  ðñó  Û 	Û Ý ß +Ñ +ß *å ?Ý )ß <å 0à$Ð ÙÐ6Ó7Ð ØÐ ð -€Ø"Ð ð %×/Ò/°Ó@ÐAÐ ð :Ð ô	�
ô 	ð $ô ð˜ð  ô ðD˜sð ¨#ð °$°s±)ô ðB	6˜Cô 	6ð
˜Cð 
 Dô 
ð2˜#ð  #ô ô&$$�kô $$ðN Sð Ð->ô ðÐ$5ð ¸$ô ð Ð1Bð  Àtô  ð,R°¸#±ð RÀ4ô Rð¨cð ¸hÀs¹mð ÐPXÐY^ÑP_ô ð %¨#ô %ðV ØØ"Ø#Ø/ò7LØ	ð7Lð ð7Lð ð	7Lð
 �d‰^ð7Lð ˜‰}ð7Lð ö7Lr   