osbot-utils 1.51.0__py3-none-any.whl → 1.52.0__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.
- osbot_utils/testing/Custom_Handler_For_Http_Tests.py +126 -0
- osbot_utils/utils/Http.py +27 -0
- osbot_utils/version +1 -1
- {osbot_utils-1.51.0.dist-info → osbot_utils-1.52.0.dist-info}/METADATA +2 -2
- {osbot_utils-1.51.0.dist-info → osbot_utils-1.52.0.dist-info}/RECORD +7 -6
- {osbot_utils-1.51.0.dist-info → osbot_utils-1.52.0.dist-info}/LICENSE +0 -0
- {osbot_utils-1.51.0.dist-info → osbot_utils-1.52.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,126 @@
|
|
1
|
+
import json
|
2
|
+
from http.server import BaseHTTPRequestHandler
|
3
|
+
from urllib.parse import parse_qs
|
4
|
+
|
5
|
+
from osbot_utils.utils.Json import json_dumps_to_bytes
|
6
|
+
|
7
|
+
|
8
|
+
class Custom_Handler_For_Http_Tests(BaseHTTPRequestHandler):
|
9
|
+
|
10
|
+
HTTP_GET_DATA_JSON : str = '/data.json'
|
11
|
+
HTTP_GET_HTML : str = "<html><p>hello world</p></html>"
|
12
|
+
HTTP_GET_IMAGE_PATH : str = '/test.png'
|
13
|
+
HTTP_GET_IMAGE_BYTES : bytes = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00'
|
14
|
+
captured_logs : list = []
|
15
|
+
print_logs : bool = False
|
16
|
+
|
17
|
+
def do_DELETE(self):
|
18
|
+
# Assuming data as a query string in the URL or as a form
|
19
|
+
content_length = int(self.headers['Content-Length']) if 'Content-Length' in self.headers else 0
|
20
|
+
data_string = self.rfile.read(content_length).decode('utf-8')
|
21
|
+
response_data = {'status': 'success', 'data_received': data_string}
|
22
|
+
self.send_response(200)
|
23
|
+
self.send_header('Content-type', 'application/json')
|
24
|
+
self.end_headers()
|
25
|
+
self.wfile.write(json_dumps_to_bytes(response_data))
|
26
|
+
|
27
|
+
|
28
|
+
def do_GET(self):
|
29
|
+
if self.path == self.HTTP_GET_IMAGE_PATH:
|
30
|
+
self.send_response(200)
|
31
|
+
self.send_header("Content-type", "image/png")
|
32
|
+
self.end_headers()
|
33
|
+
self.wfile.write(self.HTTP_GET_IMAGE_BYTES)
|
34
|
+
return
|
35
|
+
if self.path == self.HTTP_GET_DATA_JSON:
|
36
|
+
response_data = {
|
37
|
+
'args': {'ddd': '1', 'eee': '2'},
|
38
|
+
'headers': {
|
39
|
+
'Accept': 'application/json',
|
40
|
+
'Accept-Encoding': 'identity',
|
41
|
+
'Host': 'httpbin.org',
|
42
|
+
'User-Agent': 'Python-urllib/3.10' ,
|
43
|
+
'X-Amzn-Trace-Id': 'Root=1-616b1b1e-4b0a1b1e1b1e1b1e1b1e1b1e'
|
44
|
+
},
|
45
|
+
'origin': 'some origin',
|
46
|
+
'url': 'https://httpbin.org/get?ddd=1&eee=2'
|
47
|
+
}
|
48
|
+
|
49
|
+
self.send_response(200)
|
50
|
+
self.send_header("Content-type", "application/json")
|
51
|
+
self.end_headers()
|
52
|
+
self.wfile.write(json.dumps(response_data).encode('utf-8'))
|
53
|
+
return
|
54
|
+
|
55
|
+
self.send_response(200)
|
56
|
+
self.send_header("Content-type", "text/html")
|
57
|
+
self.end_headers()
|
58
|
+
html = self.HTTP_GET_HTML
|
59
|
+
self.wfile.write(html.encode())
|
60
|
+
|
61
|
+
def do_HEAD(self):
|
62
|
+
self.send_response(200)
|
63
|
+
self.send_header('Content-type', 'text/html')
|
64
|
+
self.end_headers()
|
65
|
+
|
66
|
+
def do_OPTIONS(self):
|
67
|
+
self.send_response(200)
|
68
|
+
self.send_header('Allow', 'POST')
|
69
|
+
self.end_headers()
|
70
|
+
|
71
|
+
def do_POST(self):
|
72
|
+
# Calculate content length & read the data
|
73
|
+
content_length = int(self.headers['Content-Length'])
|
74
|
+
post_data = self.rfile.read(content_length).decode('utf-8')
|
75
|
+
post_data = parse_qs(post_data) # parse the received data
|
76
|
+
|
77
|
+
# Create the response data structure
|
78
|
+
response_data = {
|
79
|
+
'args': {'ddd': '1', 'eee': '2'}, # This should match the expected args in the test
|
80
|
+
'data': '',
|
81
|
+
'files': {},
|
82
|
+
'form': {key: value[0] for key, value in post_data.items()}, # Convert form data from list to single value
|
83
|
+
'headers': {
|
84
|
+
'Accept': self.headers['Accept'],
|
85
|
+
'Accept-Encoding': self.headers['Accept-Encoding'],
|
86
|
+
'Content-Length': self.headers['Content-Length'],
|
87
|
+
'Content-Type' : self.headers['Content-Type'],
|
88
|
+
'Host' : self.headers['Host'],
|
89
|
+
'User-Agent' : self.headers['User-Agent'],
|
90
|
+
'X-Amzn-Trace-Id': 'Root=1-616b1b1e-4b0a1b1e1b1e1b1e1b1e1b1e'
|
91
|
+
},
|
92
|
+
'origin': 'some origin',
|
93
|
+
'json': { 'json':'is here', 'a':42},
|
94
|
+
'url': self.headers['Host'] + self.path # Construct the URL from the Host header and path
|
95
|
+
}
|
96
|
+
|
97
|
+
# Send the HTTP response
|
98
|
+
|
99
|
+
self.send_response(200)
|
100
|
+
self.send_header('Content-type', 'application/json')
|
101
|
+
self.end_headers()
|
102
|
+
# Convert the response data to JSON and write it to the response
|
103
|
+
self.wfile.write(json.dumps(response_data).encode('utf-8'))
|
104
|
+
|
105
|
+
def do_PUT(self):
|
106
|
+
content_length = int(self.headers['Content-Length']) # todo refactor into helper method (since there are a number of methods here that use this)
|
107
|
+
post_data = self.rfile.read(content_length).decode('utf-8')
|
108
|
+
post_data = parse_qs(post_data) # parse the received data
|
109
|
+
response_data = {
|
110
|
+
'args': {'ddd': '1', 'eee': '2'}, # This should match the expected args in the test
|
111
|
+
'data': '',
|
112
|
+
'files': {},
|
113
|
+
'form': {key: value[0] for key, value in post_data.items()}, # Convert form data from list to single value
|
114
|
+
'headers': { 'X-Amzn-Trace-Id': 'Root=1-616b1b1e-4b0a1b1e1b1e1b1e1b1e1b1e'},
|
115
|
+
'origin': 'some origin',
|
116
|
+
}
|
117
|
+
self.send_response(200)
|
118
|
+
self.send_header('Content-type', 'application/json')
|
119
|
+
self.end_headers()
|
120
|
+
self.wfile.write(json.dumps(response_data).encode('utf-8'))
|
121
|
+
|
122
|
+
def log_message(self, msg_format, *args):
|
123
|
+
log_message = "%s - - %s" % (self.address_string(), msg_format % args)
|
124
|
+
self.captured_logs.append(log_message)
|
125
|
+
if self.print_logs:
|
126
|
+
print(log_message)
|
osbot_utils/utils/Http.py
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
import json
|
2
|
+
import os
|
2
3
|
import socket
|
3
4
|
import ssl
|
4
5
|
from time import sleep
|
6
|
+
from urllib.parse import quote, urljoin, urlparse
|
5
7
|
from urllib.request import Request, urlopen
|
6
8
|
|
7
9
|
from osbot_utils.utils.Files import save_bytes_as_file, file_size, file_bytes, file_open_bytes, file_create
|
@@ -141,3 +143,28 @@ def PUT(url, data='', headers=None):
|
|
141
143
|
|
142
144
|
def PUT_json(*args, **kwargs):
|
143
145
|
return json.loads(PUT(*args, **kwargs))
|
146
|
+
|
147
|
+
def url_join_safe(base_path, path=''):
|
148
|
+
if not isinstance(base_path, str) or not isinstance(path, str):
|
149
|
+
return None
|
150
|
+
|
151
|
+
if not base_path.endswith('/'): # Ensure that the base path ends with '/'
|
152
|
+
base_path += '/'
|
153
|
+
|
154
|
+
if path.startswith('/'): # Remove leading '/' from path
|
155
|
+
path = path[1:]
|
156
|
+
|
157
|
+
path_quoted = quote(path)
|
158
|
+
path_normalised = path_quoted.replace("..", "") # Quote the path to encode special characters and prevent directory traversal
|
159
|
+
joined_path = urljoin(base_path, path_normalised) # Join the base path and normalized path
|
160
|
+
parsed_base = urlparse(base_path) # Parse and verify the result
|
161
|
+
parsed_joined = urlparse(joined_path)
|
162
|
+
|
163
|
+
if (parsed_joined.scheme == parsed_base.scheme and # Ensure the joined URL starts with the base URL to prevent open redirect vulnerabilities
|
164
|
+
parsed_joined.netloc == parsed_base.netloc and
|
165
|
+
joined_path.startswith(base_path)):
|
166
|
+
if joined_path.endswith('/'): # Remove trailing slash
|
167
|
+
joined_path = joined_path[:-1]
|
168
|
+
return joined_path
|
169
|
+
|
170
|
+
return None
|
osbot_utils/version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
v1.
|
1
|
+
v1.52.0
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: osbot_utils
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.52.0
|
4
4
|
Summary: OWASP Security Bot - Utils
|
5
5
|
Home-page: https://github.com/owasp-sbot/OSBot-Utils
|
6
6
|
License: MIT
|
@@ -22,7 +22,7 @@ Description-Content-Type: text/markdown
|
|
22
22
|
|
23
23
|
Powerful Python util methods and classes that simplify common apis and tasks.
|
24
24
|
|
25
|
-

|
26
26
|
[](https://codecov.io/gh/owasp-sbot/OSBot-Utils)
|
27
27
|
|
28
28
|
|
@@ -251,6 +251,7 @@ osbot_utils/helpers/trace/Trace_Call__View_Model.py,sha256=a40nn6agCEMd2ecsJ93n8
|
|
251
251
|
osbot_utils/helpers/trace/Trace_Files.py,sha256=SNpAmuBlSUS9NyVocgZ5vevzqVaIqoh622yZge3a53A,978
|
252
252
|
osbot_utils/helpers/trace/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
253
253
|
osbot_utils/testing/Catch.py,sha256=HdNoKnrPBjvVj87XYN-Wa1zpo5z3oByURT6TKbd5QpQ,2229
|
254
|
+
osbot_utils/testing/Custom_Handler_For_Http_Tests.py,sha256=LKscFEcuwTQQ9xl4q71PR5FA8U-q8OtfTkCJoIgQIoQ,5358
|
254
255
|
osbot_utils/testing/Duration.py,sha256=iBrczAuw6j3jXtG7ZPraT0PXbCILEcCplJbqei96deA,2217
|
255
256
|
osbot_utils/testing/Hook_Method.py,sha256=uCpc89ZhMCfWiyt3tFhIGIInXiY6wTuwAZ1I8UQVRuw,4365
|
256
257
|
osbot_utils/testing/Log_To_Queue.py,sha256=pZQ7I1ne-H365a4WLS60oAD-B16pxIZO4suvCdaTW8U,1703
|
@@ -279,7 +280,7 @@ osbot_utils/utils/Env.py,sha256=XMwF5BrtpoPJdOraswAFPrcQ3tRTocjqvA8I61eOCJw,5741
|
|
279
280
|
osbot_utils/utils/Exceptions.py,sha256=KyOUHkXQ_6jDTq04Xm261dbEZuRidtsM4dgzNwSG8-8,389
|
280
281
|
osbot_utils/utils/Files.py,sha256=7fdqbfFyo6Ow5Repi_dZAzHqGb0XYh6tDALYAy42pBY,22522
|
281
282
|
osbot_utils/utils/Functions.py,sha256=0E6alPJ0fJpBiJgFOWooCOi265wSRyxxXAJ5CELBnso,3498
|
282
|
-
osbot_utils/utils/Http.py,sha256=
|
283
|
+
osbot_utils/utils/Http.py,sha256=YHHHRpy_QtH-Q_jaSuDwWRl-mmKBiC0DzfDew3l7fgg,6079
|
283
284
|
osbot_utils/utils/Int.py,sha256=PmlUdU4lSwf4gJdmTVdqclulkEp7KPCVUDO6AcISMF4,116
|
284
285
|
osbot_utils/utils/Json.py,sha256=7COxBlZRnpxtpNqpmzMPYkcKTnCok-s686nT27oiKEQ,6489
|
285
286
|
osbot_utils/utils/Json_Cache.py,sha256=mLPkkDZN-3ZVJiDvV1KBJXILtKkTZ4OepzOsDoBPhWg,2006
|
@@ -297,8 +298,8 @@ osbot_utils/utils/Toml.py,sha256=SD6IA4-mrtoBXcI0dIGKV9POMQNd6WYKvmDQq7GQ6ZQ,143
|
|
297
298
|
osbot_utils/utils/Version.py,sha256=Ww6ChwTxqp1QAcxOnztkTicShlcx6fbNsWX5xausHrg,422
|
298
299
|
osbot_utils/utils/Zip.py,sha256=G6Hk_hDcm9yvWzhTKzhT0R_6f0NBIAchHqMxGb3kfh4,14037
|
299
300
|
osbot_utils/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
300
|
-
osbot_utils/version,sha256=
|
301
|
-
osbot_utils-1.
|
302
|
-
osbot_utils-1.
|
303
|
-
osbot_utils-1.
|
304
|
-
osbot_utils-1.
|
301
|
+
osbot_utils/version,sha256=8vEHTopueL8q72Dw7rD-UnsLBahO-qnshWhrIIA_f-A,8
|
302
|
+
osbot_utils-1.52.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
303
|
+
osbot_utils-1.52.0.dist-info/METADATA,sha256=oAWX7EihUIYgMTuzzWfOEO0z4XKdrRWi-J1ap0ksrlE,1266
|
304
|
+
osbot_utils-1.52.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
305
|
+
osbot_utils-1.52.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|