This commit is contained in:
Kenneth Kehl
2023-08-29 14:54:30 -07:00
parent 19dcd7a48b
commit 1ecb747c6d
588 changed files with 34100 additions and 23589 deletions

View File

@@ -16,7 +16,7 @@ VALID_SNS_TOPICS = Config.VALID_SNS_TOPICS
_signing_cert_cache = {}
_cert_url_re = re.compile(
r'sns\.([a-z]{1,3}(?:-gov)?-[a-z]+-[0-9]{1,2})\.amazonaws\.com',
r"sns\.([a-z]{1,3}(?:-gov)?-[a-z]+-[0-9]{1,2})\.amazonaws\.com",
)
@@ -38,28 +38,36 @@ def get_certificate(url):
def validate_arn(sns_payload):
if VALIDATE_SNS_TOPICS:
arn = sns_payload.get('TopicArn')
arn = sns_payload.get("TopicArn")
if arn not in VALID_SNS_TOPICS:
raise ValidationError("Invalid Topic Name")
def get_string_to_sign(sns_payload):
payload_type = sns_payload.get('Type')
if payload_type in ['SubscriptionConfirmation', 'UnsubscribeConfirmation']:
fields = ['Message', 'MessageId', 'SubscribeURL', 'Timestamp', 'Token', 'TopicArn', 'Type']
elif payload_type == 'Notification':
fields = ['Message', 'MessageId', 'Subject', 'Timestamp', 'TopicArn', 'Type']
payload_type = sns_payload.get("Type")
if payload_type in ["SubscriptionConfirmation", "UnsubscribeConfirmation"]:
fields = [
"Message",
"MessageId",
"SubscribeURL",
"Timestamp",
"Token",
"TopicArn",
"Type",
]
elif payload_type == "Notification":
fields = ["Message", "MessageId", "Subject", "Timestamp", "TopicArn", "Type"]
else:
raise ValidationError("Unexpected Message Type")
string_to_sign = ''
string_to_sign = ""
for field in fields:
field_value = sns_payload.get(field)
if not isinstance(field_value, str):
if field == 'Subject' and field_value is None:
if field == "Subject" and field_value is None:
continue
raise ValidationError(f"In {field}, found non-string value: {field_value}")
string_to_sign += field + '\n' + field_value + '\n'
string_to_sign += field + "\n" + field_value + "\n"
if isinstance(string_to_sign, six.text_type):
string_to_sign = string_to_sign.encode()
return string_to_sign
@@ -72,10 +80,12 @@ def validate_sns_cert(sns_payload):
Modified to swap m2crypto for oscrypto
"""
if not isinstance(sns_payload, dict):
raise ValidationError("Unexpected message type {!r}".format(type(sns_payload).__name__))
raise ValidationError(
"Unexpected message type {!r}".format(type(sns_payload).__name__)
)
# Amazon SNS currently supports signature version 1.
if sns_payload.get('SignatureVersion') != '1':
if sns_payload.get("SignatureVersion") != "1":
raise ValidationError("Wrong Signature Version (expected 1)")
validate_arn(sns_payload)
@@ -83,12 +93,15 @@ def validate_sns_cert(sns_payload):
string_to_sign = get_string_to_sign(sns_payload)
# Key signing cert url via Lambda and via webhook are slightly different
signing_cert_url = sns_payload.get('SigningCertUrl') if 'SigningCertUrl' in \
sns_payload else sns_payload.get('SigningCertURL')
signing_cert_url = (
sns_payload.get("SigningCertUrl")
if "SigningCertUrl" in sns_payload
else sns_payload.get("SigningCertURL")
)
if not isinstance(signing_cert_url, str):
raise ValidationError("Signing cert url must be a string")
cert_scheme, cert_netloc, *_ = urlparse(signing_cert_url)
if cert_scheme != 'https' or not re.match(_cert_url_re, cert_netloc):
if cert_scheme != "https" or not re.match(_cert_url_re, cert_netloc):
raise ValidationError("Cert does not appear to be from AWS")
certificate = _signing_cert_cache.get(signing_cert_url)
@@ -104,7 +117,7 @@ def validate_sns_cert(sns_payload):
oscrypto.asymmetric.load_certificate(certificate),
signature,
string_to_sign,
"sha1"
"sha1",
)
return True
except oscrypto.errors.SignatureError: