Skip to content

Commit 04119bd

Browse files
refactor: don't re-use redacted headers to redact query string params
1 parent 8a73eb5 commit 04119bd

File tree

5 files changed

+112
-23
lines changed

5 files changed

+112
-23
lines changed

src/EntityDb.Mvc.Tests/Agents/HttpContextAgentSignatureTests.cs

Lines changed: 86 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,23 @@ namespace EntityDb.Mvc.Tests.Agents
99
public class HttpContextAgentSignatureTests
1010
{
1111
[Fact]
12-
public void GivenNoDoNotRecordHeaders_WhenHttpContextHasSingleHeader_ThenAgentSignatureHasSingleHeader()
12+
public void GivenNoRedactedHeaders_WhenHttpContextHasHeader_ThenAgentSignatureHasHeaderValue()
1313
{
1414
// ARRANGE
1515

16-
const string RecordHeaderName = nameof(RecordHeaderName);
17-
const string RecordHeaderValue = nameof(RecordHeaderValue);
16+
const string HeaderName = nameof(HeaderName);
17+
const string HeaderValue = nameof(HeaderValue);
1818

1919
var httpContextAgentOptions = new HttpContextAgentSignatureOptions
2020
{
21-
DoNotRecordHeaders = Array.Empty<string>()
21+
RedactedHeaders = Array.Empty<string>()
2222
};
2323

2424
var httpContext = HttpContextSeeder.CreateHttpContext(new HttpContextSeederOptions
2525
{
2626
Headers = new()
2727
{
28-
[RecordHeaderName] = new[] { RecordHeaderValue }
28+
[HeaderName] = new[] { HeaderValue }
2929
}
3030
});
3131

@@ -36,28 +36,31 @@ public void GivenNoDoNotRecordHeaders_WhenHttpContextHasSingleHeader_ThenAgentSi
3636
// ASSERT
3737

3838
agentSignature.Request.Headers.Length.ShouldBe(1);
39-
agentSignature.Request.Headers[0].Name.ShouldBe(RecordHeaderName);
39+
agentSignature.Request.Headers[0].Name.ShouldBe(HeaderName);
4040
agentSignature.Request.Headers[0].Values.Length.ShouldBe(1);
41-
agentSignature.Request.Headers[0].Values[0].ShouldBe(RecordHeaderValue);
41+
agentSignature.Request.Headers[0].Values[0].ShouldBe(HeaderValue);
4242
}
4343

4444
[Fact]
45-
public void GivenDoNotRecordHeader_WhenHttpContextContainsOnlyThatHeader_ThenAgentSignatureHasNoHeaders()
45+
public void GivenRedactedHeader_WhenHttpContextContainsOnlyThatHeader_ThenAgentSignatureHasRedactedHeader()
4646
{
4747
// ARRANGE
4848

49-
const string DoNotRecordHeader = nameof(DoNotRecordHeader);
49+
const string HeaderName = nameof(HeaderName);
50+
const string HeaderValue = nameof(HeaderValue);
51+
const string RedactedValue = nameof(RedactedValue);
5052

5153
var httpContextAgentOptions = new HttpContextAgentSignatureOptions
5254
{
53-
DoNotRecordHeaders = new[] { DoNotRecordHeader }
55+
RedactedHeaders = new[] { HeaderName },
56+
RedactedValue = RedactedValue,
5457
};
5558

5659
var httpContext = HttpContextSeeder.CreateHttpContext(new HttpContextSeederOptions
5760
{
5861
Headers = new()
5962
{
60-
[DoNotRecordHeader] = Array.Empty<string>()
63+
[HeaderName] = new[] { HeaderValue }
6164
}
6265
});
6366

@@ -67,7 +70,78 @@ public void GivenDoNotRecordHeader_WhenHttpContextContainsOnlyThatHeader_ThenAge
6770

6871
// ASSERT
6972

70-
agentSignature.Request.Headers.ShouldBeEmpty();
73+
agentSignature.Request.Headers.Length.ShouldBe(1);
74+
agentSignature.Request.Headers[0].Name.ShouldBe(HeaderName);
75+
agentSignature.Request.Headers[0].Values.Length.ShouldBe(1);
76+
agentSignature.Request.Headers[0].Values[0].ShouldBe(RedactedValue);
77+
}
78+
79+
[Fact]
80+
public void GivenNoRedactedQueryStringParams_WhenHttpContextHasQueryStringParam_ThenAgentSignatureHasQueryStringParamValue()
81+
{
82+
// ARRANGE
83+
84+
const string QueryStringParamName = nameof(QueryStringParamName);
85+
const string QueryStringParamValue = nameof(QueryStringParamValue);
86+
87+
var httpContextAgentOptions = new HttpContextAgentSignatureOptions
88+
{
89+
RedactedQueryStringParams = Array.Empty<string>()
90+
};
91+
92+
var httpContext = HttpContextSeeder.CreateHttpContext(new HttpContextSeederOptions
93+
{
94+
QueryStringParams = new()
95+
{
96+
[QueryStringParamName] = new[] { QueryStringParamValue }
97+
}
98+
});
99+
100+
// ACT
101+
102+
var agentSignature = HttpContextAgentSignature.GetSnapshot(httpContext, httpContextAgentOptions);
103+
104+
// ASSERT
105+
106+
agentSignature.Request.QueryStringParams.Length.ShouldBe(1);
107+
agentSignature.Request.QueryStringParams[0].Name.ShouldBe(QueryStringParamName);
108+
agentSignature.Request.QueryStringParams[0].Values.Length.ShouldBe(1);
109+
agentSignature.Request.QueryStringParams[0].Values[0].ShouldBe(QueryStringParamValue);
110+
}
111+
112+
[Fact]
113+
public void GivenRedactedQueryStringParam_WhenHttpContextContainsOnlyThatQueryStringParam_ThenAgentSignatureHasRedactedQueryStringParams()
114+
{
115+
// ARRANGE
116+
117+
const string QueryStringParamName = nameof(QueryStringParamName);
118+
const string QueryStringParamValue = nameof(QueryStringParamValue);
119+
const string RedactedValue = nameof(RedactedValue);
120+
121+
var httpContextAgentOptions = new HttpContextAgentSignatureOptions
122+
{
123+
RedactedQueryStringParams = new[] { QueryStringParamName },
124+
RedactedValue = RedactedValue,
125+
};
126+
127+
var httpContext = HttpContextSeeder.CreateHttpContext(new HttpContextSeederOptions
128+
{
129+
QueryStringParams = new()
130+
{
131+
[QueryStringParamName] = new[] { QueryStringParamValue }
132+
}
133+
});
134+
135+
// ACT
136+
137+
var agentSignature = HttpContextAgentSignature.GetSnapshot(httpContext, httpContextAgentOptions);
138+
139+
// ASSERT
140+
141+
agentSignature.Request.QueryStringParams.Length.ShouldBe(1);
142+
agentSignature.Request.QueryStringParams[0].Name.ShouldBe(QueryStringParamName);
143+
agentSignature.Request.QueryStringParams[0].Values.Length.ShouldBe(1);
144+
agentSignature.Request.QueryStringParams[0].Values[0].ShouldBe(RedactedValue);
71145
}
72146
}
73147
}

