Skip to content

Commit 5de363d

Browse files
authored
Improve host.json sanitization on v1.x (#9625)
1 parent 92e3dd8 commit 5de363d

File tree

5 files changed

+90
-7
lines changed

5 files changed

+90
-7
lines changed

src/WebJobs.Script.WebHost/WebJobs.Script.WebHost.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,6 @@
503503
<Compile Include="Diagnostics\ExtendedEventSource.cs" />
504504
<Compile Include="Diagnostics\FunctionsEventSource.cs" />
505505
<Compile Include="Diagnostics\FunctionsSystemLogsEventSource.cs" />
506-
<Compile Include="Diagnostics\Sanitizer.cs" />
507506
<Compile Include="Extensions\FunctionMetadataExtensions.cs" />
508507
<Compile Include="Extensions\HttpRouteCollectionExtensions.cs" />
509508
<Compile Include="Extensions\HttpRouteFactoryExtensions.cs" />

src/WebJobs.Script.WebHost/Diagnostics/Sanitizer.cs renamed to src/WebJobs.Script/Diagnostics/Sanitizer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ namespace Microsoft.Azure.WebJobs.Logging
1111
// Note: This file is shared between the WebJobs SDK and Script repos. Update both if changes are needed.
1212
internal static class Sanitizer
1313
{
14-
private const string SecretReplacement = "[Hidden Credential]";
14+
public const string SecretReplacement = "[Hidden Credential]";
1515
private static readonly char[] ValueTerminators = new char[] { '<', '"', '\'' };
1616

1717
// List of keywords that should not be replaced with [Hidden Credential]
1818
private static readonly string[] AllowedTokens = new string[] { "PublicKeyToken=" };
19-
private static readonly string[] CredentialTokens = new string[] { "Token=", "DefaultEndpointsProtocol=http", "AccountKey=", "Data Source=", "Server=", "Password=", "pwd=", "&amp;sig=", "SharedAccessKey=" };
19+
private static readonly string[] CredentialTokens = new string[] { "Token=", "DefaultEndpointsProtocol=http", "AccountKey=", "Data Source=", "Server=", "Password=", "pwd=", "&amp;sig=", "&sig=", "?sig=", "SharedAccessKey=" };
2020

2121
/// <summary>
2222
/// Removes well-known credential strings from strings.

src/WebJobs.Script/Host/ScriptHost.cs

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ public class ScriptHost : JobHost
4444
private const string HostAssemblyName = "ScriptHost";
4545
private const string GeneratedTypeNamespace = "Host";
4646
internal const string GeneratedTypeName = "Functions";
47+
48+
private static readonly string[] CredentialNameFragments = new[] { "password", "pwd", "key", "secret", "token", "sas" };
49+
4750
private readonly IScriptHostEnvironment _scriptHostEnvironment;
4851
private readonly string _storageConnectionString;
4952
private readonly IMetricsLogger _metricsLogger;
@@ -308,19 +311,70 @@ public void Initialize()
308311
internal static string SanitizeHostJson(JObject hostJsonObject)
309312
{
310313
JObject sanitizedObject = new JObject();
311-
312314
foreach (var propName in WellKnownHostJsonProperties)
313315
{
314316
var propValue = hostJsonObject[propName];
315317
if (propValue != null)
316318
{
317-
sanitizedObject[propName] = propValue;
319+
sanitizedObject[propName] = Sanitize(propValue);
318320
}
319321
}
320322

321323
return sanitizedObject.ToString();
322324
}
323325

326+
private static JToken Sanitize(JToken token)
327+
{
328+
if (token is JObject obj)
329+
{
330+
JObject sanitized = new JObject();
331+
foreach (var prop in obj)
332+
{
333+
if (IsPotentialCredential(prop.Key))
334+
{
335+
sanitized[prop.Key] = Sanitizer.SecretReplacement;
336+
}
337+
else
338+
{
339+
sanitized[prop.Key] = Sanitize(prop.Value);
340+
}
341+
}
342+
343+
return sanitized;
344+
}
345+
346+
if (token is JArray arr)
347+
{
348+
JArray sanitized = new JArray();
349+
foreach (var value in arr)
350+
{
351+
sanitized.Add(Sanitize(value));
352+
}
353+
354+
return sanitized;
355+
}
356+
357+
if (token.Type == JTokenType.String)
358+
{
359+
return Sanitizer.Sanitize(token.ToString());
360+
}
361+
362+
return token;
363+
}
364+
365+
private static bool IsPotentialCredential(string name)
366+
{
367+
foreach (string fragment in CredentialNameFragments)
368+
{
369+
if (name.IndexOf(fragment, StringComparison.OrdinalIgnoreCase) != -1)
370+
{
371+
return true;
372+
}
373+
}
374+
375+
return false;
376+
}
377+
324378
// Validate that for any precompiled assembly, all functions have the same configuration precedence.
325379
private void VerifyPrecompileStatus(IEnumerable<FunctionDescriptor> functions)
326380
{

src/WebJobs.Script/WebJobs.Script.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,7 @@
516516
<Compile Include="HostInitializationException.cs" />
517517
<Compile Include="Scale\ApplicationPerformanceCounters.cs" />
518518
<Compile Include="Diagnostics\CompositeTraceWriter.cs" />
519+
<Compile Include="Diagnostics\Sanitizer.cs" />
519520
<Compile Include="Description\Binding\BindingDirection.cs" />
520521
<Compile Include="Description\Binding\BindingMetadata.cs" />
521522
<Compile Include="Description\DotNet\DotNetConstants.cs" />

test/WebJobs.Script.Tests/ScriptHostTests.cs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,18 +1244,32 @@ public void Initialize_Sanitizes_HostJsonLog()
12441244
Directory.CreateDirectory(rootPath);
12451245
}
12461246

1247-
// Turn off all logging. We shouldn't see any output.
12481247
string hostJsonContent = @"
12491248
{
12501249
'functionTimeout': '00:05:00',
12511250
'functions': [ 'FunctionA', 'FunctionB' ],
12521251
'logger': {
12531252
'categoryFilter': {
12541253
'defaultLevel': 'Information'
1255-
}
1254+
},
1255+
'prop': 'Hey=AS1$@%#$%W-k2j"";SharedAccessKey=foo,Data Source=barzons,Server=bathouse""testing',
1256+
'values': [ 'plain', 10, 'Password=hunter2' ],
1257+
'my-password': 'hunter2',
1258+
'service_token': 'token',
1259+
'StorageSas': 'access',
1260+
'aSecret': { 'value1': 'value' }
12561261
},
12571262
'Values': {
12581263
'MyCustomValue': 'abc'
1264+
},
1265+
'applicationInsights': {
1266+
'prop': 'Hey=AS1$@%#$%W-k2j"";SharedAccessKey=foo,Data Source=barzons,Server=bathouse""testing',
1267+
'values': [ 'plain', 10, 'Password=hunter2' ],
1268+
'sampleSettings': {
1269+
'my-password': 'hunter2',
1270+
'service_token': 'token',
1271+
'StorageSas': 'access'
1272+
}
12591273
}
12601274
}";
12611275

@@ -1283,6 +1297,21 @@ public void Initialize_Sanitizes_HostJsonLog()
12831297
'logger': {
12841298
'categoryFilter': {
12851299
'defaultLevel': 'Information'
1300+
},
1301+
'prop': 'Hey=AS1$@%#$%W-k2j"";[Hidden Credential]""testing',
1302+
'values': [ 'plain', 10, '[Hidden Credential]' ],
1303+
'my-password': '[Hidden Credential]',
1304+
'service_token': '[Hidden Credential]',
1305+
'StorageSas': '[Hidden Credential]',
1306+
'aSecret': '[Hidden Credential]'
1307+
},
1308+
'applicationInsights': {
1309+
'prop': 'Hey=AS1$@%#$%W-k2j"";[Hidden Credential]""testing',
1310+
'values': [ 'plain', 10, '[Hidden Credential]' ],
1311+
'sampleSettings': {
1312+
'my-password': '[Hidden Credential]',
1313+
'service_token': '[Hidden Credential]',
1314+
'StorageSas': '[Hidden Credential]'
12861315
}
12871316
}
12881317
}";

0 commit comments

Comments
 (0)