+1
-2
pyproject.toml
+1
-2
pyproject.toml
+8
-8
src/atproto/__init__.py
+8
-8
src/atproto/__init__.py
···
1
1
from dns.resolver import resolve as resolve_dns
2
2
from re import match as regex_match
3
3
from typing import Any
4
-
import requests
4
+
import httpx
5
5
6
6
from .validator import is_valid_authserver_meta
7
7
from ..security import is_safe_url
···
125
125
directory: str = PLC_DIRECTORY,
126
126
) -> dict[str, Any] | None:
127
127
if did.startswith("did:plc:"):
128
-
response = requests.get(f"{directory}/{did}")
129
-
if response.ok:
128
+
response = httpx.get(f"{directory}/{did}")
129
+
if response.is_success:
130
130
return response.json()
131
131
return None
132
132
···
149
149
150
150
assert is_safe_url(pds_url)
151
151
endpoint = f"{pds_url}/.well-known/oauth-protected-resource"
152
-
response = requests.get(endpoint)
152
+
response = httpx.get(endpoint)
153
153
if response.status_code != 200:
154
154
return None
155
155
parsed: dict[str, list[str]] = response.json()
···
176
176
177
177
178
178
def http_get_json(url: str) -> Any | None:
179
-
response = requests.get(url)
180
-
if response.ok:
179
+
response = httpx.get(url)
180
+
if response.is_success:
181
181
return response.json()
182
182
return None
183
183
184
184
185
185
def http_get(url: str) -> str | None:
186
-
response = requests.get(url)
187
-
if response.ok:
186
+
response = httpx.get(url)
187
+
if response.is_success:
188
188
return response.text
189
189
return None
+1
-1
src/atproto/oauth.py
+1
-1
src/atproto/oauth.py
+22
-4
src/main.py
+22
-4
src/main.py
···
2
2
from typing import Any
3
3
import json
4
4
5
-
from .atproto import PdsUrl, get_record, resolve_did_from_handle, resolve_pds_from_did
5
+
from .atproto import (
6
+
PdsUrl,
7
+
get_record,
8
+
is_valid_did,
9
+
resolve_did_from_handle,
10
+
resolve_pds_from_did,
11
+
)
6
12
from .atproto.oauth import pds_authed_req
7
13
from .db import close_db_connection, init_db
8
14
from .oauth import get_auth_session, oauth, save_auth_session
···
16
22
links: dict[str, list[dict[str, str]]] = {}
17
23
profiles: dict[str, tuple[str, str]] = {}
18
24
19
-
SCHEMA = "one.nauta"
25
+
SCHEMA = "at.ligo"
20
26
21
27
22
28
@app.before_request
···
38
44
return render_template("index.html")
39
45
40
46
47
+
@app.get("/did:<string:did>")
48
+
def page_profile_with_did(did: str):
49
+
did = f"did:{did}"
50
+
if not is_valid_did(did):
51
+
return "invalid did", 400
52
+
return page_profile(did)
53
+
54
+
41
55
@app.get("/@<string:handle>")
42
-
def page_profile(handle: str):
56
+
def page_profile_with_handle(handle: str):
43
57
reload = request.args.get("reload") is not None
44
58
45
59
did = resolve_did_from_handle(handle, reload=reload)
46
60
if did is None:
47
61
return "did not found", 404
62
+
return page_profile(did, reload=reload)
63
+
64
+
65
+
def page_profile(did: str, reload: bool = False):
48
66
pds = resolve_pds_from_did(did, reload=reload)
49
67
if pds is None:
50
68
return "pds not found", 404
···
242
260
user=user,
243
261
update_dpop_pds_nonce=update_dpop_pds_nonce,
244
262
)
245
-
if not response or not response.ok:
263
+
if not response or not response.is_success:
246
264
app.logger.warning("PDS HTTP ERROR")
+13
-11
src/security.py
+13
-11
src/security.py
···
1
1
from urllib.parse import urlparse
2
-
import requests_hardened
2
+
import httpx
3
3
4
4
5
5
# this is a crude/partial filter that looks at HTTPS URLs and checks if they seem "safe" for server-side requests (SSRF). This is only a partial mitigation, the actual HTTP client also needs to prevent other attacks and behaviors.
···
29
29
return True
30
30
31
31
32
-
# configures a "hardened" requests wrapper
33
-
hardened_http = requests_hardened.Manager(
34
-
requests_hardened.Config(
35
-
default_timeout=(2, 10),
36
-
never_redirect=True,
37
-
ip_filter_enable=True,
38
-
ip_filter_allow_loopback_ips=False,
39
-
user_agent_override="AtprotoCookbookOAuthFlaskDemo",
40
-
)
41
-
)
32
+
class HardenedHttp:
33
+
def get_session(self) -> httpx.Client:
34
+
return httpx.Client(
35
+
timeout=httpx.Timeout(20, connect=5),
36
+
follow_redirects=False,
37
+
headers={
38
+
"User-Agent": "ligo.at/0",
39
+
},
40
+
)
41
+
42
+
43
+
hardened_http = HardenedHttp()
+56
-66
uv.lock
+56
-66
uv.lock
···
3
3
requires-python = ">=3.13"
4
4
5
5
[[package]]
6
+
name = "anyio"
7
+
version = "4.11.0"
8
+
source = { registry = "https://pypi.org/simple" }
9
+
dependencies = [
10
+
{ name = "idna" },
11
+
{ name = "sniffio" },
12
+
]
13
+
sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" }
14
+
wheels = [
15
+
{ url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" },
16
+
]
17
+
18
+
[[package]]
6
19
name = "authlib"
7
20
version = "1.6.5"
8
21
source = { registry = "https://pypi.org/simple" }
···
78
91
]
79
92
80
93
[[package]]
81
-
name = "charset-normalizer"
82
-
version = "3.4.3"
83
-
source = { registry = "https://pypi.org/simple" }
84
-
sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371, upload-time = "2025-08-09T07:57:28.46Z" }
85
-
wheels = [
86
-
{ url = "https://files.pythonhosted.org/packages/65/ca/2135ac97709b400c7654b4b764daf5c5567c2da45a30cdd20f9eefe2d658/charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe", size = 205326, upload-time = "2025-08-09T07:56:24.721Z" },
87
-
{ url = "https://files.pythonhosted.org/packages/71/11/98a04c3c97dd34e49c7d247083af03645ca3730809a5509443f3c37f7c99/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8", size = 146008, upload-time = "2025-08-09T07:56:26.004Z" },
88
-
{ url = "https://files.pythonhosted.org/packages/60/f5/4659a4cb3c4ec146bec80c32d8bb16033752574c20b1252ee842a95d1a1e/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9", size = 159196, upload-time = "2025-08-09T07:56:27.25Z" },
89
-
{ url = "https://files.pythonhosted.org/packages/86/9e/f552f7a00611f168b9a5865a1414179b2c6de8235a4fa40189f6f79a1753/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31", size = 156819, upload-time = "2025-08-09T07:56:28.515Z" },
90
-
{ url = "https://files.pythonhosted.org/packages/7e/95/42aa2156235cbc8fa61208aded06ef46111c4d3f0de233107b3f38631803/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f", size = 151350, upload-time = "2025-08-09T07:56:29.716Z" },
91
-
{ url = "https://files.pythonhosted.org/packages/c2/a9/3865b02c56f300a6f94fc631ef54f0a8a29da74fb45a773dfd3dcd380af7/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927", size = 148644, upload-time = "2025-08-09T07:56:30.984Z" },
92
-
{ url = "https://files.pythonhosted.org/packages/77/d9/cbcf1a2a5c7d7856f11e7ac2d782aec12bdfea60d104e60e0aa1c97849dc/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9", size = 160468, upload-time = "2025-08-09T07:56:32.252Z" },
93
-
{ url = "https://files.pythonhosted.org/packages/f6/42/6f45efee8697b89fda4d50580f292b8f7f9306cb2971d4b53f8914e4d890/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5", size = 158187, upload-time = "2025-08-09T07:56:33.481Z" },
94
-
{ url = "https://files.pythonhosted.org/packages/70/99/f1c3bdcfaa9c45b3ce96f70b14f070411366fa19549c1d4832c935d8e2c3/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", size = 152699, upload-time = "2025-08-09T07:56:34.739Z" },
95
-
{ url = "https://files.pythonhosted.org/packages/a3/ad/b0081f2f99a4b194bcbb1934ef3b12aa4d9702ced80a37026b7607c72e58/charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce", size = 99580, upload-time = "2025-08-09T07:56:35.981Z" },
96
-
{ url = "https://files.pythonhosted.org/packages/9a/8f/ae790790c7b64f925e5c953b924aaa42a243fb778fed9e41f147b2a5715a/charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef", size = 107366, upload-time = "2025-08-09T07:56:37.339Z" },
97
-
{ url = "https://files.pythonhosted.org/packages/8e/91/b5a06ad970ddc7a0e513112d40113e834638f4ca1120eb727a249fb2715e/charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", size = 204342, upload-time = "2025-08-09T07:56:38.687Z" },
98
-
{ url = "https://files.pythonhosted.org/packages/ce/ec/1edc30a377f0a02689342f214455c3f6c2fbedd896a1d2f856c002fc3062/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", size = 145995, upload-time = "2025-08-09T07:56:40.048Z" },
99
-
{ url = "https://files.pythonhosted.org/packages/17/e5/5e67ab85e6d22b04641acb5399c8684f4d37caf7558a53859f0283a650e9/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", size = 158640, upload-time = "2025-08-09T07:56:41.311Z" },
100
-
{ url = "https://files.pythonhosted.org/packages/f1/e5/38421987f6c697ee3722981289d554957c4be652f963d71c5e46a262e135/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", size = 156636, upload-time = "2025-08-09T07:56:43.195Z" },
101
-
{ url = "https://files.pythonhosted.org/packages/a0/e4/5a075de8daa3ec0745a9a3b54467e0c2967daaaf2cec04c845f73493e9a1/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", size = 150939, upload-time = "2025-08-09T07:56:44.819Z" },
102
-
{ url = "https://files.pythonhosted.org/packages/02/f7/3611b32318b30974131db62b4043f335861d4d9b49adc6d57c1149cc49d4/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", size = 148580, upload-time = "2025-08-09T07:56:46.684Z" },
103
-
{ url = "https://files.pythonhosted.org/packages/7e/61/19b36f4bd67f2793ab6a99b979b4e4f3d8fc754cbdffb805335df4337126/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", size = 159870, upload-time = "2025-08-09T07:56:47.941Z" },
104
-
{ url = "https://files.pythonhosted.org/packages/06/57/84722eefdd338c04cf3030ada66889298eaedf3e7a30a624201e0cbe424a/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", size = 157797, upload-time = "2025-08-09T07:56:49.756Z" },
105
-
{ url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224, upload-time = "2025-08-09T07:56:51.369Z" },
106
-
{ url = "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", size = 100086, upload-time = "2025-08-09T07:56:52.722Z" },
107
-
{ url = "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", size = 107400, upload-time = "2025-08-09T07:56:55.172Z" },
108
-
{ url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175, upload-time = "2025-08-09T07:57:26.864Z" },
109
-
]
110
-
111
-
[[package]]
112
94
name = "click"
113
95
version = "8.3.0"
114
96
source = { registry = "https://pypi.org/simple" }
···
208
190
]
209
191
210
192
[[package]]
193
+
name = "h11"
194
+
version = "0.16.0"
195
+
source = { registry = "https://pypi.org/simple" }
196
+
sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
197
+
wheels = [
198
+
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
199
+
]
200
+
201
+
[[package]]
202
+
name = "httpcore"
203
+
version = "1.0.9"
204
+
source = { registry = "https://pypi.org/simple" }
205
+
dependencies = [
206
+
{ name = "certifi" },
207
+
{ name = "h11" },
208
+
]
209
+
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
210
+
wheels = [
211
+
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
212
+
]
213
+
214
+
[[package]]
215
+
name = "httpx"
216
+
version = "0.28.1"
217
+
source = { registry = "https://pypi.org/simple" }
218
+
dependencies = [
219
+
{ name = "anyio" },
220
+
{ name = "certifi" },
221
+
{ name = "httpcore" },
222
+
{ name = "idna" },
223
+
]
224
+
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
225
+
wheels = [
226
+
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
227
+
]
228
+
229
+
[[package]]
211
230
name = "idna"
212
231
version = "3.10"
213
232
source = { registry = "https://pypi.org/simple" }
···
246
265
{ name = "dnspython" },
247
266
{ name = "flask", extra = ["dotenv"] },
248
267
{ name = "gunicorn" },
249
-
{ name = "requests" },
250
-
{ name = "requests-hardened" },
268
+
{ name = "httpx" },
251
269
]
252
270
253
271
[package.metadata]
···
256
274
{ name = "dnspython", specifier = ">=2.8.0" },
257
275
{ name = "flask", extras = ["dotenv"], specifier = ">=3.1.2" },
258
276
{ name = "gunicorn", specifier = ">=23.0.0" },
259
-
{ name = "requests", specifier = ">=2.32" },
260
-
{ name = "requests-hardened", specifier = ">=1.2.0" },
277
+
{ name = "httpx", specifier = ">=0.28.1" },
261
278
]
262
279
263
280
[[package]]
···
340
357
]
341
358
342
359
[[package]]
343
-
name = "requests"
344
-
version = "2.32.5"
360
+
name = "sniffio"
361
+
version = "1.3.1"
345
362
source = { registry = "https://pypi.org/simple" }
346
-
dependencies = [
347
-
{ name = "certifi" },
348
-
{ name = "charset-normalizer" },
349
-
{ name = "idna" },
350
-
{ name = "urllib3" },
351
-
]
352
-
sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
363
+
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" }
353
364
wheels = [
354
-
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
355
-
]
356
-
357
-
[[package]]
358
-
name = "requests-hardened"
359
-
version = "1.2.0"
360
-
source = { registry = "https://pypi.org/simple" }
361
-
dependencies = [
362
-
{ name = "requests" },
363
-
]
364
-
sdist = { url = "https://files.pythonhosted.org/packages/0d/ab/3206848b4657be7902bb10af5686f71da450d9135340ecd6ee80da718557/requests_hardened-1.2.0.tar.gz", hash = "sha256:24ff13c798a22afc3465c24ff955b003c81f605e2ec30cbdbd40f28389cfca72", size = 7254, upload-time = "2025-09-26T12:20:20.518Z" }
365
-
wheels = [
366
-
{ url = "https://files.pythonhosted.org/packages/8d/0e/b521e2034f0984b3a446009223e8ec67bfae5e3d4a11b0066951d2df6515/requests_hardened-1.2.0-py3-none-any.whl", hash = "sha256:7d70b38bbfdea3f1d27d9149a5967f8c350b3496d232b1d4b031b7d0f2590ba9", size = 9197, upload-time = "2025-09-26T12:20:19.126Z" },
367
-
]
368
-
369
-
[[package]]
370
-
name = "urllib3"
371
-
version = "2.5.0"
372
-
source = { registry = "https://pypi.org/simple" }
373
-
sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" }
374
-
wheels = [
375
-
{ url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" },
365
+
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
376
366
]
377
367
378
368
[[package]]