fluidattacks_core_http 12.0.0__tar.gz
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.
- fluidattacks_core_http-12.0.0/.envrc +1 -0
- fluidattacks_core_http-12.0.0/.gitignore +5 -0
- fluidattacks_core_http-12.0.0/PKG-INFO +16 -0
- fluidattacks_core_http-12.0.0/build/default.nix +20 -0
- fluidattacks_core_http-12.0.0/build/filter.nix +12 -0
- fluidattacks_core_http-12.0.0/flake.lock +303 -0
- fluidattacks_core_http-12.0.0/flake.nix +29 -0
- fluidattacks_core_http-12.0.0/fluidattacks_core/http/__init__.py +5 -0
- fluidattacks_core_http-12.0.0/fluidattacks_core/http/client.py +90 -0
- fluidattacks_core_http-12.0.0/fluidattacks_core/http/py.typed +0 -0
- fluidattacks_core_http-12.0.0/fluidattacks_core/http/validations.py +133 -0
- fluidattacks_core_http-12.0.0/import-linter.cfg +22 -0
- fluidattacks_core_http-12.0.0/mypy.ini +32 -0
- fluidattacks_core_http-12.0.0/pyproject.toml +38 -0
- fluidattacks_core_http-12.0.0/ruff.toml +42 -0
- fluidattacks_core_http-12.0.0/uv.lock +1086 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
use_flake ".#python311.devShell"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: fluidattacks_core_http
|
|
3
|
+
Version: 12.0.0
|
|
4
|
+
Summary: Fluid Attacks Core HTTP Library
|
|
5
|
+
Author-email: Development <development@fluidattacks.com>
|
|
6
|
+
License: MPL-2.0
|
|
7
|
+
Classifier: Development Status :: 1 - Planning
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
13
|
+
Requires-Python: >=3.11
|
|
14
|
+
Requires-Dist: aiohttp>=3.10.0
|
|
15
|
+
Requires-Dist: certifi>=2024.8.30
|
|
16
|
+
Requires-Dist: urllib3>=2.6.1
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{ src, workspace_root }:
|
|
2
|
+
{
|
|
3
|
+
inherit src workspace_root;
|
|
4
|
+
root_path = "develops/fluidattacks-core/python/http";
|
|
5
|
+
module_name = "fluidattacks_core";
|
|
6
|
+
pypi_token = {
|
|
7
|
+
method = "oidc";
|
|
8
|
+
};
|
|
9
|
+
override = {
|
|
10
|
+
checks = {
|
|
11
|
+
basedpyright = false;
|
|
12
|
+
pytest = false;
|
|
13
|
+
};
|
|
14
|
+
configs = {
|
|
15
|
+
import_linter = ../import-linter.cfg;
|
|
16
|
+
mypy = ../mypy.ini;
|
|
17
|
+
ruff = ../ruff.toml;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
{
|
|
2
|
+
"nodes": {
|
|
3
|
+
"flake-parts": {
|
|
4
|
+
"inputs": {
|
|
5
|
+
"nixpkgs-lib": "nixpkgs-lib"
|
|
6
|
+
},
|
|
7
|
+
"locked": {
|
|
8
|
+
"lastModified": 1762810396,
|
|
9
|
+
"narHash": "sha256-dxFVgQPG+R72dkhXTtqUm7KpxElw3u6E+YlQ2WaDgt8=",
|
|
10
|
+
"owner": "hercules-ci",
|
|
11
|
+
"repo": "flake-parts",
|
|
12
|
+
"rev": "0bdadb1b265fb4143a75bd1ec7d8c915898a9923",
|
|
13
|
+
"type": "github"
|
|
14
|
+
},
|
|
15
|
+
"original": {
|
|
16
|
+
"owner": "hercules-ci",
|
|
17
|
+
"repo": "flake-parts",
|
|
18
|
+
"type": "github"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"nix_filter": {
|
|
22
|
+
"locked": {
|
|
23
|
+
"lastModified": 1757882181,
|
|
24
|
+
"narHash": "sha256-+cCxYIh2UNalTz364p+QYmWHs0P+6wDhiWR4jDIKQIU=",
|
|
25
|
+
"owner": "numtide",
|
|
26
|
+
"repo": "nix-filter",
|
|
27
|
+
"rev": "59c44d1909c72441144b93cf0f054be7fe764de5",
|
|
28
|
+
"type": "github"
|
|
29
|
+
},
|
|
30
|
+
"original": {
|
|
31
|
+
"owner": "numtide",
|
|
32
|
+
"repo": "nix-filter",
|
|
33
|
+
"type": "github"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"nixpkgs": {
|
|
37
|
+
"locked": {
|
|
38
|
+
"lastModified": 1758690382,
|
|
39
|
+
"narHash": "sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o=",
|
|
40
|
+
"owner": "nixos",
|
|
41
|
+
"repo": "nixpkgs",
|
|
42
|
+
"rev": "e643668fd71b949c53f8626614b21ff71a07379d",
|
|
43
|
+
"type": "github"
|
|
44
|
+
},
|
|
45
|
+
"original": {
|
|
46
|
+
"owner": "nixos",
|
|
47
|
+
"ref": "nixos-unstable",
|
|
48
|
+
"repo": "nixpkgs",
|
|
49
|
+
"type": "github"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"nixpkgs-lib": {
|
|
53
|
+
"locked": {
|
|
54
|
+
"lastModified": 1761765539,
|
|
55
|
+
"narHash": "sha256-b0yj6kfvO8ApcSE+QmA6mUfu8IYG6/uU28OFn4PaC8M=",
|
|
56
|
+
"owner": "nix-community",
|
|
57
|
+
"repo": "nixpkgs.lib",
|
|
58
|
+
"rev": "719359f4562934ae99f5443f20aa06c2ffff91fc",
|
|
59
|
+
"type": "github"
|
|
60
|
+
},
|
|
61
|
+
"original": {
|
|
62
|
+
"owner": "nix-community",
|
|
63
|
+
"repo": "nixpkgs.lib",
|
|
64
|
+
"type": "github"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"nixpkgs_2": {
|
|
68
|
+
"locked": {
|
|
69
|
+
"lastModified": 1758690382,
|
|
70
|
+
"narHash": "sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o=",
|
|
71
|
+
"owner": "nixos",
|
|
72
|
+
"repo": "nixpkgs",
|
|
73
|
+
"rev": "e643668fd71b949c53f8626614b21ff71a07379d",
|
|
74
|
+
"type": "github"
|
|
75
|
+
},
|
|
76
|
+
"original": {
|
|
77
|
+
"owner": "nixos",
|
|
78
|
+
"ref": "nixos-unstable",
|
|
79
|
+
"repo": "nixpkgs",
|
|
80
|
+
"type": "github"
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"nixpkgs_3": {
|
|
84
|
+
"locked": {
|
|
85
|
+
"lastModified": 1761373498,
|
|
86
|
+
"narHash": "sha256-Q/uhWNvd7V7k1H1ZPMy/vkx3F8C13ZcdrKjO7Jv7v0c=",
|
|
87
|
+
"owner": "nixos",
|
|
88
|
+
"repo": "nixpkgs",
|
|
89
|
+
"rev": "6a08e6bb4e46ff7fcbb53d409b253f6bad8a28ce",
|
|
90
|
+
"type": "github"
|
|
91
|
+
},
|
|
92
|
+
"original": {
|
|
93
|
+
"owner": "nixos",
|
|
94
|
+
"ref": "nixos-unstable",
|
|
95
|
+
"repo": "nixpkgs",
|
|
96
|
+
"type": "github"
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"nixpkgs_flake": {
|
|
100
|
+
"locked": {
|
|
101
|
+
"lastModified": 1778253243,
|
|
102
|
+
"narHash": "sha256-KkH2QvU6iRLDTGGEwH5jnMqHWj4zMTuHxi1S1zdr01w=",
|
|
103
|
+
"owner": "nixos",
|
|
104
|
+
"repo": "nixpkgs",
|
|
105
|
+
"rev": "4fcda7103530b23c5ca44286344912082aad4220",
|
|
106
|
+
"type": "github"
|
|
107
|
+
},
|
|
108
|
+
"original": {
|
|
109
|
+
"owner": "nixos",
|
|
110
|
+
"repo": "nixpkgs",
|
|
111
|
+
"type": "github"
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"pyproject-build-systems": {
|
|
115
|
+
"inputs": {
|
|
116
|
+
"nixpkgs": "nixpkgs",
|
|
117
|
+
"pyproject-nix": "pyproject-nix",
|
|
118
|
+
"uv2nix": "uv2nix"
|
|
119
|
+
},
|
|
120
|
+
"locked": {
|
|
121
|
+
"lastModified": 1761781027,
|
|
122
|
+
"narHash": "sha256-YDvxPAm2WnxrznRqWwHLjryBGG5Ey1ATEJXrON+TWt8=",
|
|
123
|
+
"owner": "pyproject-nix",
|
|
124
|
+
"repo": "build-system-pkgs",
|
|
125
|
+
"rev": "795a980d25301e5133eca37adae37283ec3c8e66",
|
|
126
|
+
"type": "github"
|
|
127
|
+
},
|
|
128
|
+
"original": {
|
|
129
|
+
"owner": "pyproject-nix",
|
|
130
|
+
"repo": "build-system-pkgs",
|
|
131
|
+
"type": "github"
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
"pyproject-nix": {
|
|
135
|
+
"inputs": {
|
|
136
|
+
"nixpkgs": [
|
|
137
|
+
"python_builder",
|
|
138
|
+
"pyproject-build-systems",
|
|
139
|
+
"nixpkgs"
|
|
140
|
+
]
|
|
141
|
+
},
|
|
142
|
+
"locked": {
|
|
143
|
+
"lastModified": 1758265079,
|
|
144
|
+
"narHash": "sha256-amLaLNwKSZPShQHzfgmc/9o76dU8xzN0743dWgvYlr8=",
|
|
145
|
+
"owner": "nix-community",
|
|
146
|
+
"repo": "pyproject.nix",
|
|
147
|
+
"rev": "02e9418fd4af638447dca4b17b1280da95527fc9",
|
|
148
|
+
"type": "github"
|
|
149
|
+
},
|
|
150
|
+
"original": {
|
|
151
|
+
"owner": "nix-community",
|
|
152
|
+
"repo": "pyproject.nix",
|
|
153
|
+
"type": "github"
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
"pyproject-nix_2": {
|
|
157
|
+
"inputs": {
|
|
158
|
+
"nixpkgs": "nixpkgs_2"
|
|
159
|
+
},
|
|
160
|
+
"locked": {
|
|
161
|
+
"lastModified": 1762427963,
|
|
162
|
+
"narHash": "sha256-CkPlAbIQ87wmjy5qHibfzk4DmMGBNqFer+lLfXjpP5M=",
|
|
163
|
+
"owner": "pyproject-nix",
|
|
164
|
+
"repo": "pyproject.nix",
|
|
165
|
+
"rev": "4540ea004e04fcd12dd2738d51383d10f956f7b9",
|
|
166
|
+
"type": "github"
|
|
167
|
+
},
|
|
168
|
+
"original": {
|
|
169
|
+
"owner": "pyproject-nix",
|
|
170
|
+
"repo": "pyproject.nix",
|
|
171
|
+
"type": "github"
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
"pyproject-nix_3": {
|
|
175
|
+
"inputs": {
|
|
176
|
+
"nixpkgs": [
|
|
177
|
+
"python_builder",
|
|
178
|
+
"uv2nix",
|
|
179
|
+
"nixpkgs"
|
|
180
|
+
]
|
|
181
|
+
},
|
|
182
|
+
"locked": {
|
|
183
|
+
"lastModified": 1760402624,
|
|
184
|
+
"narHash": "sha256-jF6UKLs2uGc2rtved8Vrt58oTWjTQoAssuYs/0578Z4=",
|
|
185
|
+
"owner": "pyproject-nix",
|
|
186
|
+
"repo": "pyproject.nix",
|
|
187
|
+
"rev": "84c4ea102127c77058ea1ed7be7300261fafc7d2",
|
|
188
|
+
"type": "github"
|
|
189
|
+
},
|
|
190
|
+
"original": {
|
|
191
|
+
"owner": "pyproject-nix",
|
|
192
|
+
"repo": "pyproject.nix",
|
|
193
|
+
"type": "github"
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
"python_builder": {
|
|
197
|
+
"inputs": {
|
|
198
|
+
"nix_filter": "nix_filter",
|
|
199
|
+
"nixpkgs_flake": "nixpkgs_flake",
|
|
200
|
+
"pyproject-build-systems": "pyproject-build-systems",
|
|
201
|
+
"pyproject-nix": "pyproject-nix_2",
|
|
202
|
+
"shell-helpers": "shell-helpers",
|
|
203
|
+
"uv2nix": "uv2nix_2"
|
|
204
|
+
},
|
|
205
|
+
"locked": {
|
|
206
|
+
"dir": "common/utils/python-builder",
|
|
207
|
+
"lastModified": 1787078362,
|
|
208
|
+
"narHash": "sha256-HYL7Rn6SuNjYxkt/PwFLksZjsauCiY+ENbOWryLsIlw=",
|
|
209
|
+
"ref": "refs/heads/trunk",
|
|
210
|
+
"rev": "09fd25a13309eb5e3b5b2a061bb340df3ae5d281",
|
|
211
|
+
"shallow": true,
|
|
212
|
+
"type": "git",
|
|
213
|
+
"url": "ssh://git@gitlab.com/fluidattacks/universe"
|
|
214
|
+
},
|
|
215
|
+
"original": {
|
|
216
|
+
"dir": "common/utils/python-builder",
|
|
217
|
+
"rev": "09fd25a13309eb5e3b5b2a061bb340df3ae5d281",
|
|
218
|
+
"shallow": true,
|
|
219
|
+
"type": "git",
|
|
220
|
+
"url": "ssh://git@gitlab.com/fluidattacks/universe"
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
"root": {
|
|
224
|
+
"inputs": {
|
|
225
|
+
"python_builder": "python_builder"
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
"shell-helpers": {
|
|
229
|
+
"inputs": {
|
|
230
|
+
"flake-parts": "flake-parts",
|
|
231
|
+
"nixpkgs": [
|
|
232
|
+
"python_builder",
|
|
233
|
+
"nixpkgs_flake"
|
|
234
|
+
]
|
|
235
|
+
},
|
|
236
|
+
"locked": {
|
|
237
|
+
"dir": "common/utils/shell-helpers",
|
|
238
|
+
"lastModified": 1762806066,
|
|
239
|
+
"narHash": "sha256-Wz99Fl7SpPU1NkTHJ36XOo6DjOEAMXCw3ThRABOS2Hs=",
|
|
240
|
+
"ref": "refs/heads/trunk",
|
|
241
|
+
"rev": "d73cfdc7ae9e7f547e22a4481d4c2ccce38b26a8",
|
|
242
|
+
"shallow": true,
|
|
243
|
+
"type": "git",
|
|
244
|
+
"url": "ssh://git@gitlab.com/fluidattacks/universe"
|
|
245
|
+
},
|
|
246
|
+
"original": {
|
|
247
|
+
"dir": "common/utils/shell-helpers",
|
|
248
|
+
"rev": "d73cfdc7ae9e7f547e22a4481d4c2ccce38b26a8",
|
|
249
|
+
"shallow": true,
|
|
250
|
+
"type": "git",
|
|
251
|
+
"url": "ssh://git@gitlab.com/fluidattacks/universe"
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
"uv2nix": {
|
|
255
|
+
"inputs": {
|
|
256
|
+
"nixpkgs": [
|
|
257
|
+
"python_builder",
|
|
258
|
+
"pyproject-build-systems",
|
|
259
|
+
"nixpkgs"
|
|
260
|
+
],
|
|
261
|
+
"pyproject-nix": [
|
|
262
|
+
"python_builder",
|
|
263
|
+
"pyproject-build-systems",
|
|
264
|
+
"pyproject-nix"
|
|
265
|
+
]
|
|
266
|
+
},
|
|
267
|
+
"locked": {
|
|
268
|
+
"lastModified": 1758933732,
|
|
269
|
+
"narHash": "sha256-HAmm1GBS1myZCFuog0DC2ZLaynvZtiUI2Crmo+cdQI0=",
|
|
270
|
+
"owner": "pyproject-nix",
|
|
271
|
+
"repo": "uv2nix",
|
|
272
|
+
"rev": "273ce18f913d8559e0d04f820d724308966d7c4d",
|
|
273
|
+
"type": "github"
|
|
274
|
+
},
|
|
275
|
+
"original": {
|
|
276
|
+
"owner": "pyproject-nix",
|
|
277
|
+
"repo": "uv2nix",
|
|
278
|
+
"type": "github"
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
"uv2nix_2": {
|
|
282
|
+
"inputs": {
|
|
283
|
+
"nixpkgs": "nixpkgs_3",
|
|
284
|
+
"pyproject-nix": "pyproject-nix_3"
|
|
285
|
+
},
|
|
286
|
+
"locked": {
|
|
287
|
+
"lastModified": 1761872265,
|
|
288
|
+
"narHash": "sha256-i25GRgp2vUOebY70L3NTAgkd+Pr1hnn5xM3qHxH0ONU=",
|
|
289
|
+
"owner": "pyproject-nix",
|
|
290
|
+
"repo": "uv2nix",
|
|
291
|
+
"rev": "74dfb62871be152ad3673b143b0cc56105a4f3c5",
|
|
292
|
+
"type": "github"
|
|
293
|
+
},
|
|
294
|
+
"original": {
|
|
295
|
+
"owner": "pyproject-nix",
|
|
296
|
+
"repo": "uv2nix",
|
|
297
|
+
"type": "github"
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
"root": "root",
|
|
302
|
+
"version": 7
|
|
303
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
description = "Fluid Attacks Core http package.";
|
|
3
|
+
|
|
4
|
+
inputs = {
|
|
5
|
+
python_builder = {
|
|
6
|
+
url = "git+ssh://git@gitlab.com/fluidattacks/universe?shallow=1&rev=09fd25a13309eb5e3b5b2a061bb340df3ae5d281&dir=common/utils/python-builder";
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
outputs =
|
|
11
|
+
{ self, ... }@inputs:
|
|
12
|
+
let
|
|
13
|
+
build_args =
|
|
14
|
+
{
|
|
15
|
+
system,
|
|
16
|
+
python_version,
|
|
17
|
+
nixpkgs,
|
|
18
|
+
builders,
|
|
19
|
+
scripts,
|
|
20
|
+
}:
|
|
21
|
+
import ./build {
|
|
22
|
+
src = import ./build/filter.nix nixpkgs.nix-filter self;
|
|
23
|
+
workspace_root = ./.;
|
|
24
|
+
};
|
|
25
|
+
in
|
|
26
|
+
{
|
|
27
|
+
packages = inputs.python_builder.outputs.build build_args;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import ipaddress
|
|
2
|
+
import ssl
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
from typing import (
|
|
5
|
+
Any,
|
|
6
|
+
Literal,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
import aiohttp
|
|
10
|
+
import certifi
|
|
11
|
+
|
|
12
|
+
from .validations import (
|
|
13
|
+
validate_local_request,
|
|
14
|
+
validate_url,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_secure_connector(
|
|
19
|
+
*,
|
|
20
|
+
allow_local_network: bool,
|
|
21
|
+
allow_localhost: bool,
|
|
22
|
+
) -> type[aiohttp.TCPConnector]:
|
|
23
|
+
class SecureTCPConnector(aiohttp.TCPConnector):
|
|
24
|
+
async def _resolve_host(
|
|
25
|
+
self,
|
|
26
|
+
host: str,
|
|
27
|
+
port: int,
|
|
28
|
+
traces: Sequence[aiohttp.tracing.Trace] | None = None,
|
|
29
|
+
) -> list[aiohttp.abc.ResolveResult]:
|
|
30
|
+
hosts = await super()._resolve_host(host, port, traces)
|
|
31
|
+
resolved_ips = [ipaddress.ip_address(host["host"]) for host in hosts]
|
|
32
|
+
validate_local_request(
|
|
33
|
+
resolved_ips,
|
|
34
|
+
allow_local_network=allow_local_network,
|
|
35
|
+
allow_localhost=allow_localhost,
|
|
36
|
+
)
|
|
37
|
+
return hosts
|
|
38
|
+
|
|
39
|
+
return SecureTCPConnector
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
async def request( # noqa: PLR0913
|
|
43
|
+
url: str,
|
|
44
|
+
*,
|
|
45
|
+
method: Literal["GET", "POST", "PUT", "PATCH", "DELETE"],
|
|
46
|
+
allow_local_network: bool = False,
|
|
47
|
+
allow_localhost: bool = False,
|
|
48
|
+
ascii_only: bool = False,
|
|
49
|
+
dns_rebind_protection: bool = True,
|
|
50
|
+
enforce_sanitization: bool = False,
|
|
51
|
+
headers: dict[str, str] | None = None,
|
|
52
|
+
json: Any | None = None, # noqa: ANN401
|
|
53
|
+
ports: list[int] | None = None,
|
|
54
|
+
schemes: list[str] | None = None,
|
|
55
|
+
timeout: int = 10, # noqa: ASYNC109
|
|
56
|
+
) -> aiohttp.ClientResponse:
|
|
57
|
+
validate_url(
|
|
58
|
+
url,
|
|
59
|
+
ascii_only=ascii_only,
|
|
60
|
+
enforce_sanitization=enforce_sanitization,
|
|
61
|
+
ports=ports or [],
|
|
62
|
+
schemes=schemes or [],
|
|
63
|
+
)
|
|
64
|
+
connector = (
|
|
65
|
+
get_secure_connector(
|
|
66
|
+
allow_local_network=allow_local_network,
|
|
67
|
+
allow_localhost=allow_localhost,
|
|
68
|
+
)
|
|
69
|
+
if dns_rebind_protection
|
|
70
|
+
else aiohttp.TCPConnector
|
|
71
|
+
)
|
|
72
|
+
connection = connector(
|
|
73
|
+
ssl=ssl.create_default_context(cafile=certifi.where()),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
async with (
|
|
77
|
+
aiohttp.ClientSession(
|
|
78
|
+
connector=connection,
|
|
79
|
+
headers=headers,
|
|
80
|
+
) as session,
|
|
81
|
+
session.request(
|
|
82
|
+
method,
|
|
83
|
+
url,
|
|
84
|
+
allow_redirects=not dns_rebind_protection,
|
|
85
|
+
json=json,
|
|
86
|
+
timeout=aiohttp.ClientTimeout(total=timeout),
|
|
87
|
+
) as response,
|
|
88
|
+
):
|
|
89
|
+
await response.read()
|
|
90
|
+
return response
|
|
File without changes
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import html
|
|
2
|
+
import ipaddress
|
|
3
|
+
|
|
4
|
+
from urllib3.exceptions import LocationParseError
|
|
5
|
+
from urllib3.util import Url, parse_url
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class HTTPValidationError(Exception):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def validate_scheme(scheme: str | None, schemes: list[str]) -> None:
|
|
13
|
+
if scheme and scheme not in schemes:
|
|
14
|
+
msg = f"Only allowed schemes are {', '.join(schemes)}"
|
|
15
|
+
raise HTTPValidationError(msg)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def validate_port(port: int | None, ports: list[int]) -> None:
|
|
19
|
+
if port and port < 1024 and port not in ports:
|
|
20
|
+
msg = f"Only allowed ports are {', '.join(map(str, ports))}, and any over 1024"
|
|
21
|
+
raise HTTPValidationError(msg)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def validate_host(host: str | None) -> None:
|
|
25
|
+
if not host:
|
|
26
|
+
return
|
|
27
|
+
try:
|
|
28
|
+
ipaddress.ip_address(host)
|
|
29
|
+
except ValueError:
|
|
30
|
+
if host[0].isalnum():
|
|
31
|
+
return
|
|
32
|
+
else:
|
|
33
|
+
return
|
|
34
|
+
msg = "Hostname or IP address invalid"
|
|
35
|
+
raise HTTPValidationError(msg)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def validate_unicode_restriction(uri: Url) -> None:
|
|
39
|
+
uri_str = str(uri)
|
|
40
|
+
if not uri_str.isascii():
|
|
41
|
+
msg = f"URI must be ascii only {uri_str}"
|
|
42
|
+
raise HTTPValidationError(msg)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def validate_html_tags(uri: Url) -> None:
|
|
46
|
+
uri_str = str(uri)
|
|
47
|
+
sanitized_uri = html.escape(uri_str)
|
|
48
|
+
if sanitized_uri != uri_str:
|
|
49
|
+
msg = "HTML/CSS/JS tags are not allowed"
|
|
50
|
+
raise HTTPValidationError(msg)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def validate_url(
|
|
54
|
+
url: str,
|
|
55
|
+
*,
|
|
56
|
+
ascii_only: bool,
|
|
57
|
+
enforce_sanitization: bool,
|
|
58
|
+
ports: list[int],
|
|
59
|
+
schemes: list[str],
|
|
60
|
+
) -> None:
|
|
61
|
+
try:
|
|
62
|
+
uri = parse_url(url)
|
|
63
|
+
except LocationParseError as exc:
|
|
64
|
+
msg = f"Invalid URL {url}"
|
|
65
|
+
raise HTTPValidationError(msg) from exc
|
|
66
|
+
validate_host(uri.host)
|
|
67
|
+
|
|
68
|
+
if ascii_only:
|
|
69
|
+
validate_unicode_restriction(uri)
|
|
70
|
+
if enforce_sanitization:
|
|
71
|
+
validate_html_tags(uri)
|
|
72
|
+
if ports:
|
|
73
|
+
validate_port(uri.port, ports)
|
|
74
|
+
if schemes:
|
|
75
|
+
validate_scheme(uri.scheme, schemes)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def validate_loopback(
|
|
79
|
+
ips: list[ipaddress.IPv4Address | ipaddress.IPv6Address],
|
|
80
|
+
) -> None:
|
|
81
|
+
if any(ip.is_loopback for ip in ips):
|
|
82
|
+
msg = "Requests to loopback addresses are not allowed"
|
|
83
|
+
raise HTTPValidationError(msg)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def validate_local_network(
|
|
87
|
+
ips: list[ipaddress.IPv4Address | ipaddress.IPv6Address],
|
|
88
|
+
) -> None:
|
|
89
|
+
if any(ip.is_private for ip in ips):
|
|
90
|
+
msg = "Requests to the local network are not allowed"
|
|
91
|
+
raise HTTPValidationError(msg)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def validate_link_local(
|
|
95
|
+
ips: list[ipaddress.IPv4Address | ipaddress.IPv6Address],
|
|
96
|
+
) -> None:
|
|
97
|
+
if any(ip.is_link_local for ip in ips):
|
|
98
|
+
msg = "Requests to the link local network are not allowed"
|
|
99
|
+
raise HTTPValidationError(msg)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def validate_shared_address(
|
|
103
|
+
ips: list[ipaddress.IPv4Address | ipaddress.IPv6Address],
|
|
104
|
+
) -> None:
|
|
105
|
+
shared_address_space = ipaddress.ip_network("100.64.0.0/10")
|
|
106
|
+
if any(ip in shared_address_space for ip in ips):
|
|
107
|
+
msg = "Requests to the shared address space are not allowed"
|
|
108
|
+
raise HTTPValidationError(msg)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def validate_limited_broadcast_address(
|
|
112
|
+
ips: list[ipaddress.IPv4Address | ipaddress.IPv6Address],
|
|
113
|
+
) -> None:
|
|
114
|
+
limited_broadcast_address = ipaddress.ip_address("255.255.255.255")
|
|
115
|
+
if any(ip == limited_broadcast_address for ip in ips):
|
|
116
|
+
msg = "Requests to the limited broadcast address are not allowed"
|
|
117
|
+
raise HTTPValidationError(msg)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def validate_local_request(
|
|
121
|
+
ips: list[ipaddress.IPv4Address | ipaddress.IPv6Address],
|
|
122
|
+
*,
|
|
123
|
+
allow_local_network: bool,
|
|
124
|
+
allow_localhost: bool,
|
|
125
|
+
) -> None:
|
|
126
|
+
if not allow_localhost:
|
|
127
|
+
validate_loopback(ips)
|
|
128
|
+
|
|
129
|
+
if not allow_local_network:
|
|
130
|
+
validate_local_network(ips)
|
|
131
|
+
validate_link_local(ips)
|
|
132
|
+
validate_shared_address(ips)
|
|
133
|
+
validate_limited_broadcast_address(ips)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[importlinter]
|
|
2
|
+
root_package = fluidattacks_core
|
|
3
|
+
include_external_packages = True
|
|
4
|
+
|
|
5
|
+
[importlinter:contract:portion]
|
|
6
|
+
name = Single Portion
|
|
7
|
+
type = layers
|
|
8
|
+
containers =
|
|
9
|
+
fluidattacks_core
|
|
10
|
+
exhaustive = true
|
|
11
|
+
layers =
|
|
12
|
+
http
|
|
13
|
+
|
|
14
|
+
[importlinter:contract:dag]
|
|
15
|
+
name = Direct Acyclic Graph
|
|
16
|
+
type = layers
|
|
17
|
+
containers =
|
|
18
|
+
fluidattacks_core.http
|
|
19
|
+
exhaustive = true
|
|
20
|
+
layers =
|
|
21
|
+
client
|
|
22
|
+
validations
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[mypy]
|
|
2
|
+
disallow_any_decorated = True
|
|
3
|
+
disallow_any_expr = False
|
|
4
|
+
disallow_any_explicit = False
|
|
5
|
+
disallow_any_generics = True
|
|
6
|
+
disallow_any_unimported = True
|
|
7
|
+
disallow_incomplete_defs = True
|
|
8
|
+
disallow_subclassing_any = True
|
|
9
|
+
disallow_untyped_calls = True
|
|
10
|
+
disallow_untyped_decorators = True
|
|
11
|
+
disallow_untyped_defs = True
|
|
12
|
+
|
|
13
|
+
check_untyped_defs = True
|
|
14
|
+
no_implicit_optional = True
|
|
15
|
+
strict = True
|
|
16
|
+
strict_equality = True
|
|
17
|
+
strict_optional = True
|
|
18
|
+
|
|
19
|
+
warn_redundant_casts = True
|
|
20
|
+
warn_unused_ignores = True
|
|
21
|
+
warn_no_return = True
|
|
22
|
+
warn_return_any = False
|
|
23
|
+
warn_unreachable = True
|
|
24
|
+
|
|
25
|
+
explicit_package_bases = True
|
|
26
|
+
|
|
27
|
+
ignore_errors = False
|
|
28
|
+
ignore_missing_imports = False
|
|
29
|
+
allow_untyped_globals = False
|
|
30
|
+
allow_redefinition = False
|
|
31
|
+
local_partial_types = False
|
|
32
|
+
implicit_reexport = False
|