src/EntityDb.Mvc.Tests/Seeder/HttpContextSeeder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ private static ConnectionInfo CreateConnectionInfo(HttpContextSeederOptions http
4141

4242
private static HttpRequest CreateHttpRequest(HttpContextSeederOptions httpContextSeederOptions)
4343
{
44-
var query = httpContextSeederOptions.Query.ToDictionary(x => x.Key, x => new StringValues(x.Value));
44+
var query = httpContextSeederOptions.QueryStringParams.ToDictionary(x => x.Key, x => new StringValues(x.Value));
4545

4646
var queryCollectionMock = new Mock<IQueryCollection>(MockBehavior.Strict);
4747

src/EntityDb.Mvc.Tests/Seeder/HttpContextSeederOptions.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,19 @@ namespace EntityDb.Mvc.Tests.Seeder
44
{
55
public class HttpContextSeederOptions
66
{
7+
// Request
78
public Dictionary<string, string[]> Headers { get; set; } = new();
8-
public Dictionary<string, string[]> Query { get; set; } = new();
9-
public bool HasIpAddress { get; set; }
10-
public string? Role { get; set; }
9+
public Dictionary<string, string[]> QueryStringParams { get; set; } = new();
1110
public string Method { get; set; } = "GET";
1211
public string Scheme { get; set; } = "https";
1312
public string Host { get; set; } = "localhost";
1413
public string Path { get; set; } = "/";
1514
public string Protocol { get; set; } = "HTTP/1.1";
15+
16+
// Connection
17+
public bool HasIpAddress { get; set; }
18+
19+
// Claims Principal
20+
public string? Role { get; set; }
1621
}
1722
}

src/EntityDb.Mvc/Agents/HttpContextAgentSignature.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public sealed record RequestSnapshot
3131
string? Path,
3232
string Protocol,
3333
NameValuesPairSnapshot[] Headers,
34-
NameValuesPairSnapshot[] Queries
34+
NameValuesPairSnapshot[] QueryStringParams
3535
);
3636

3737
/// <summary>
@@ -55,14 +55,13 @@ public sealed record Snapshot
5555
ConnectionSnapshot Connection
5656
);
5757

