8000 better validation for MessageAttributes + right order · localstack/localstack@e2f12ac · GitHub
[go: up one dir, main page]

Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

8000
Appearance settings

Commit e2f12ac

Browse files
committed
better validation for MessageAttributes + right order
1 parent 385a141 commit e2f12ac

File tree

4 files changed

+217
-35
lines changed

4 files changed

+217
-35
lines changed

localstack-core/localstack/services/sns/provider.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -199,14 +199,15 @@ def publish_batch(
199199

200200
total_batch_size = 0
201201
message_contexts = []
202-
for entry in publish_batch_request_entries:
202+
for entry_index, entry in enumerate(publish_batch_request_entries, start=1):
203203
message_payload = entry.get("Message")
204204
message_attributes = entry.get("MessageAttributes", {})
205-
total_batch_size += get_total_publish_size(message_payload, message_attributes)
206205
if message_attributes:
207206
# if a message contains non-valid message attributes
208207
# will fail for the first non-valid message encountered, and raise ParameterValueInvalid
209-
validate_message_attributes(message_attributes)
208+
validate_message_attributes(message_attributes, position=entry_index)
209+
210+
total_batch_size += get_total_publish_size(message_payload, message_attributes)
210211

211212
# TODO: WRITE AWS VALIDATED
212213
if entry.get("MessageStructure") == "json":
@@ -532,6 +533,9 @@ def publish(
532533
f"Invalid parameter: PhoneNumber Reason: {phone_number} is not valid to publish to"
533534
)
534535

536+
if message_attributes:
537+
validate_message_attributes(message_attributes)
538+
535539
if get_total_publish_size(message, message_attributes) > MAXIMUM_MESSAGE_LENGTH:
536540
raise InvalidParameterException("Invalid parameter: Message too long")
537541

@@ -579,9 +583,6 @@ def publish(
579583
"Invalid parameter: Message Structure - JSON message body failed to parse"
580584
)
581585

582-
if message_attributes:
583-
validate_message_attributes(message_attributes)
584-
585586
if not phone_number:
586587
# use the account to get the store from the TopicArn (you can only publish in the same region as the topic)
587588
parsed_arn = parse_and_validate_topic_arn(topic_or_target_arn)
@@ -918,12 +919,15 @@ def validate_subscription_attribute(
918919
)
919920

920921

921-
def validate_message_attributes(message_attributes: MessageAttributeMap) -> None:
922+
def validate_message_attributes(
923+
message_attributes: MessageAttributeMap, position: int | None = None
924+
) -> None:
922925
"""
923926
Validate the message attributes, and raises an exception if those do not follow AWS validation
924927
See: https://docs.aws.amazon.com/sns/latest/dg/sns-message-attributes.html
925928
Regex from: https://stackoverflow.com/questions/40718851/regex-that-does-not-allow-consecutive-dots
926929
:param message_attributes: the message attributes map for the message
930+
:param position: given to give the Batch Entry position if coming from `publishBatch`
927931
:raises: InvalidParameterValueException
928932
:return: None
929933
"""
@@ -934,7 +938,18 @@ def validate_message_attributes(message_attributes: MessageAttributeMap) -> None
934938
)
935939
validate_message_attribute_name(attr_name)
936940
# `DataType` is a required field for MessageAttributeValue
937-
data_type = attr["DataType"]
941+
if (data_type := attr.get("DataType")) is None:
942+
if position:
943+
at = f"publishBatchRequestEntries.{position}.member.messageAttributes.{attr_name}.member.dataType"
944+
else:
945+
at = f"messageAttributes.{attr_name}.member.dataType"
946+
947+
raise CommonServiceException(
948+
code="ValidationError",
949+
message=f"1 validation error detected: Value null at '{at}' failed to satisfy constraint: Member must not be null",
950+
sender_fault=True,
951+
)
952+
938953
if data_type not in (
939954
"String",
940955
"Number",
@@ -943,6 +958,11 @@ def validate_message_attributes(message_attributes: MessageAttributeMap) -> None
943958
raise InvalidParameterValueException(
944959
f"The message attribute '{attr_name}' has an invalid message attribute type, the set of supported type prefixes is Binary, Number, and String."
945960
)
961+
if not any(attr_value.endswith("Value") for attr_value in attr):
962+
raise InvalidParameterValueException(
963+
f"The message attribute '{attr_name}' must contain non-empty message attribute value for message attribute type '{data_type}'."
964+
)
965+
946966
value_key_data_type = "Binary" if data_type.startswith("Binary") else "String"
947967
value_key = f"{value_key_data_type}Value"
948968
if value_key not in attr:

tests/aws/services/sns/test_sns.py

Lines changed: 31 additions & 23 deletions
2088
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import requests
1313
import xmltodict
1414
from botocore.auth import SigV4Auth
15+
from botocore.config import Config
1516
from botocore.exceptions import ClientError
1617
from cryptography import x509
1718
from cryptography.hazmat.primitives import hashes
@@ -2080,15 +2081,27 @@ def test_subscription_after_failure_to_deliver(
20802081

20812082
@markers.aws.validated
20822083
def test_empty_or_wrong_message_attributes(
2083-
self, sns_create_sqs_subscription, sns_create_topic, sqs_create_queue, snapshot, aws_client
2084+
self,
2085+
sns_create_sqs_subscription,
2086+
sns_create_topic,
2087+
sqs_create_queue,
2088+
snapshot,
2089+
aws_client_factory,
2090+
region_name,
20842091
):
20852092
topic_arn = sns_create_topic()["TopicArn"]
20862093
queue_url = sqs_create_queue()
20872094

2095
sns_create_sqs_subscription(topic_arn=topic_arn, queue_url=queue_url)
20892096

2097+
client_no_validation = aws_client_factory(
2098+
region_name=region_name, config=Config(parameter_validation=False)
2099+
).sns
2100+
20902101
wrong_message_attributes = {
20912102
"missing_string_attr": {"attr1": {"DataType": "String", "StringValue": ""}},
2103+
"fully_missing_string_attr": {"attr1": {"DataType": "String"}},
2104+
"fully_missing_data_type": {"attr1": {"StringValue": "value"}},
20922105
"missing_binary_attr": {"attr1": {"DataType": "Binary", "BinaryValue": b""}},
20932106
"str_attr_binary_value": {"attr1": {"DataType": "String", "BinaryValue": b"123"}},
20942107
"int_attr_binary_value": {"attr1": {"DataType": "Number", "BinaryValue": b"123"}},
@@ -2105,35 +2118,30 @@ def test_empty_or_wrong_message_attributes(
21052118

21062119
for error_type, msg_attrs in wrong_message_attributes.items():
21072120
with pytest.raises(ClientError) as e:
2108-
aws_client.sns.publish(
2121+
client_no_validation.publish(
21092122
TopicArn=topic_arn,
21102123
Message="test message",
21112124
MessageAttributes=msg_attrs,
21122125
)
21132126

21142127
snapshot.match(error_type, e.value.response)
21152128

2116-
with pytest.raises(ClientError) as e:
2117-
aws_client.sns.publish_batch(
2118-
TopicArn=topic_arn,
2119-
PublishBatchRequestEntries=[
2120-
{
2121-
"Id": "1",
2122-
"Message": "test-batch",
2123-
"MessageAttributes": wrong_message_attributes["missing_string_attr"],
2124-
},
2125-
{
2126-
"Id": "2",
2127-
"Message": "test-batch",
2128-
"MessageAttributes": wrong_message_attributes["str_attr_binary_value"],
2129-
},
2130-
{
2131-
"Id": "3",
2132-
"Message": "valid-batch",
2133-
},
2134-
],
2135-
)
2136-
snapshot.match("batch-exception", e.value.response)
2129+
with pytest.raises(ClientError) as e:
2130+
client_no_validation.publish_batch(
2131+
TopicArn=topic_arn,
2132+
PublishBatchRequestEntries=[
2133+
{
2134+
"Id": "1",
2135+
"Message": "test-batch",
2136+
"MessageAttributes": msg_attrs,
2137+
},
2138+
{
2139+
"Id": "3",
2140+
"Message": "valid-batch",
2141+
},
2142+
],
2143+
)
2144+
snapshot.match(f"batch-{error_type}", e.value.response)
21372145

21382146
@markers.aws.validated
21392147
def test_message_attributes_prefixes(

0 commit comments

Comments
 (0)
0