Skip to content

Commit fed8e9f

Browse files
authored
Fix issue with double encode of path in canonical request (#451)
1 parent 5baae09 commit fed8e9f

File tree

6 files changed

+29
-15
lines changed

6 files changed

+29
-15
lines changed

packages/aws-sdk-signers/src/aws_sdk_signers/signers.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class SigV4SigningProperties(TypedDict, total=False):
4141
date: str
4242
payload_signing_enabled: bool
4343
content_checksum_enabled: bool
44+
uri_encode_path: bool
4445

4546

4647
class SigV4Signer:
@@ -232,7 +233,9 @@ def canonical_request(
232233
canonical_payload = self._format_canonical_payload(
233234
request=request, signing_properties=signing_properties
234235
)
235-
canonical_path = self._format_canonical_path(path=request.destination.path)
236+
canonical_path = self._format_canonical_path(
237+
path=request.destination.path, signing_properties=signing_properties
238+
)
236239
canonical_query = self._format_canonical_query(query=request.destination.query)
237240
normalized_fields = self._normalize_signing_fields(request=request)
238241
canonical_fields = self._format_canonical_fields(fields=normalized_fields)
@@ -290,11 +293,17 @@ def _scope(self, signing_properties: SigV4SigningProperties) -> str:
290293
# Scope format: <YYYYMMDD>/<AWS Region>/<AWS Service>/aws4_request
291294
return f"{formatted_date}/{region}/{service}/aws4_request"
292295

293-
def _format_canonical_path(self, *, path: str | None) -> str:
296+
def _format_canonical_path(
297+
self, *, path: str | None, signing_properties: SigV4SigningProperties
298+
) -> str:
294299
if path is None:
295300
path = "/"
296-
normalized_path = _remove_dot_segments(path)
297-
return quote(string=normalized_path, safe="/%")
301+
302+
if signing_properties.get("uri_encode_path", True):
303+
normalized_path = _remove_dot_segments(path)
304+
return quote(string=normalized_path, safe="/")
305+
else:
306+
return _remove_dot_segments(path, remove_consecutive_slashes=False)
298307

299308
def _format_canonical_query(self, *, query: str | None) -> str:
300309
if query is None:
@@ -596,7 +605,7 @@ async def canonical_request(
596605
request=request, signing_properties=signing_properties
597606
)
598607
canonical_path = await self._format_canonical_path(
599-
path=request.destination.path
608+
path=request.destination.path, signing_properties=signing_properties
600609
)
601610
canonical_query = await self._format_canonical_query(
602611
query=request.destination.query
@@ -658,11 +667,17 @@ async def _scope(self, signing_properties: SigV4SigningProperties) -> str:
658667
# Scope format: <YYYYMMDD>/<AWS Region>/<AWS Service>/aws4_request
659668
return f"{formatted_date}/{region}/{service}/aws4_request"
660669

661-
async def _format_canonical_path(self, *, path: str | None) -> str:
670+
async def _format_canonical_path(
671+
self, *, path: str | None, signing_properties: SigV4SigningProperties
672+
) -> str:
662673
if path is None:
663674
path = "/"
664-
normalized_path = _remove_dot_segments(path)
665-
return quote(string=normalized_path, safe="/%")
675+
676+
if signing_properties.get("uri_encode_path", True):
677+
normalized_path = _remove_dot_segments(path)
678+
return quote(string=normalized_path, safe="/")
679+
else:
680+
return _remove_dot_segments(path, remove_consecutive_slashes=False)
666681

667682
async def _format_canonical_query(self, *, query: str | None) -> str:
668683
if query is None:
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=652487583200325589f1fba4c7e578f72c47cb61beeca81406b39ddec1366741
1+
AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=446b817944c553435b35e813c261ff4e161fff982d1bacdef1c87f6785dd1662

packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-space/get-space.creq

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
GET
2-
/example%20space/
2+
/example%2520space/
33

44
host:example.amazonaws.com
55
x-amz-date:20150830T123600Z
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
GET /example%20space/ HTTP/1.1
22
Host:example.amazonaws.com
33
X-Amz-Date:20150830T123600Z
4-
Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=652487583200325589f1fba4c7e578f72c47cb61beeca81406b39ddec1366741
4+
Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=446b817944c553435b35e813c261ff4e161fff982d1bacdef1c87f6785dd1662
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
AWS4-HMAC-SHA256
22
20150830T123600Z
33
20150830/us-east-1/service/aws4_request
4-
63ee75631ed7234ae61b5f736dfc7754cdccfedbff4b5128a915706ee9390d86
4+
6a04b36fa5a84d8d24b4287d506da70f4d5289a1772e6c608495a841a8f38627

packages/aws-sdk-signers/tests/unit/auth/test_sigv4.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,7 @@ def _test_signature_version_4_sync(test_case_name: str, signer: SigV4Signer) ->
9999
request = create_request_from_raw_request(test_case)
100100

101101
signing_props = SigV4SigningProperties(
102-
region=REGION,
103-
service=SERVICE,
104-
date=DATE_STR,
102+
region=REGION, service=SERVICE, date=DATE_STR
105103
)
106104
with pytest.warns(AWSSDKWarning):
107105
actual_canonical_request = signer.canonical_request(
@@ -198,6 +196,7 @@ def create_request_from_raw_request(
198196
query = ""
199197
host = raw.headers.get("host", "")
200198
url = URI(host=host, path=path, query=query)
199+
201200
return AWSRequest(
202201
destination=url,
203202
method=request_method,

0 commit comments

Comments
 (0)