Repository: localstack/localstack

Issue:
bug: SQS batch entry IDs are not correctly validated
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Current Behavior

Sending a `send-message-batch` command to `SQS` with an entry whose ID contains an invalid character (`:`) is not failing and processed successfully.

### Expected Behavior

Command should fail with the following error:

```
An error occurred (AWS.SimpleQueueService.InvalidBatchEntryId) when calling the SendMessageBatch operation: A batch entry id can only contain alphanumeric characters, hyphens and underscores. It can be at most 80 letters long.
```

### How are you starting LocalStack?

With a docker-compose file

### Steps To Reproduce

#### How are you starting localstack (e.g., `bin/localstack` command, arguments, or `docker-compose.yml`)

```yml
# docker-compose.yml
version: '3.8'
services:
  localstack:
    image: localstack/localstack:latest
    container_name: localstack_success_snapshot
    ports:
      - '6666:4566' # LocalStack Gateway
    environment:
      DEBUG: ${DEBUG-}
      PERSISTENCE: ${PERSISTENCE-}
      LAMBDA_EXECUTOR: ${LAMBDA_EXECUTOR-}
      DOCKER_HOST: 'unix:///var/run/docker.sock'
      HOSTNAME_EXTERNAL: 'localhost'
    volumes:
      - ./localstack-bootstrap:/docker-entrypoint-initaws.d/
```

```
$ docker-compose up -d
```

#### Client commands (e.g., AWS SDK code snippet, or sequence of "awslocal" commands)

```
$ aws --endpoint-url http://localhost:6666 sqs create-queue --queue-name my-queue
$ aws --endpoint-url http://localhost:6666 sqs send-message-batch --queue-url https://localhost:6666/000000000/my-queue --entries '[{"Id":"this:should:fail","MessageBody":"Hello world!"}]'
```


### Environment

```markdown
- OS: MacOS Monterey 12.6.1 (21G217)
- LocalStack: latest
```


### Anything else?

_No response_


Discussion:
Welcome to LocalStack! Thanks for reporting your first issue and our team will be working towards fixing the issue for you or reach out for more background information. We recommend joining our [Slack Community](https://localstack.cloud/contact/) for real-time help and drop a message to LocalStack Pro Support if you are a Pro user! If you are willing to contribute towards fixing this issue, please have a look at our [contributing guidelines](https://github.com/localstack/.github/blob/main/CONTRIBUTING.md) and our [contributing guide](https://docs.localstack.cloud/contributing/).

Resolving patch:
diff --git a/localstack/services/sqs/provider.py b/localstack/services/sqs/provider.py
--- a/localstack/services/sqs/provider.py
+++ b/localstack/services/sqs/provider.py
@@ -1069,16 +1069,21 @@ def _validate_actions(self, actions: ActionNameList):
                     "WSDL for a list of valid actions. "
                 )
 
-    def _assert_batch(self, batch: List):
+    def _assert_batch(self, batch: List) -> None:
         if not batch:
             raise EmptyBatchRequest
         visited = set()
         for entry in batch:
-            # TODO: InvalidBatchEntryId
-            if entry["Id"] in visited:
+            entry_id = entry["Id"]
+            if not re.search(r"^[\w-]+$", entry_id) or len(entry_id) > 80:
+                raise InvalidBatchEntryId(
+                    "A batch entry id can only contain alphanumeric characters, hyphens and underscores. "
+                    "It can be at most 80 letters long."
+                )
+            if entry_id in visited:
                 raise BatchEntryIdsNotDistinct()
             else:
-                visited.add(entry["Id"])
+                visited.add(entry_id)
 
     def _assert_valid_batch_size(self, batch: List, max_message_size: int):
         batch_message_size = sum(


Test patch:
diff --git a/tests/integration/test_sqs.py b/tests/integration/test_sqs.py
--- a/tests/integration/test_sqs.py
+++ b/tests/integration/test_sqs.py
@@ -1291,6 +1291,21 @@ def test_too_many_entries_in_batch_request(self, sqs_client, sqs_create_queue, s
             sqs_client.send_message_batch(QueueUrl=queue_url, Entries=message_batch)
         snapshot.match("test_too_many_entries_in_batch_request", e.value.response)
 
+    @pytest.mark.aws_validated
+    @pytest.mark.skip_snapshot_verify(paths=["$..Error.Detail"])
+    def test_invalid_batch_id(self, sqs_client, sqs_create_queue, snapshot):
+        queue_name = f"queue-{short_uid()}"
+        queue_url = sqs_create_queue(QueueName=queue_name)
+        message_batch = [
+            {
+                "Id": f"message:{(batch_id:=short_uid())}",
+                "MessageBody": f"messageBody-{batch_id}",
+            }
+        ]
+        with pytest.raises(ClientError) as e:
+            sqs_client.send_message_batch(QueueUrl=queue_url, Entries=message_batch)
+        snapshot.match("test_invalid_batch_id", e.value.response)
+
     @pytest.mark.aws_validated
     def test_publish_get_delete_message_batch(self, sqs_client, sqs_create_queue):
         message_count = 10
diff --git a/tests/integration/test_sqs.snapshot.json b/tests/integration/test_sqs.snapshot.json
--- a/tests/integration/test_sqs.snapshot.json
+++ b/tests/integration/test_sqs.snapshot.json
@@ -591,5 +591,22 @@
         }
       }
     }
+  },
+  "tests/integration/test_sqs.py::TestSqsProvider::test_invalid_batch_id": {
+    "recorded-date": "12-12-2022, 21:57:35",
+    "recorded-content": {
+      "test_invalid_batch_id": {
+        "Error": {
+          "Code": "AWS.SimpleQueueService.InvalidBatchEntryId",
+          "Detail": null,
+          "Message": "A batch entry id can only contain alphanumeric characters, hyphens and underscores. It can be at most 80 letters long.",
+          "Type": "Sender"
+        },
+        "ResponseMetadata": {
+          "HTTPHeaders": {},
+          "HTTPStatusCode": 400
+        }
+      }
+    }
   }
-}
\ No newline at end of file
+}