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

Commit e0a9ae5

Browse files
committed
Merge branch 'update-input-validation'
2 parents 3ec216e + 78b17af commit e0a9ae5

5 files changed

Lines changed: 255 additions & 6 deletions

File tree

httpsig/sign.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,18 @@ def __init__(self, key_id, secret, algorithm=None, sign_algorithm=None, headers=
108108
if algorithm is None:
109109
algorithm = DEFAULT_ALGORITHM
110110

111+
if not key_id:
112+
raise ValueError("key_id can't be empty")
113+
114+
if len(key_id) > 100000:
115+
raise ValueError("key_id cant be larger than 100000 chars")
116+
117+
if not secret:
118+
raise ValueError("secret can't be empty")
119+
120+
if len(secret) > 100000:
121+
raise ValueError("secret cant be larger than 100000 chars")
122+
111123
super(HeaderSigner, self).__init__(secret=secret, algorithm=algorithm, sign_algorithm=sign_algorithm)
112124
self.headers = headers or ['date']
113125
self.signature_template = build_signature_template(

httpsig/tests/test_signature.py

Lines changed: 216 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,219 @@ def test_unsupported_hash_algorithm(self):
164164
def test_deprecated_hash_algorithm(self):
165165
with pytest.raises(HttpSigException) as e:
166166
sign.HeaderSigner(key_id='Test', secret=self.key_2048, sign_algorithm=PSS("sha256", salt_length=0))
167-
self.assertEqual(str(e.value), "Hash algorithm: sha256 is deprecated. Please use: sha512")
167+
self.assertEqual(str(e.value), "Hash algorithm: sha256 is deprecated. Please use: sha512")
168+
def test_empty_secret(self):
169+
with self.assertRaises(ValueError) as e:
170+
sign.HeaderSigner(key_id='Test', secret='', headers=[
171+
'(request-target)',
172+
'host',
173+
'date',
174+
'content-type',
175+
'digest',
176+
'content-length'
177+
])
178+
self.assertEqual(str(e.exception), "secret can't be empty")
179+
180+
def test_none_secret(self):
181+
with self.assertRaises(ValueError) as e:
182+
sign.HeaderSigner(key_id='Test', secret=None, headers=[
183+
'(request-target)',
184+
'host',
185+
'date',
186+
'content-type',
187+
'digest',
188+
'content-length'
189+
])
190+
self.assertEqual(str(e.exception), "secret can't be empty")
191+
192+
def test_huge_secret(self):
193+
with self.assertRaises(ValueError) as e:
194+
sign.HeaderSigner(key_id='Test', secret='x' * 1000000, headers=[
195+
'(request-target)',
196+
'host',
197+
'date',
198+
'content-type',
199+
'digest',
200+
'content-length'
201+
])
202+
self.assertEqual(str(e.exception), "secret cant be larger than 100000 chars")
203+
204+
def test_empty_key_id(self):
205+
with self.assertRaises(ValueError) as e:
206+
sign.HeaderSigner(key_id='', secret=self.key, headers=[
207+
'(request-target)',
208+
'host',
209+
'date',
210+
'content-type',
211+
'digest',
212+
'content-length'
213+
])
214+
self.assertEqual(str(e.exception), "key_id can't be empty")
215+
216+
def test_none_key_id(self):
217+
with self.assertRaises(ValueError) as e:
218+
sign.HeaderSigner(key_id=None, secret=self.key, headers=[
219+
'(request-target)',
220+
'host',
221+
'date',
222+
'content-type',
223+
'digest',
224+
'content-length'
225+
])
226+
self.assertEqual(str(e.exception), "key_id can't be empty")
227+
228+
def test_huge_key_id(self):
229+
with self.assertRaises(ValueError) as e:
230+
sign.HeaderSigner(key_id='x' * 1000000, secret=self.key, headers=[
231+
'(request-target)',
232+
'host',
233+
'date',
234+
'content-type',
235+
'digest',
236+
'content-length'
237+
])
238+
self.assertEqual(str(e.exception), "key_id cant be larger than 100000 chars")
239+
240+
def test_empty_method(self):
241+
hs = sign.HeaderSigner(key_id='Test', secret=self.key, headers=[
242+
'(request-target)',
243+
'host',
244+
'date',
245+
'content-type',
246+
'digest',
247+
'content-length'
248+
])
249+
unsigned = {
250+
'Host': self.header_host,
251+
'Date': self.header_date,
252+
'Content-Type': self.header_content_type,
253+
'Digest': self.header_digest,
254+
'Content-Length': self.header_content_length,
255+
}
256+
257+
with self.assertRaises(ValueError) as e:
258+
hs.sign(unsigned, method='', path=self.test_path)
259+
self.assertEqual(str(e.exception), 'method and path arguments required when using "(request-target)"')
260+
261+
def test_none_method(self):
262+
hs = sign.HeaderSigner(key_id='Test', secret=self.key, headers=[
263+
'(request-target)',
264+
'host',
265+
'date',
266+
'content-type',
267+
'digest',
268+
'content-length'
269+
])
270+
unsigned = {
271+
'Host': self.header_host,
272+
'Date': self.header_date,
273+
'Content-Type': self.header_content_type,
274+
'Digest': self.header_digest,
275+
'Content-Length': self.header_content_length,
276+
}
277+
278+
with self.assertRaises(ValueError) as e:
279+
hs.sign(unsigned, method=None, path=self.test_path)
280+
self.assertEqual(str(e.exception), 'method and path arguments required when using "(request-target)"')
281+
282+
def test_empty_path(self):
283+
hs = sign.HeaderSigner(key_id='Test', secret=self.key, headers=[
284+
'(request-target)',
285+
'host',
286+
'date',
287+
'content-type',
288+
'digest',
289+
'content-length'
290+
])
291+
unsigned = {
292+
'Host': self.header_host,
293+
'Date': self.header_date,
294+
'Content-Type': self.header_content_type,
295+
'Digest': self.header_digest,
296+
'Content-Length': self.header_content_length,
297+
}
298+
299+
with self.assertRaises(ValueError) as e:
300+
hs.sign(unsigned, method=self.test_method, path='')
301+
self.assertEqual(str(e.exception), 'method and path arguments required when using "(request-target)"')
302+
303+
def test_none_path(self):
304+
hs = sign.HeaderSigner(key_id='Test', secret=self.key, headers=[
305+
'(request-target)',
306+
'host',
307+
'date',
308+
'content-type',
309+
'digest',
310+
'content-length'
311+
])
312+
unsigned = {
313+
'Host': self.header_host,
314+
'Date': self.header_date,
315+
'Content-Type': self.header_content_type,
316+
'Digest': self.header_digest,
317+
'Content-Length': self.header_content_length,
318+
}
319+
320+
with self.assertRaises(ValueError) as e:
321+
hs.sign(unsigned, method=self.test_method, path=None)
322+
self.assertEqual(str(e.exception), 'method and path arguments required when using "(request-target)"')
323+
324+
def test_missing_header_host(self):
325+
hs = sign.HeaderSigner(key_id='Test', secret=self.key, headers=[
326+
'(request-target)',
327+
'host',
328+
'date',
329+
'content-type',
330+
'digest',
331+
'content-length'
332+
])
333+
unsigned = {
334+
'Date': self.header_date,
335+
'Content-Type': self.header_content_type,
336+
'Digest': self.header_digest,
337+
'Content-Length': self.header_content_length,
338+
}
339+
340+
with self.assertRaises(ValueError) as e:
341+
hs.sign(unsigned, method=self.test_method, path=self.test_path)
342+
self.assertEqual(str(e.exception), 'missing required header "host"')
343+
344+
def test_missing_header_date(self):
345+
hs = sign.HeaderSigner(key_id='Test', secret=self.key, headers=[
346+
'(request-target)',
347+
'host',
348+
'date',
349+
'content-type',
350+
'digest',
351+
'content-length'
352+
])
353+
unsigned = {
354+
'Host': self.header_host,
355+
'Content-Type': self.header_content_type,
356+
'Digest': self.header_digest,
357+
'Content-Length': self.header_content_length,
358+
}
359+
360+
with self.assertRaises(ValueError) as e:
361+
hs.sign(unsigned, method=self.test_method, path=self.test_path)
362+
self.assertEqual(str(e.exception), 'missing required header "date"')
363+
364+
def test_missing_header_digest(self):
365+
hs = sign.HeaderSigner(key_id='Test', secret=self.key, headers=[
366+
'(request-target)',
367+
'host',
368+
'date',
369+
'content-type',
370+
'digest',
371+
'content-length'
372+
])
373+
unsigned = {
374+
'Host': self.header_host,
375+
'Date': self.header_date,
376+
'Content-Type': self.header_content_type,
377+
'Content-Length': self.header_content_length,
378+
}
379+
380+
with self.assertRaises(ValueError) as e:
381+
hs.sign(unsigned, method=self.test_method, path=self.test_path)
382+
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
@@ -134,8 +134,9 @@ def test_incorrect_headers(self):
134134
required_headers=["some-other-header"],
135135
host=HOST, method=METHOD, path=PATH,
136136
sign_header=self.sign_header, sign_algorithm=self.sign_algorithm)
137-
with self.assertRaises(Exception):
137+
with self.assertRaises(ValueError) as e:
138138
hv.verify()
139+
self.assertEqual(str(e.exception), 'some-other-header is a required header(s)')
139140

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

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

176192
class TestVerifyHMACSHA256(TestVerifyHMACSHA1):
177193

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)