Skip to content
This repository was archived by the owner on Apr 13, 2024. It is now read-only.

Commit 18138b8

Browse files
committed
Merge branch 'update-input-validation'
2 parents 7198be9 + 78b17af commit 18138b8

File tree

5 files changed

+255
-6
lines changed

5 files changed

+255
-6
lines changed

httpsig/sign.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,17 @@ class HeaderSigner(Signer):
107107
def __init__(self, key_id, secret, algorithm=None, sign_algorithm=None, headers=None, sign_header='authorization'):
108108
if algorithm is None:
109109
algorithm = DEFAULT_ALGORITHM
110+
if not key_id:
111+
raise ValueError("key_id can't be empty")
112+
113+
if len(key_id) > 100000:
114+
raise ValueError("key_id cant be larger than 100000 chars")
115+
116+
if not secret:
117+
raise ValueError("secret can't be empty")
118+
119+
if len(secret) > 100000:
120+
raise ValueError("secret cant be larger than 100000 chars")
110121

111122
super(HeaderSigner, self).__init__(secret=secret, algorithm=algorithm, sign_algorithm=sign_algorithm)
112123
self.headers = headers or ['date']

httpsig/tests/test_signature.py

Lines changed: 217 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,220 @@ def test_unsupported_hash_algorithm(self):
140140
def test_deprecated_hash_algorithm(self):
141141
with pytest.raises(HttpSigException) as e:
142142
sign.HeaderSigner(key_id='Test', secret=self.key_2048, sign_algorithm=PSS("sha256", salt_length=0))
143-
self.assertEqual(str(e.value), "Hash algorithm: sha256 is deprecated. Please use: sha512")
143+
self.assertEqual(str(e.value), "Hash algorithm: sha256 is deprecated. Please use: sha512")
144+
145+
def test_empty_secret(self):
146+
with self.assertRaises(ValueError) as e:
147+
sign.HeaderSigner(key_id='Test', secret='', headers=[
148+
'(request-target)',
149+
'host',
150+
'date',
151+
'content-type',
152+
'digest',
153+
'content-length'
154+
])
155+
self.assertEqual(str(e.exception), "secret can't be empty")
156+
157+
def test_none_secret(self):
158+
with self.assertRaises(ValueError) as e:
159+
sign.HeaderSigner(key_id='Test', secret=None, headers=[
160+
'(request-target)',
161+
'host',
162+
'date',
163+
'content-type',
164+
'digest',
165+
'content-length'
166+
])
167+
self.assertEqual(str(e.exception), "secret can't be empty")
168+
169+
def test_huge_secret(self):
170+
with self.assertRaises(ValueError) as e:
171+
sign.HeaderSigner(key_id='Test', secret='x' * 1000000, headers=[
172+
'(request-target)',
173+
'host',
174+
'date',
175+
'content-type',
176+
'digest',
177+
'content-length'
178+
])
179+
self.assertEqual(str(e.exception), "secret cant be larger than 100000 chars")
180+
181+
def test_empty_key_id(self):
182+
with self.assertRaises(ValueError) as e:
183+
sign.HeaderSigner(key_id='', secret=self.key_2048, headers=[
184+
'(request-target)',
185+
'host',
186+
'date',
187+
'content-type',
188+
'digest',
189+
'content-length'
190+
])
191+
self.assertEqual(str(e.exception), "key_id can't be empty")
192+
193+
def test_none_key_id(self):
194+
with self.assertRaises(ValueError) as e:
195+
sign.HeaderSigner(key_id=None, secret=self.key_2048, headers=[
196+
'(request-target)',
197+
'host',
198+
'date',
199+
'content-type',
200+
'digest',
201+
'content-length'
202+
])
203+
self.assertEqual(str(e.exception), "key_id can't be empty")
204+
205+
def test_huge_key_id(self):
206+
with self.assertRaises(ValueError) as e:
207+
sign.HeaderSigner(key_id='x' * 1000000, secret=self.key_2048, headers=[
208+
'(request-target)',
209+
'host',
210+
'date',
211+
'content-type',
212+
'digest',
213+
'content-length'
214+
])
215+
self.assertEqual(str(e.exception), "key_id cant be larger than 100000 chars")
216+
217+
def test_empty_method(self):
218+
hs = sign.HeaderSigner(key_id='Test', secret=self.key_2048, sign_algorithm=PSS("sha512", salt_length=0), headers=[
219+
'(request-target)',
220+
'host',
221+
'date',
222+
'content-type',
223+
'digest',
224+
'content-length'
225+
])
226+
unsigned = {
227+
'Host': self.header_host,
228+
'Date': self.header_date,
229+
'Content-Type': self.header_content_type,
230+
'Digest': self.header_digest,
231+
'Content-Length': self.header_content_length,
232+
}
233+
234+
with self.assertRaises(ValueError) as e:
235+
hs.sign(unsigned, method='', path=self.test_path)
236+
self.assertEqual(str(e.exception), 'method and path arguments required when using "(request-target)"')
237+
238+
def test_none_method(self):
239+
hs = sign.HeaderSigner(key_id='Test', secret=self.key_2048, sign_algorithm=PSS("sha512", salt_length=0), headers=[
240+
'(request-target)',
241+
'host',
242+
'date',
243+
'content-type',
244+
'digest',
245+
'content-length'
246+
])
247+
unsigned = {
248+
'Host': self.header_host,
249+
'Date': self.header_date,
250+
'Content-Type': self.header_content_type,
251+
'Digest': self.header_digest,
252+
'Content-Length': self.header_content_length,
253+
}
254+
255+
with self.assertRaises(ValueError) as e:
256+
hs.sign(unsigned, method=None, path=self.test_path)
257+
self.assertEqual(str(e.exception), 'method and path arguments required when using "(request-target)"')
258+
259+
def test_empty_path(self):
260+
hs = sign.HeaderSigner(key_id='Test', secret=self.key_2048, sign_algorithm=PSS("sha512", salt_length=0), headers=[
261+
'(request-target)',
262+
'host',
263+
'date',
264+
'content-type',
265+
'digest',
266+
'content-length'
267+
])
268+
unsigned = {
269+
'Host': self.header_host,
270+
'Date': self.header_date,
271+
'Content-Type': self.header_content_type,
272+
'Digest': self.header_digest,
273+
'Content-Length': self.header_content_length,
274+
}
275+
276+
with self.assertRaises(ValueError) as e:
277+
hs.sign(unsigned, method=self.test_method, path='')
278+
self.assertEqual(str(e.exception), 'method and path arguments required when using "(request-target)"')
279+
280+
def test_none_path(self):
281+
hs = sign.HeaderSigner(key_id='Test', secret=self.key_2048, sign_algorithm=PSS("sha512", salt_length=0), headers=[
282+
'(request-target)',
283+
'host',
284+
'date',
285+
'content-type',
286+
'digest',
287+
'content-length'
288+
])
289+
unsigned = {
290+
'Host': self.header_host,
291+
'Date': self.header_date,
292+
'Content-Type': self.header_content_type,
293+
'Digest': self.header_digest,
294+
'Content-Length': self.header_content_length,
295+
}
296+
297+
with self.assertRaises(ValueError) as e:
298+
hs.sign(unsigned, method=self.test_method, path=None)
299+
self.assertEqual(str(e.exception), 'method and path arguments required when using "(request-target)"')
300+
301+
def test_missing_header_host(self):
302+
hs = sign.HeaderSigner(key_id='Test', secret=self.key_2048, sign_algorithm=PSS("sha512", salt_length=0), headers=[
303+
'(request-target)',
304+
'host',
305+
'date',
306+
'content-type',
307+
'digest',
308+
'content-length'
309+
])
310+
unsigned = {
311+
'Date': self.header_date,
312+
'Content-Type': self.header_content_type,
313+
'Digest': self.header_digest,
314+
'Content-Length': self.header_content_length,
315+
}
316+
317+
with self.assertRaises(ValueError) as e:
318+
hs.sign(unsigned, method=self.test_method, path=self.test_path)
319+
self.assertEqual(str(e.exception), 'missing required header "host"')
320+
321+
def test_missing_header_date(self):
322+
hs = sign.HeaderSigner(key_id='Test', secret=self.key_2048, sign_algorithm=PSS("sha512", salt_length=0), headers=[
323+
'(request-target)',
324+
'host',
325+
'date',
326+
'content-type',
327+
'digest',
328+
'content-length'
329+
])
330+
unsigned = {
331+
'Host': self.header_host,
332+
'Content-Type': self.header_content_type,
333+
'Digest': self.header_digest,
334+
'Content-Length': self.header_content_length,
335+
}
336+
337+
with self.assertRaises(ValueError) as e:
338+
hs.sign(unsigned, method=self.test_method, path=self.test_path)
339+
self.assertEqual(str(e.exception), 'missing required header "date"')
340+
341+
def test_missing_header_digest(self):
342+
hs = sign.HeaderSigner(key_id='Test', secret=self.key_2048, sign_algorithm=PSS("sha512", salt_length=0), headers=[
343+
'(request-target)',
344+
'host',
345+
'date',
346+
'content-type',
347+
'digest',
348+
'content-length'
349+
])
350+
unsigned = {
351+
'Host': self.header_host,
352+
'Date': self.header_date,
353+
'Content-Type': self.header_content_type,
354+
'Content-Length': self.header_content_length,
355+
}
356+
357+
with self.assertRaises(ValueError) as e:
358+
hs.sign(unsigned, method=self.test_method, path=self.test_path)
359+
self.assertEqual(str(e.exception), 'missing required header "digest"')

