Skip to content

Commit ebc50da

Browse files
authored
Implement new RecordSanitizer class to redact test recording files. (#28785)
1 parent 5d0e703 commit ebc50da

File tree

3 files changed

+112
-2
lines changed

3 files changed

+112
-2
lines changed

tools/TestFx/Recorder/RecordEntry.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public HttpResponseMessage GetResponse()
9393
HttpResponseMessage response = new HttpResponseMessage();
9494
response.StatusCode = StatusCode;
9595
ResponseHeaders.ForEach(h => response.Headers.TryAddWithoutValidation(h.Key, h.Value));
96-
ResponseContentType = RecorderUtilities.GetContetTypeFromHeaders(ResponseHeaders);
96+
ResponseContentType = RecorderUtilities.GetContentTypeFromHeaders(ResponseHeaders);
9797
response.Content = RecorderUtilities.CreateHttpContent(ResponseBody, ResponseContentType);
9898
ResponseHeaders.ForEach(h => response.Content.Headers.TryAddWithoutValidation(h.Key, h.Value));
9999
return response;
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
using Microsoft.Security.Utilities;
16+
using Newtonsoft.Json.Linq;
17+
using System;
18+
using System.Linq;
19+
20+
namespace Microsoft.Azure.Commands.TestFx.Recorder
21+
{
22+
public class RecordSanitizer
23+
{
24+
private readonly SecretMasker _masker;
25+
26+
public RecordSanitizer(string redactionToken = null)
27+
{
28+
var defaultRedactionToken = string.IsNullOrWhiteSpace(redactionToken) ? "Sanitized" : redactionToken;
29+
30+
_masker = new SecretMasker(
31+
regexSecrets: WellKnownRegexPatterns.HighConfidenceMicrosoftSecurityModels,
32+
defaultRegexRedactionToken: defaultRedactionToken);
33+
}
34+
35+
public void ProcessJsonToken(JToken token)
36+
{
37+
if (token == null)
38+
return;
39+
40+
switch (token.Type)
41+
{
42+
case JTokenType.Array:
43+
ProcessArrayToken(token);
44+
break;
45+
case JTokenType.Object:
46+
ProcessObjectToken(token);
47+
break;
48+
case JTokenType.String:
49+
ProcessStringToken(token);
50+
break;
51+
}
52+
}
53+
54+
private void ProcessArrayToken(JToken token)
55+
{
56+
foreach (var item in token.Children())
57+
{
58+
ProcessJsonToken(item);
59+
}
60+
}
61+
62+
private void ProcessObjectToken(JToken token)
63+
{
64+
foreach (var property in token.Children<JProperty>())
65+
{
66+
ProcessJsonToken(property.Value);
67+
}
68+
}
69+
70+
private void ProcessStringToken(JToken token)
71+
{
72+
if (TrySanitizeTokenValue(token, out var sanitizedData))
73+
{
74+
((JValue)token).Value = sanitizedData;
75+
}
76+
}
77+
78+
private bool TrySanitizeTokenValue(JToken token, out string sanitizedData)
79+
{
80+
sanitizedData = null;
81+
var jsonString = token?.Value<string>();
82+
83+
if (string.IsNullOrWhiteSpace(jsonString))
84+
return false;
85+
86+
try
87+
{
88+
var detectionResults = _masker.DetectSecrets(jsonString);
89+
if (!detectionResults.Any())
90+
return false;
91+
92+
sanitizedData = _masker.MaskSecrets(jsonString);
93+
return true;
94+
}
95+
catch (Exception ex)
96+
{
97+
Console.WriteLine($"Error occurred while sanitizing the test recording file:");
98+
Console.WriteLine($"Raw token: {jsonString}");
99+
Console.WriteLine($"Error message: {ex.Message}");
100+
Console.WriteLine($"Stack trace: {ex.StackTrace}");
101+
102+
return false;
103+
}
104+
}
105+
}
106+
}

tools/TestFx/Recorder/RecorderUtilities.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public static string FormatString(string content)
9999
return content;
100100
}
101101

102-
public static RecordEntryContentType GetContetTypeFromHeaders(Dictionary<string, List<string>> responseHeaders)
102+
public static RecordEntryContentType GetContentTypeFromHeaders(Dictionary<string, List<string>> responseHeaders)
103103
{
104104
string mimeType = string.Empty;
105105
RecordEntryContentType contentType = RecordEntryContentType.Null;
@@ -232,6 +232,10 @@ public static string TryFormatJson(string content)
232232
}
233233
}
234234
}
235+
236+
var sanitizer = new RecordSanitizer(SanitizeValue);
237+
sanitizer.ProcessJsonToken(parsedJson);
238+
235239
return JsonConvert.SerializeObject(parsedJson, Formatting.Indented);
236240
}
237241
catch

0 commit comments

Comments
 (0)