fix forward slash validation in S3 pre-signed url
diff --git a/localstack/services/s3/presigned_url.py b/localstack/services/s3/presigned_url.py
index 8eaaf9d7007a0..5690e24e523d7 100644
--- a/localstack/services/s3/presigned_url.py
+++ b/localstack/services/s3/presigned_url.py
@@ -22,6 +22,7 @@
     AccessDenied,
     AuthorizationQueryParametersError,
     InvalidArgument,
+    InvalidBucketName,
     SignatureDoesNotMatch,
 )
 from localstack.aws.chain import HandlerChain
@@ -32,6 +33,7 @@
     _create_invalid_argument_exc,
     capitalize_header_name_from_snake_case,
     forwarded_from_virtual_host_addressed_request,
+    is_bucket_name_valid,
 )
 from localstack.utils.strings import to_bytes
 
@@ -535,6 +537,9 @@ def __init__(self, context: RequestContext):
         self.request_query_string = qs
 
         if forwarded_from_virtual_host_addressed_request(self._headers):
+            # FIXME: maybe move this so it happens earlier in the chain when using virtual host?
+            if not is_bucket_name_valid(self._bucket):
+                raise InvalidBucketName(BucketName=self._bucket)
             netloc = self._headers.get(S3_VIRTUAL_HOST_FORWARDED_HEADER)
             self.host = netloc
             self._original_host = netloc
@@ -547,7 +552,13 @@ def __init__(self, context: RequestContext):
             netloc = urlparse.urlparse(self.request.url).netloc
             self.host = netloc
             self._original_host = netloc
-            self.path = context.request.path
+            # check that the path starts with the bucket
+            # our path has been sanitized, we should use the un-sanitized one
+            if not self.request.path.startswith(f"/{self._bucket}"):
+                splitted_path = self.request.path.split("/", maxsplit=2)
+                self.path = f"/{self._bucket}/{splitted_path[-1]}"
+            else:
+                self.path = self.request.path
 
         self.aws_request = self._get_aws_request()
 
diff --git a/tests/integration/s3/test_s3.py b/tests/integration/s3/test_s3.py
index de3d72627b814..cf15315cf979a 100644
--- a/tests/integration/s3/test_s3.py
+++ b/tests/integration/s3/test_s3.py
@@ -4822,6 +4822,42 @@ def test_presigned_url_v4_signed_headers_in_qs(
         )
         assert response.status_code == 200
 
+    @pytest.mark.aws_validated
+    @pytest.mark.xfail(
+        condition=is_old_provider(),
+        reason="Not implemented in legacy provider",
+    )
+    def test_pre_signed_url_forward_slash_bucket(
+        self,
+        s3_client,
+        s3_bucket,
+        patch_s3_skip_signature_validation_false,
+    ):
+        # PHP SDK accepts a bucket name with a forward slash when generating a pre-signed URL
+        # however the signature will not match afterwards (in AWS or with LocalStack)
+        # the error message was misleading, because by default we remove the double slash from the path, and we did not
+        # calculate the same signature as AWS
+        object_key = "temp.txt"
+        s3_client.put_object(Key=object_key, Bucket=s3_bucket, Body="123")
+
+        s3_endpoint_path_style = _endpoint_url()
+        client = _s3_client_custom_config(
+            Config(signature_version="s3v4", s3={}),
+            endpoint_url=s3_endpoint_path_style,
+        )
+
+        url = client.generate_presigned_url(
+            "put_object",
+            Params={"Bucket": s3_bucket, "Key": object_key},
+        )
+        parts = url.partition(s3_bucket)
+        # add URL encoded forward slash to the bucket name in the path
+        url_f_slash = parts[0] + "%2F" + parts[1] + parts[2]
+
+        req = requests.get(url_f_slash)
+        request_content = xmltodict.parse(req.content)
+        assert "GET\n//test-bucket" in request_content["Error"]["CanonicalRequest"]
+
     @staticmethod
     def _get_presigned_snapshot_transformers(snapshot):
         return [

This PR mostly add a test to verify behaviour raised in a community slack issue. The PHP SDK allows the user to specify a bucket with a slash in the name while generating a pre-signed URL. However, this is not allowed by AWS, and we would not raise the proper exception (and there was a small issue in our calculation, as we were using the sanitized path to create the `CanonicalRequest` used for the signature). 

I just added some proper exceptions in that case for the test to pass like it would against AWS. 