httpsig/tests/test_verify.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,9 @@ def test_incorrect_headers(self):
133133
required_headers=["some-other-header"],
134134
host=HOST, method=METHOD, path=PATH,
135135
sign_header=self.sign_header, sign_algorithm=self.sign_algorithm)
136-
with self.assertRaises(Exception):
136+
with self.assertRaises(ValueError) as e:
137137
hv.verify()
138+
self.assertEqual(str(e.exception), 'some-other-header is a required header(s)')
138139

139140
def test_extra_auth_headers(self):
140141
HOST = "example.com"
@@ -171,6 +172,21 @@ def test_extra_auth_headers(self):
171172
sign_algorithm=self.sign_algorithm)
172173
self.assertTrue(hv.verify())
173174

175+
def test_empty_secret(self):
176+
with self.assertRaises(ValueError) as e:
177+
HeaderVerifier(secret='', headers={})
178+
self.assertEqual(str(e.exception), 'secret cant be empty')
179+
180+
def test_none_secret(self):
181+
with self.assertRaises(ValueError) as e:
182+
HeaderVerifier(secret=None, headers={})
183+
self.assertEqual(str(e.exception), 'secret cant be empty')
184+
185+
def test_huge_secret(self):
186+
with self.assertRaises(ValueError) as e:
187+
HeaderVerifier(secret='x' * 1000000, headers={})
188+
self.assertEqual(str(e.exception), 'secret cant be larger than 100000 chars')
189+
174190

