Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions tests/test_utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,38 @@ def example_function(*args, **kwargs):
mock_is_github_ip.assert_called_once()
self.assertEqual(mock_abort.call_count, 7)
self.assertEqual(response, "Test Success")

def test_is_valid_signature(self):
"""Test is_valid_signature with valid and invalid inputs."""
import hashlib
import hmac
from utility import is_valid_signature

key = "secret_key_123"
payload = b'{"ref": "refs/heads/master"}'
mac = hmac.new(b"secret_key_123", payload, hashlib.sha256)
valid_header = f"sha256={mac.hexdigest()}"

# Valid signature with bytes and str payload
self.assertTrue(is_valid_signature(valid_header, payload, key))
self.assertTrue(
is_valid_signature(valid_header, payload.decode(), key))

# Invalid signatures / tamper
self.assertFalse(is_valid_signature("sha256=wrong", payload, key))

# Missing or invalid signature formats
self.assertFalse(is_valid_signature(None, payload, key))
self.assertFalse(is_valid_signature("", payload, key))
self.assertFalse(is_valid_signature(12345, payload, key))
self.assertFalse(is_valid_signature("no_equal_sign", payload, key))

# Unknown or non-callable algorithms
self.assertFalse(is_valid_signature("unknown=1234", payload, key))
self.assertFalse(is_valid_signature("__dict__=1234", payload, key))
self.assertFalse(
is_valid_signature("algorithms_guaranteed=1", payload, key))

# None data or key
self.assertFalse(is_valid_signature(valid_header, None, key))
self.assertFalse(is_valid_signature(valid_header, payload, None))
41 changes: 35 additions & 6 deletions utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,42 @@ def is_valid_signature(x_hub_signature, data, private_key):
:param x_hub_signature: Signature to check
:type x_hub_signature: str
:param data: Signature's data
:type data: bytearray
:type data: bytearray or bytes or str
:param private_key: Signature's token
:type private_key: str
:type private_key: str or bytes
"""
if not x_hub_signature or not isinstance(x_hub_signature, str):
return False

if '=' not in x_hub_signature:
return False

hash_algorithm, github_signature = x_hub_signature.split('=', 1)
algorithm = hashlib.__dict__.get(hash_algorithm)
encoded_key = bytes(private_key, 'latin-1')
mac = hmac.new(encoded_key, msg=data, digestmod=algorithm)
hash_algorithm = hash_algorithm.lower()

if hash_algorithm not in hashlib.algorithms_available:
return False

algorithm = getattr(hashlib, hash_algorithm, None)
if not callable(algorithm):
return False

return hmac.compare_digest(mac.hexdigest(), github_signature)
if private_key is None:
return False
if isinstance(private_key, str):
encoded_key = private_key.encode('latin-1')
elif isinstance(private_key, (bytes, bytearray)):
encoded_key = bytes(private_key)
else:
return False

if data is None:
return False
if isinstance(data, str):
data = data.encode('utf-8')

try:
mac = hmac.new(encoded_key, msg=data, digestmod=algorithm)
return hmac.compare_digest(mac.hexdigest(), github_signature)
except (ValueError, TypeError):
return False