Skip to content

Commit 85b6d17

Browse files
fix: use space character as the separator for user-agent header instead of comma (#1156)
* Use space character as the separator for user-agent header instead of comma * Update changelog * chore: revert changes to settings file More because I don't know what they will affect, due to not using Rider myself. * docs(changelog): attribute contributor * refactor: add remark on why user-agent header is treated differently * refactor: minor changes --------- Co-authored-by: FantasticFiasco <mattias@kindb.org>
1 parent 8596e6b commit 85b6d17

17 files changed

+56
-2
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ This project adheres to [Semantic Versioning](http://semver.org/) and is followi
66

77
## Unreleased
88

9+
### :syringe: Fixed
10+
11+
- [#1155](https://github.com/FantasticFiasco/aws-signature-version-4/issues/1155) Multi-part user-agent header is joined incorrectly leading to AWS signature failures (contribution by [@mungojam](https://github.com/mungojam))
12+
913
## [4.0.5] - 2024-05-01
1014

1115
### :syringe: Fixed

src/Private/CanonicalRequest.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,10 @@ public static (string, string) Build(
112112

113113
foreach (var header in sortedHeaders)
114114
{
115-
builder.Append($"{header.Key}:{string.Join(HeaderValueSeparator, header.Value)}\n");
115+
// The 'user-agent' header should be treated differently, as discovered in
116+
// https://github.com/FantasticFiasco/aws-signature-version-4/issues/1155
117+
var separator = header.Key.ToLowerInvariant() == "user-agent" ? " " : HeaderValueSeparator;
118+
builder.Append($"{header.Key}:{string.Join(separator, header.Value)}\n");
116119
}
117120

118121
builder.Append('\n');

test/Integration/ApiGateway/AwsSignatureHandlerShould.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public AwsSignatureHandlerShould(IntegrationTestContext context, TestSuiteContex
4141
[Theory]
4242
[InlineData("get-header-key-duplicate")]
4343
[InlineData("get-header-value-multiline")]
44+
[InlineData("get-header-value-multiple-user-agent")]
4445
[InlineData("get-header-value-order")]
4546
[InlineData("get-header-value-trim")]
4647
[InlineData("get-unreserved")]
@@ -86,6 +87,7 @@ public async Task PassTestSuiteGivenUserWithPermissions(params string[] scenario
8687
[Theory]
8788
[InlineData("get-header-key-duplicate")]
8889
[InlineData("get-header-value-multiline")]
90+
[InlineData("get-header-value-multiple-user-agent")]
8991
[InlineData("get-header-value-order")]
9092
[InlineData("get-header-value-trim")]
9193
[InlineData("get-unreserved")]

test/Integration/ApiGateway/SendAsyncShould.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ public async Task SucceedGivenHttpCompletionOptionAndCancellationTokenAndImmutab
298298
[Theory]
299299
[InlineData("get-header-key-duplicate")]
300300
[InlineData("get-header-value-multiline")]
301+
[InlineData("get-header-value-multiple-user-agent")]
301302
[InlineData("get-header-value-order")]
302303
[InlineData("get-header-value-trim")]
303304
[InlineData("get-unreserved")]
@@ -347,6 +348,7 @@ public async Task PassTestSuiteGivenUserWithPermissions(params string[] scenario
347348
[Theory]
348349
[InlineData("get-header-key-duplicate")]
349350
[InlineData("get-header-value-multiline")]
351+
[InlineData("get-header-value-multiple-user-agent")]
350352
[InlineData("get-header-value-order")]
351353
[InlineData("get-header-value-trim")]
352354
[InlineData("get-unreserved")]

test/Integration/ApiGateway/SendShould.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ public async Task SucceedGivenHttpCompletionOptionAndCancellationTokenAndImmutab
298298
[Theory]
299299
[InlineData("get-header-key-duplicate")]
300300
[InlineData("get-header-value-multiline")]
301+
[InlineData("get-header-value-multiple-user-agent")]
301302
[InlineData("get-header-value-order")]
302303
[InlineData("get-header-value-trim")]
303304
[InlineData("get-unreserved")]
@@ -347,6 +348,7 @@ public void PassTestSuiteGivenUserWithPermissions(params string[] scenarioName)
347348
[Theory]
348349
[InlineData("get-header-key-duplicate")]
349350
[InlineData("get-header-value-multiline")]
351+
[InlineData("get-header-value-multiple-user-agent")]
350352
[InlineData("get-header-value-order")]
351353
[InlineData("get-header-value-trim")]
352354
[InlineData("get-unreserved")]

test/Integration/S3/AwsSignatureHandlerShould.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public AwsSignatureHandlerShould(IntegrationTestContext context, TestSuiteContex
2323
[Theory]
2424
[InlineData("get-header-key-duplicate")]
2525
[InlineData("get-header-value-multiline")]
26+
[InlineData("get-header-value-multiple-user-agent")]
2627
[InlineData("get-header-value-order")]
2728
[InlineData("get-header-value-trim")]
2829
[InlineData("get-unreserved")]
@@ -70,6 +71,7 @@ public async Task PassTestSuiteGivenUserWithPermissions(params string[] scenario
7071
[Theory]
7172
[InlineData("get-header-key-duplicate")]
7273
[InlineData("get-header-value-multiline")]
74+
[InlineData("get-header-value-multiple-user-agent")]
7375
[InlineData("get-header-value-order")]
7476
[InlineData("get-header-value-trim")]
7577
[InlineData("get-unreserved")]

test/Integration/S3/SendAsyncShould.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public SendAsyncShould(IntegrationTestContext context, TestSuiteContext testSuit
2323
[Theory]
2424
[InlineData("get-header-key-duplicate")]
2525
[InlineData("get-header-value-multiline")]
26+
[InlineData("get-header-value-multiple-user-agent")]
2627
[InlineData("get-header-value-order")]
2728
[InlineData("get-header-value-trim")]
2829
[InlineData("get-unreserved")]
@@ -74,6 +75,7 @@ public async Task PassTestSuiteGivenUserWithPermissions(params string[] scenario
7475
[Theory]
7576
[InlineData("get-header-key-duplicate")]
7677
[InlineData("get-header-value-multiline")]
78+
[InlineData("get-header-value-multiple-user-agent")]
7779
[InlineData("get-header-value-order")]
7880
[InlineData("get-header-value-trim")]
7981
[InlineData("get-unreserved")]

test/Integration/S3/SendShould.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public SendShould(IntegrationTestContext context, TestSuiteContext testSuiteCont
2222
[Theory]
2323
[InlineData("get-header-key-duplicate")]
2424
[InlineData("get-header-value-multiline")]
25+
[InlineData("get-header-value-multiple-user-agent")]
2526
[InlineData("get-header-value-order")]
2627
[InlineData("get-header-value-trim")]
2728
[InlineData("get-unreserved")]
@@ -73,6 +74,7 @@ public async Task PassTestSuiteGivenUserWithPermissions(params string[] scenario
7374
[Theory]
7475
[InlineData("get-header-key-duplicate")]
7576
[InlineData("get-header-value-multiline")]
77+
[InlineData("get-header-value-multiple-user-agent")]
7678
[InlineData("get-header-value-order")]
7779
[InlineData("get-header-value-trim")]
7880
[InlineData("get-unreserved")]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;user-agent;x-amz-date, Signature=01fe72f4f4f40689e7868c6d448c29133334c2ad2e8f5f08ae5537155989cee0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
GET
2+
/
3+
4+
host:example.amazonaws.com
5+
my-header1:value3,value2
6+
user-agent:value4 value1
7+
x-amz-date:20150830T123600Z
8+
9+
host;my-header1;user-agent;x-amz-date
10+
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
GET / HTTP/1.1
2+
Host:example.amazonaws.com
3+
User-Agent:value4
4+
User-Agent:value1
5+
My-Header1:value3
6+
My-Header1:value2
7+
X-Amz-Date:20150830T123600Z
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
GET / HTTP/1.1
2+
Host:example.amazonaws.com
3+
My-Header1:value3
4+
My-Header1:value2
5+
User-Agent:value4
6+
User-Agent:value1
7+
X-Amz-Date:20150830T123600Z
8+
Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=08c7e5a9acfcfeb3ab6b2185e75ce8b1deb5e634ec47601a50643f830c755c01
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
AWS4-HMAC-SHA256
2+
20150830T123600Z
3+
20150830/us-east-1/service/aws4_request
4+
cdfe07e1b7f30c9fcc39be612b73a3a097454d4adea5b719884be5cb0f46f28e

test/Unit/Private/AuthorizationHeaderShould.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public AuthorizationHeaderShould(TestSuiteContext context)
1818
[Theory]
1919
[InlineData("get-header-key-duplicate")]
2020
[InlineData("get-header-value-multiline")]
21+
[InlineData("get-header-value-multiple-user-agent")]
2122
[InlineData("get-header-value-order")]
2223
[InlineData("get-header-value-trim")]
2324
[InlineData("get-unreserved")]

test/Unit/Private/CanonicalRequestShould.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public CanonicalRequestShould(TestSuiteContext context)
2323
[Theory]
2424
[InlineData("get-header-key-duplicate")]
2525
[InlineData("get-header-value-multiline")]
26+
[InlineData("get-header-value-multiple-user-agent")]
2627
[InlineData("get-header-value-order")]
2728
[InlineData("get-header-value-trim")]
2829
[InlineData("get-unreserved")]

test/Unit/Private/SignerShould.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public SignerShould(TestSuiteContext context)
3030
[Theory]
3131
[InlineData("get-header-key-duplicate")]
3232
[InlineData("get-header-value-multiline")]
33+
[InlineData("get-header-value-multiple-user-agent")]
3334
[InlineData("get-header-value-order")]
3435
[InlineData("get-header-value-trim")]
3536
[InlineData("get-unreserved")]
@@ -85,6 +86,7 @@ public async Task PassTestSuiteAsync(params string[] scenarioName)
8586
[Theory]
8687
[InlineData("get-header-key-duplicate")]
8788
[InlineData("get-header-value-multiline")]
89+
[InlineData("get-header-value-multiple-user-agent")]
8890
[InlineData("get-header-value-order")]
8991
[InlineData("get-header-value-trim")]
9092
[InlineData("get-unreserved")]
@@ -204,7 +206,7 @@ public void RespectBaseAddress(string baseAddress, string requestUri, string exp
204206
#region Not add X-Amz-Content-SHA256 content header
205207

206208
// Only requests to S3 should add the "X-Amz-Content-SHA256" header
207-
209+
208210
[Fact]
209211
public async Task NotAddXAmzContentHeaderAsync()
210212
{

test/Unit/Private/StringToSignShould.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public StringToSignShould(TestSuiteContext context)
1818
[Theory]
1919
[InlineData("get-header-key-duplicate")]
2020
[InlineData("get-header-value-multiline")]
21+
[InlineData("get-header-value-multiple-user-agent")]
2122
[InlineData("get-header-value-order")]
2223
[InlineData("get-header-value-trim")]
2324
[InlineData("get-unreserved")]

0 commit comments

Comments
 (0)