diff --git a/tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/AdminTests.cs b/tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/AdminTests.cs index 908c9af5ba4..24397f34bb1 100644 --- a/tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/AdminTests.cs +++ b/tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/AdminTests.cs @@ -732,5 +732,64 @@ public async Task TestAddTransformWithBadUriRegexCondition(string body, string e Assert.Empty(testRecordingHandler.Transforms); Assert.Contains(errorText, assertion.Message); } + + [Fact] + public async Task AddSanitizerThrowsOnMissingRequiredArgument() + { + RecordingHandler testRecordingHandler = new RecordingHandler(Directory.GetCurrentDirectory()); + var httpContext = new DefaultHttpContext(); + httpContext.Request.Headers["x-abstraction-identifier"] = "GeneralStringSanitizer"; + httpContext.Request.Body = TestHelpers.GenerateStreamRequestBody("{\"value\":\"replacementValue\"}"); + httpContext.Request.ContentLength = 33; + + var controller = new Admin(testRecordingHandler) + { + ControllerContext = new ControllerContext() + { + HttpContext = httpContext + } + }; + + testRecordingHandler.Sanitizers.Clear(); + + var assertion = await Assert.ThrowsAsync( + async () => await controller.AddSanitizer() + ); + + Assert.True(assertion.StatusCode.Equals(HttpStatusCode.BadRequest)); + Assert.Contains("Required parameter key System.String target was not found in the request body.", assertion.Message); + } + + [Fact] + public async Task AddSanitizerContinuesWithTwoRequiredParams() + { + var targetKey = "Content-Type"; + var targetString = "application/javascript"; + + RecordingHandler testRecordingHandler = new RecordingHandler(Directory.GetCurrentDirectory()); + var httpContext = new DefaultHttpContext(); + httpContext.Request.Headers["x-abstraction-identifier"] = "HeaderStringSanitizer"; + httpContext.Request.Body = TestHelpers.GenerateStreamRequestBody("{\"target\":\"" + targetString + "\", \"key\":\"" + targetKey + "\"}"); + httpContext.Request.ContentLength = 79; + + var controller = new Admin(testRecordingHandler) + { + ControllerContext = new ControllerContext() + { + HttpContext = httpContext + } + }; + + testRecordingHandler.Sanitizers.Clear(); + await controller.AddSanitizer(); + + var addedSanitizer = testRecordingHandler.Sanitizers.First(); + Assert.True(addedSanitizer is HeaderStringSanitizer); + + var actualTargetString = (string)typeof(HeaderStringSanitizer).GetField("_targetValue", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(addedSanitizer); + var actualTargetKey = (string)typeof(HeaderStringSanitizer).GetField("_targetKey", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(addedSanitizer); + Assert.Equal(targetKey, actualTargetKey); + Assert.Equal(targetString, actualTargetString); + } } } diff --git a/tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/SanitizerTests.cs b/tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/SanitizerTests.cs index 0ab213f33b6..b8f40c23c74 100644 --- a/tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/SanitizerTests.cs +++ b/tools/test-proxy/Azure.Sdk.Tools.TestProxy.Tests/SanitizerTests.cs @@ -1,5 +1,6 @@ using Azure.Sdk.Tools.TestProxy.Common; using Azure.Sdk.Tools.TestProxy.Sanitizers; +using System.Collections.Generic; using System.Linq; using System.Text; using Xunit; @@ -453,5 +454,191 @@ public void ConditionalSanitizeUriRegexProperlySkips() session.Session.Sanitize(removeHeadersSanitizer); Assert.DoesNotContain(false, session.Session.Entries.Select(x => x.Request.Headers.ContainsKey(targetHeader))); } + + [Fact] + public void GenStringSanitizerAppliesForMultipleComponents() + { + var session = TestHelpers.LoadRecordSession("Test.RecordEntries/post_delete_get_content.json"); + var targetString = "listtable09bf2a3d"; + var replacementString = ""; + var targetEntry = session.Session.Entries[0]; + + var originalRequestBody = Encoding.UTF8.GetString(targetEntry.Request.Body); + var originalResponseBody = Encoding.UTF8.GetString(targetEntry.Response.Body); + var originalLocation = targetEntry.Response.Headers["Location"].First().ToString(); + + var sanitizer = new GeneralStringSanitizer(targetString, replacementString); + session.Session.Sanitize(sanitizer); + + var resultRequestBody = Encoding.UTF8.GetString(targetEntry.Request.Body); + var resultResponseBody = Encoding.UTF8.GetString(targetEntry.Response.Body); + var resultLocation = targetEntry.Response.Headers["Location"].First().ToString(); + + + // request body + Assert.NotEqual(originalRequestBody, resultRequestBody); + Assert.DoesNotContain(targetString, resultRequestBody); + Assert.Contains(replacementString, resultRequestBody); + + // result body + Assert.NotEqual(originalResponseBody, resultResponseBody); + Assert.DoesNotContain(targetString, resultResponseBody); + Assert.Contains(replacementString, resultResponseBody); + + // uri + Assert.NotEqual(originalLocation, resultLocation); + Assert.DoesNotContain(targetString, resultLocation); + Assert.Contains(replacementString, resultLocation); + } + + [Fact] + public void GenStringSanitizerQuietExitForAllHttpComponents() + { + var session = TestHelpers.LoadRecordSession("Test.RecordEntries/post_delete_get_content.json"); + var untouchedSession = TestHelpers.LoadRecordSession("Test.RecordEntries/post_delete_get_content.json"); + + var targetString = ".*"; + var replacementString = ""; + var targetEntry = session.Session.Entries[0]; + var targetUntouchedEntry = untouchedSession.Session.Entries[0]; + var matcher = new RecordMatcher(); + + var sanitizer = new GeneralStringSanitizer(targetString, replacementString); + session.Session.Sanitize(sanitizer); + + var resultRequestBody = Encoding.UTF8.GetString(targetEntry.Request.Body); + var resultResponseBody = Encoding.UTF8.GetString(targetEntry.Response.Body); + var resultLocation = targetEntry.Response.Headers["Location"].First().ToString(); + + Assert.DoesNotContain(targetString, resultRequestBody); + Assert.DoesNotContain(targetString, resultResponseBody); + Assert.DoesNotContain(targetString, resultLocation); + + Assert.Equal(0, matcher.CompareHeaderDictionaries(targetUntouchedEntry.Request.Headers, targetEntry.Request.Headers, new HashSet(), new HashSet())); + Assert.Equal(0, matcher.CompareHeaderDictionaries(targetUntouchedEntry.Response.Headers, targetEntry.Response.Headers, new HashSet(), new HashSet())); + Assert.Equal(0, matcher.CompareBodies(targetUntouchedEntry.Request.Body, targetEntry.Request.Body)); + Assert.Equal(0, matcher.CompareBodies(targetUntouchedEntry.Response.Body, targetEntry.Response.Body)); + Assert.Equal(targetUntouchedEntry.RequestUri, targetEntry.RequestUri); + } + + [Theory] + [InlineData("Accept-Encoding", ",", "", "Test.RecordEntries/post_delete_get_content.json")] + [InlineData("Accept", "*/*", "", "Test.RecordEntries/oauth_request_with_variables.json")] + [InlineData("User-Agent", ".19041-SP0", "", "Test.RecordEntries/post_delete_get_content.json")] + public void HeaderStringSanitizerApplies(string targetKey, string targetValue, string replacementValue, string recordingFile) + { + var session = TestHelpers.LoadRecordSession(recordingFile); + var targetEntry = session.Session.Entries[0]; + var originalHeaderValue = targetEntry.Request.Headers[targetKey].First().ToString(); + + var sanitizer = new HeaderStringSanitizer(targetKey, targetValue, value: replacementValue); + session.Session.Sanitize(sanitizer); + + var resultHeaderValue = targetEntry.Request.Headers[targetKey].First().ToString(); + + Assert.NotEqual(resultHeaderValue, originalHeaderValue); + Assert.Contains(replacementValue, resultHeaderValue); + Assert.DoesNotContain(targetValue, resultHeaderValue); + } + + [Theory] + [InlineData("DataServiceVersion", "application/json", "", "Test.RecordEntries/post_delete_get_content.json")] + public void HeaderStringSanitizerQuietlyExits(string targetKey, string targetValue, string replacementValue, string recordingFile) + { + var session = TestHelpers.LoadRecordSession(recordingFile); + var untouchedSession = TestHelpers.LoadRecordSession(recordingFile); + var targetUntouchedEntry = untouchedSession.Session.Entries[0]; + var targetEntry = session.Session.Entries[0]; + var matcher = new RecordMatcher(); + + var sanitizer = new HeaderStringSanitizer(targetKey, targetValue, value: replacementValue); + session.Session.Sanitize(sanitizer); + + Assert.Equal(0, matcher.CompareHeaderDictionaries(targetUntouchedEntry.Request.Headers, targetEntry.Request.Headers, new HashSet(), new HashSet())); + Assert.Equal(0, matcher.CompareHeaderDictionaries(targetUntouchedEntry.Response.Headers, targetEntry.Response.Headers, new HashSet(), new HashSet())); + } + + [Theory] + [InlineData("listtable09bf2a3d", "", "Test.RecordEntries/post_delete_get_content.json")] + [InlineData("%20profile%20offline", "", "Test.RecordEntries/oauth_request.json")] + [InlineData("|,&x-client-last-telemetry=2|0|", "", "Test.RecordEntries/oauth_request.json")] + [InlineData("}", "", "Test.RecordEntries/response_with_null_secrets.json")] + public void BodyStringSanitizerApplies(string targetValue, string replacementValue, string recordingFile) + { + var session = TestHelpers.LoadRecordSession(recordingFile); + var targetEntry = session.Session.Entries[0]; + var originalBodyValue = Encoding.UTF8.GetString(targetEntry.Request.Body); + + var sanitizer = new BodyStringSanitizer(targetValue, value: replacementValue); + session.Session.Sanitize(sanitizer); + + var resultBodyValue = Encoding.UTF8.GetString(targetEntry.Request.Body); + + Assert.NotEqual(originalBodyValue, resultBodyValue); + Assert.Contains(replacementValue, resultBodyValue); + Assert.DoesNotContain(targetValue, resultBodyValue); + } + + [Theory] + [InlineData("TableNames", "", "Test.RecordEntries/response_with_null_secrets.json")] + [InlineData("d2270777-c002-0072-313d-4ce19f000000", "", "Test.RecordEntries/response_with_null_secrets.json")] + [InlineData(".19041-SP0", "", "Test.RecordEntries/response_with_null_secrets.json")] + public void BodyStringSanitizerQuietlyExits(string targetValue, string replacementValue, string recordingFile) + { + var session = TestHelpers.LoadRecordSession(recordingFile); + var untouchedSession = TestHelpers.LoadRecordSession(recordingFile); + var targetEntry = session.Session.Entries[0]; + var originalBodyValue = Encoding.UTF8.GetString(targetEntry.Request.Body); + var targetUntouchedEntry = untouchedSession.Session.Entries[0]; + var matcher = new RecordMatcher(); + + var sanitizer = new BodyStringSanitizer(targetValue, value: replacementValue); + session.Session.Sanitize(sanitizer); + + var resultBodyValue = Encoding.UTF8.GetString(targetEntry.Request.Body); + Assert.Equal(0, matcher.CompareBodies(targetUntouchedEntry.Request.Body, targetEntry.Request.Body)); + Assert.Equal(0, matcher.CompareBodies(targetUntouchedEntry.Response.Body, targetEntry.Response.Body)); + } + + [Theory] + [InlineData("/v2.0/", "", "Test.RecordEntries/oauth_request.json")] + [InlineData("https://management.azure.com/subscriptions/12345678-1234-1234-5678-123456789010", "", "Test.RecordEntries/request_with_subscriptionid.json")] + [InlineData("?api-version=2019-05-01", "", "Test.RecordEntries/request_with_subscriptionid.json")] + public void UriStringSanitizerApplies(string targetValue, string replacementValue, string recordingFile) + { + var session = TestHelpers.LoadRecordSession(recordingFile); + var untouchedSession = TestHelpers.LoadRecordSession(recordingFile); + + var targetEntry = session.Session.Entries[0]; + var targetUntouchedEntry = untouchedSession.Session.Entries[0]; + var matcher = new RecordMatcher(); + + var sanitizer = new UriStringSanitizer(targetValue, replacementValue); + session.Session.Sanitize(sanitizer); + + var originalUriValue = targetUntouchedEntry.RequestUri.ToString(); + var resultUriValue = targetEntry.RequestUri.ToString(); + + Assert.NotEqual(originalUriValue, resultUriValue); + Assert.Contains(replacementValue, resultUriValue); + Assert.DoesNotContain(targetValue, resultUriValue); + } + + [Theory] + [InlineData("fakeazsdktestaccount2", " + /// General purpose string replacement. Simple abstraction of string.Replace(). + /// + /// The name of the header we're operating against. + /// The substitution or whole new header value, depending on "regex" setting. + /// The substitution or new header value, depending on the "targetValue" setting. + /// An updated value of the input string, with replacement operations completed if necessary. + public static string ReplaceValue(string inputValue, string targetValue, string replacementValue) + { + return inputValue.Replace(targetValue, replacementValue); + } + /// /// General purpose string replacement/subsitution given a set of inputs. Used in many regex substitution sanitizers. /// /// The name of the header we're operating against. - /// The substitution or whole new header value, depending on "regex" setting. + /// The substitution or whole input value, depending on "regex" setting. /// A regex. Can be defined as a simple regex replace OR if groupName is set, a subsitution operation. /// The capture group that needs to be operated upon. Do not set if you're invoking a simple replacement operation. /// Note that with this implementation, you can refer to a numbered group if you didn't name it, EG: '0'. diff --git a/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Sanitizers/BodyKeySanitizer.cs b/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Sanitizers/BodyKeySanitizer.cs index 364d316ec19..d3b1949a040 100644 --- a/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Sanitizers/BodyKeySanitizer.cs +++ b/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Sanitizers/BodyKeySanitizer.cs @@ -7,7 +7,7 @@ namespace Azure.Sdk.Tools.TestProxy.Sanitizers { /// - /// This sanitizer operates on a RecordSession entry and applies itself to the Request and Response bodies contained therein. It ONLY operates on the request/response bodies. Not header or URIs. + /// This sanitizer operates on a RecordSession entry and applies regex replacement to the Request and Response bodies contained therein. It ONLY operates on the request/response bodies. Not header or URIs. /// public class BodyKeySanitizer : RecordedTestSanitizer { diff --git a/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Sanitizers/BodyRegexSanitizer.cs b/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Sanitizers/BodyRegexSanitizer.cs index fc2a70a059f..f89e6f71131 100644 --- a/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Sanitizers/BodyRegexSanitizer.cs +++ b/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Sanitizers/BodyRegexSanitizer.cs @@ -5,7 +5,7 @@ namespace Azure.Sdk.Tools.TestProxy.Sanitizers { /// - /// This sanitizer operates on a RecordSession entry and applies itself to the Request and Response bodies contained therein. It ONLY operates on the request/response bodies. Not header or URIs. + /// This sanitizer operates on a RecordSession entry and applies regex replacement to the Request and Response bodies contained therein. It ONLY operates on the request/response bodies. Not header or URIs. /// public class BodyRegexSanitizer : RecordedTestSanitizer { diff --git a/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Sanitizers/BodyStringSanitizer.cs b/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Sanitizers/BodyStringSanitizer.cs new file mode 100644 index 00000000000..0bd1b04ae28 --- /dev/null +++ b/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Sanitizers/BodyStringSanitizer.cs @@ -0,0 +1,43 @@ +using Azure.Sdk.Tools.TestProxy.Common; +using System; +using System.Text; + +namespace Azure.Sdk.Tools.TestProxy.Sanitizers +{ + /// + /// This sanitizer operates on a RecordSession entry and applies value replacement to the Request and Response bodies contained therein. It ONLY operates on the request/response bodies. Not header or URIs. + /// + public class BodyStringSanitizer : RecordedTestSanitizer + { + private string _newValue; + private string _targetValue; + + /// + /// This sanitizer offers regex replace within a returned body. Specifically, this means regex applying to the raw JSON. If you are attempting to simply + /// replace a specific key, the BodyKeySanitizer is probably the way to go. Regardless, there are examples present in SanitizerTests.cs. + /// + /// A target string. This could contain special regex characters like "?()+*" but they will be treated as a literal. + /// The substitution value. + /// + /// A condition that dictates when this sanitizer applies to a request/response pair. The content of this key should be a JSON object that contains configuration keys. + /// Currently, that only includes the key "uriRegex". This translates to an object that looks like '{ "uriRegex": "when this regex matches, apply the sanitizer" }'. Defaults to "apply always." + /// + public BodyStringSanitizer(string target, string value = "Sanitized", ApplyCondition condition = null) + { + _targetValue = target; + _newValue = value; + Condition = condition; + } + + public override string SanitizeTextBody(string contentType, string body) + { + return StringSanitizer.ReplaceValue(inputValue: body, targetValue: _targetValue, replacementValue: _newValue); + } + + + public override byte[] SanitizeBody(string contentType, byte[] body) + { + return Encoding.UTF8.GetBytes(StringSanitizer.ReplaceValue(inputValue: Encoding.UTF8.GetString(body), targetValue: _targetValue, replacementValue: _newValue)); + } + } +} diff --git a/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Sanitizers/GeneralStringSanitizer.cs b/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Sanitizers/GeneralStringSanitizer.cs new file mode 100644 index 00000000000..9cb7177328c --- /dev/null +++ b/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Sanitizers/GeneralStringSanitizer.cs @@ -0,0 +1,68 @@ +using Azure.Sdk.Tools.TestProxy.Common; +using System.Collections.Generic; + +namespace Azure.Sdk.Tools.TestProxy.Sanitizers +{ + /// + /// This sanitizer operates on a RecordSession entry and applies itself to the Request and Response bodies contained therein. This "general" sanitizer applies the configured string value replacement + /// to headers, body, and URI. + /// + public class GeneralStringSanitizer : RecordedTestSanitizer + { + private string _newValue; + private string _targetValue; + + private BodyStringSanitizer _bodySanitizer; + private UriStringSanitizer _uriSanitizer; + + /// + /// This sanitizer offers a value replace across request/response Body, Headers, and URI. For the body, this means a string replacement applied directly to the raw JSON. + /// + /// The substitution value. + /// A target string. This could contain special regex characters like "?()+*" but they will be treated as a literal. + /// + /// A condition that dictates when this sanitizer applies to a request/response pair. The content of this key should be a JSON object that contains configuration keys. + /// Currently, that only includes the key "uriRegex". This translates to an object that looks like '{ "uriRegex": "when this regex matches, apply the sanitizer" }'. Defaults to "apply always." + /// + public GeneralStringSanitizer(string target, string value = "Sanitized", ApplyCondition condition = null) + { + _targetValue = target; + _newValue = value; + Condition = condition; + + _bodySanitizer = new BodyStringSanitizer(target, value, condition); + _uriSanitizer = new UriStringSanitizer(target, value, condition); + } + + public override void SanitizeHeaders(IDictionary headers) + { + foreach (var headerKey in headers.Keys) + { + // Accessing 0th key safe due to the fact that we force header values in without splitting them on ;. + // We do this because letting .NET split and then reassemble header values introduces a space into the header itself + // Ex: "application/json;odata=minimalmetadata" with .NET default header parsing becomes "application/json; odata=minimalmetadata" + // Given this breaks signature verification, we have to avoid it. + var originalValue = headers[headerKey][0]; + + var replacement = StringSanitizer.ReplaceValue(inputValue: originalValue, targetValue: _targetValue, replacementValue: _newValue); + + headers[headerKey][0] = replacement; + } + } + + public override string SanitizeUri(string uri) + { + return _uriSanitizer.SanitizeUri(uri); + } + + public override string SanitizeTextBody(string contentType, string body) + { + return _bodySanitizer.SanitizeTextBody(contentType, body); + } + + public override byte[] SanitizeBody(string contentType, byte[] body) + { + return _bodySanitizer.SanitizeBody(contentType, body); + } + } +} diff --git a/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Sanitizers/HeaderStringSanitizer.cs b/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Sanitizers/HeaderStringSanitizer.cs new file mode 100644 index 00000000000..9b8472aba76 --- /dev/null +++ b/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Sanitizers/HeaderStringSanitizer.cs @@ -0,0 +1,50 @@ +using Azure.Sdk.Tools.TestProxy.Common; +using System.Collections.Generic; + +namespace Azure.Sdk.Tools.TestProxy.Sanitizers +{ + /// + /// This sanitizer operates on a RecordSession entry and applies value replacement to the headers contained therein. This sanitizer ONLY applies to the request/response headers, + /// body and URI are left untouched. + /// + public class HeaderStringSanitizer : RecordedTestSanitizer + { + private string _targetKey; + private string _newValue; + private string _targetValue; + + /// + /// Applies a simple value replacement for a target header key. If it does not exist, no actions will be taken. + /// + /// The name of the header we're operating against. + /// A target string. This could contain special regex characters like "?()+*" but they will be treated as a literal. + /// The substitution value. + /// + /// A condition that dictates when this sanitizer applies to a request/response pair. The content of this key should be a JSON object that contains configuration keys. + /// Currently, that only includes the key "uriRegex". This translates to an object that looks like '{ "uriRegex": "when this regex matches, apply the sanitizer" }'. Defaults to "apply always." + /// + public HeaderStringSanitizer(string key, string target, string value = "Sanitized", ApplyCondition condition = null) + { + _targetKey = key; + _newValue = value; + _targetValue = target; + Condition = condition; + } + + public override void SanitizeHeaders(IDictionary headers) + { + if (headers.ContainsKey(_targetKey)) + { + // Accessing 0th key safe due to the fact that we force header values in without splitting them on ;. + // We do this because letting .NET split and then reassemble header values introduces a space into the header itself + // Ex: "application/json;odata=minimalmetadata" with .NET default header parsing becomes "application/json; odata=minimalmetadata" + // Given this breaks signature verification, we have to avoid it. + var originalValue = headers[_targetKey][0]; + + var replacement = StringSanitizer.ReplaceValue(inputValue: originalValue, targetValue: _targetValue, replacementValue: _newValue); + + headers[_targetKey] = new string[] { replacement }; + } + } + } +} diff --git a/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Sanitizers/UriStringSanitizer.cs b/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Sanitizers/UriStringSanitizer.cs new file mode 100644 index 00000000000..1329dbc83c3 --- /dev/null +++ b/tools/test-proxy/Azure.Sdk.Tools.TestProxy/Sanitizers/UriStringSanitizer.cs @@ -0,0 +1,34 @@ +using Azure.Sdk.Tools.TestProxy.Common; + +namespace Azure.Sdk.Tools.TestProxy.Sanitizers +{ + /// + /// General use sanitizer for cleaning URIs via straightforward string replacement. + /// + public class UriStringSanitizer : RecordedTestSanitizer + { + private string _newValue; + private string _targetValue; + + /// + /// Runs a simple string replacement against the request/response URIs. + /// + /// The substitution value. + /// A target string. This could contain special regex characters like "?()+*" but they will be treated as a literal. + /// + /// A condition that dictates when this sanitizer applies to a request/response pair. The content of this key should be a JSON object that contains configuration keys. + /// Currently, that only includes the key "uriRegex". This translates to an object that looks like '{ "uriRegex": "when this regex matches, apply the sanitizer" }'. Defaults to "apply always." + /// + public UriStringSanitizer(string target, string value = "Sanitized", ApplyCondition condition = null) + { + _targetValue = target; + _newValue = value; + Condition = condition; + } + + public override string SanitizeUri(string uri) + { + return StringSanitizer.ReplaceValue(inputValue: uri, targetValue: _targetValue, replacementValue: _newValue); + } + } +}