58-
private static NameValuesPairSnapshot[] GetNameValuesPairSnapshots(IEnumerable<KeyValuePair<string, StringValues>> dictionary, HttpContextAgentSignatureOptions httpContextAgentOptions)
58+
private static NameValuesPairSnapshot[] GetNameValuesPairSnapshots(IEnumerable<KeyValuePair<string, StringValues>> dictionary, string[] redactedKeys, string redactedValue)
5959
{
6060
return dictionary
61-
.Where(pair => !httpContextAgentOptions.DoNotRecordHeaders.Contains(pair.Key))
6261
.Select(pair => new NameValuesPairSnapshot
6362
(
6463
Name: pair.Key,
65-
Values: pair.Value.ToArray()
64+
Values: redactedKeys.Contains(pair.Key) ? new[] { redactedValue } : pair.Value.ToArray()
6665
))
6766
.ToArray();
6867
}
@@ -76,8 +75,8 @@ private static RequestSnapshot GetRequestSnapshot(HttpRequest httpRequest, HttpC
7675
Host: httpRequest.Host.Value,
7776
Path: httpRequest.Path.Value,
7877
Protocol: httpRequest.Protocol,
79-
Headers: GetNameValuesPairSnapshots(httpRequest.Headers, httpContextAgentOptions),
80-
Queries: GetNameValuesPairSnapshots(httpRequest.Query, httpContextAgentOptions)
78+
Headers: GetNameValuesPairSnapshots(httpRequest.Headers, httpContextAgentOptions.RedactedHeaders, httpContextAgentOptions.RedactedValue),
79+
QueryStringParams: GetNameValuesPairSnapshots(httpRequest.Query, httpContextAgentOptions.RedactedQueryStringParams, httpContextAgentOptions.RedactedValue)
8180
);
8281
}
8382

src/EntityDb.Mvc/Agents/HttpContextAgentSignatureOptions.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,17 @@ public class HttpContextAgentSignatureOptions
1111
/// If there is a header whose value is sensitive and should not be recorded in the
1212
/// agent signature, put it in this array.
1313
/// </summary>
14-
public string[] DoNotRecordHeaders { get; set; } = Array.Empty<string>();
14+
public string[] RedactedHeaders { get; set; } = Array.Empty<string>();
15+
16+
/// <summary>
17+
/// If there is a query string param whose value is sensitive and should not be recorded in the
18+
/// agent signature, put it in this array.
19+
/// </summary>
20+
public string[] RedactedQueryStringParams { get; set; } = Array.Empty<string>();
21+
22+
/// <summary>
23+
/// Redacted information will be substituted with this value.
24+
/// </summary>
25+
public string RedactedValue { get; set; } = "*REDACTED*";
1526
}
1627
}

0 commit comments

Comments
 (0)