175191
class TestVerifyHMACSHA256(TestVerifyHMACSHA1):
176192

httpsig/utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def generate_message(required_headers, headers, host=None, method=None,
6565
h = h.lower()
6666
if h == '(request-target)':
6767
if not method or not path:
68-
raise Exception('method and path arguments required when ' +
68+
raise ValueError('method and path arguments required when ' +
6969
'using "(request-target)"')
7070
signable_list.append('%s: %s %s' % (h, method.lower(), path))
7171

@@ -77,11 +77,11 @@ def generate_message(required_headers, headers, host=None, method=None,
7777
if 'host' in headers:
7878
host = headers[h]
7979
else:
80-
raise Exception('missing required header "%s"' % h)
80+
raise ValueError('missing required header "%s"' % h)
8181
signable_list.append('%s: %s' % (h, host))
8282
else:
8383
if h not in headers:
84-
raise Exception('missing required header "%s"' % h)
84+
raise ValueError('missing required header "%s"' % h)
8585

8686
signable_list.append('%s: %s' % (h, headers[h]))
8787

httpsig/verify.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ def __init__(self, headers, secret, required_headers=None, method=None,
7373
:param sign_algorithm: Required for 'hs2019' algorithm, specifies the
7474
digital signature algorithm (derived from keyId) to use.
7575
"""
76+
if not secret:
77+
raise ValueError("secret cant be empty")
78+
79+
if len(secret) > 100000:
80+
raise ValueError("secret cant be larger than 100000 chars")
81+
7682
required_headers = required_headers or ['date']
7783
self.headers = CaseInsensitiveDict(headers)
7884

@@ -112,7 +118,7 @@ def verify(self):
112118
if len(set(self.required_headers) - set(auth_headers)) > 0:
113119
error_headers = ', '.join(
114120
set(self.required_headers) - set(auth_headers))
115-
raise Exception(
121+
raise ValueError(
116122
'{} is a required header(s)'.format(error_headers))
117123

118124
signing_str = generate_message(

0 commit comments

Comments
 (0)