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.
@@ -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.51.0
1
+ v1.52.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: osbot_utils
3
- Version: 1.51.0
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
- ![Current Release](https://img.shields.io/badge/release-v1.51.0-blue)
25
+ ![Current Release](https://img.shields.io/badge/release-v1.52.0-blue)
26
26
  [![codecov](https://codecov.io/gh/owasp-sbot/OSBot-Utils/graph/badge.svg?token=GNVW0COX1N)](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=WlXEfgT_NaiDVD7vCDUxy_nOm5Qf8x_L0A3zd8B5tX8,4706
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=ixJmwyRL1VTzeIxW6jCmHARkd1AR8-WHR4Kue-LZ5hk,8
301
- osbot_utils-1.51.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
302
- osbot_utils-1.51.0.dist-info/METADATA,sha256=5xljws2rnme6CCW_3yB3uAYk7HtJ_5Tqw65q0p8p4hg,1266
303
- osbot_utils-1.51.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
304
- osbot_utils-1.51.0.dist-info/RECORD,,
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,,