Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configurable masking token, perf improvements, refactoring. #4586

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
5 changes: 4 additions & 1 deletion src/Agent.Sdk/SecretMasking/ILoggedSecretMasker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
//using Microsoft.TeamFoundation.DistributedTask.Logging;
using ValueEncoder = Microsoft.TeamFoundation.DistributedTask.Logging.ValueEncoder;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace Agent.Sdk.SecretMasking
{
Expand All @@ -13,9 +15,10 @@ public interface ILoggedSecretMasker : ISecretMasker
{
static int MinSecretLengthLimit { get; }

void AddRegex(String pattern, string origin);
void AddRegex(String pattern, string origin, string moniker = null, ISet<string> sniffLiterals = null, RegexOptions regexOptions = 0);
void AddValue(String value, string origin);
void AddValueEncoder(ValueEncoder encoder, string origin);
void SetTrace(ITraceWriter trace);
IDictionary<string, string> GetTelemetry();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetTelemetry

Added this on ILoggedSecretMasker because I thought it made most sense given that we will produce this data for both the legacy and current secret maskers. This interface currently holds all extensions to the original ADO masker interface.

}
}
2 changes: 1 addition & 1 deletion src/Agent.Sdk/SecretMasking/ISecret.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ internal interface ISecret
/// <summary>
/// Returns one item (start, length) for each match found in the input string.
/// </summary>
IEnumerable<ReplacementPosition> GetPositions(String input);
IEnumerable<Replacement> GetReplacements(String input);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replacement

ReplacementPositions now has additional metadata, for example, the preferred token to use for redaction, so I performed a rename.

}
28 changes: 26 additions & 2 deletions src/Agent.Sdk/SecretMasking/LegacyLoggedSecretMasker.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
using Agent.Sdk.SecretMasking;
using Microsoft.TeamFoundation.DistributedTask.Logging;

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Threading;

using ISecretMasker = Microsoft.TeamFoundation.DistributedTask.Logging.ISecretMasker;

namespace Agent.Sdk.Util.SecretMasking;
Expand All @@ -11,6 +18,7 @@ namespace Agent.Sdk.Util.SecretMasking;
public class LegacyLoggedSecretMasker : ILoggedSecretMasker
{
private ISecretMasker _secretMasker;
private double elapsedMaskingTime;
private ITraceWriter _trace;

private void Trace(string msg)
Expand Down Expand Up @@ -60,7 +68,7 @@ public void AddRegex(string pattern)
/// </summary>
/// <param name="pattern"></param>
/// <param name="origin"></param>
public void AddRegex(string pattern, string origin)
public void AddRegex(string pattern, string origin, string moniker = null, ISet<string> sniffLiterals = null, RegexOptions regexOptions = 0)
{
this.Trace($"Setting up regex for origin: {origin}.");
if (pattern == null)
Expand Down Expand Up @@ -129,13 +137,29 @@ public ISecretMasker Clone()
return new LegacyLoggedSecretMasker(this._secretMasker.Clone());
}


public double ElapsedMaskingTime => this.elapsedMaskingTime;

public string MaskSecrets(string input)
{
return this._secretMasker.MaskSecrets(input);
var stopwatch = Stopwatch.StartNew();
string result = this._secretMasker.MaskSecrets(input);
Interlocked.Exchange(ref elapsedMaskingTime, stopwatch.ElapsedTicks);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exchange

I'm not sure what threading is in play in the agent. The masker is thread-safe for the masking operation, so I used Interlocked.Exchange to update the elapsed time property.

return result;
}

Sdk.SecretMasking.ISecretMasker Sdk.SecretMasking.ISecretMasker.Clone()
{
return new LegacyLoggedSecretMasker(this._secretMasker.Clone());
}

public IDictionary<string, string> GetTelemetry()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetTelemetry

Note that we only produce the ElapsedMaskingTime data point for the legacy masker, as I think we are only really concerned about relative runtime perf differences between the maskers (i.e., and other telemetry isn't interesting/won't be consumed for the legacy masker).

{
var result = new Dictionary<string, string>
{
{ nameof(ElapsedMaskingTime), elapsedMaskingTime.ToString() },
};

return result;
}
}
57 changes: 19 additions & 38 deletions src/Agent.Sdk/SecretMasking/LoggedSecretMasker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,28 @@
using System;
using ValueEncoder = Microsoft.TeamFoundation.DistributedTask.Logging.ValueEncoder;
using ISecretMaskerVSO = Microsoft.TeamFoundation.DistributedTask.Logging.ISecretMasker;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace Agent.Sdk.SecretMasking
{
/// <summary>
/// Extended secret masker service, that allows to log origins of secrets
/// </summary>
public class LoggedSecretMasker : ILoggedSecretMasker
public class LoggedSecretMasker : SecretMasker, ILoggedSecretMasker
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SecretMasker

The logged secret masker (as opposed to the legacy logged secret masker) can extend SecretMasker rather than wrapping it (because no other ILoggedSecretMasker will be wrapped by this class). This change will help us switch over to using a SecretMasker instance that 1ES provides that lives in a different repo (https://github.com/microsoft/security-utilities).

{
private ISecretMasker _secretMasker;
private ITraceWriter _trace;


private void Trace(string msg)
{
this._trace?.Info(msg);
}

public LoggedSecretMasker(ISecretMasker secretMasker)
{
this._secretMasker = secretMasker;
}

public void SetTrace(ITraceWriter trace)
{
this._trace = trace;
}

public void AddValue(string pattern)
{
this._secretMasker.AddValue(pattern);
}

/// <summary>
/// Overloading of AddValue method with additional logic for logging origin of provided secret
/// </summary>
Expand All @@ -52,17 +42,13 @@ public void AddValue(string value, string origin)

AddValue(value);
}
public void AddRegex(string pattern)
{
this._secretMasker.AddRegex(pattern);
}

/// <summary>
/// Overloading of AddRegex method with additional logic for logging origin of provided secret
/// </summary>
/// <param name="pattern"></param>
/// <param name="origin"></param>
public void AddRegex(string pattern, string origin)
public void AddRegex(string pattern, string origin, string moniker, ISet<string> sniffLiterals, RegexOptions regexOptions)
{
this.Trace($"Setting up regex for origin: {origin}.");
if (pattern == null)
Expand All @@ -71,44 +57,38 @@ public void AddRegex(string pattern, string origin)
return;
}

AddRegex(pattern);
AddRegex(pattern, moniker, sniffLiterals, regexOptions);
}

// We don't allow to skip secrets longer than 5 characters.
// Note: the secret that will be ignored is of length n-1.
public static int MinSecretLengthLimit => 6;

public int MinSecretLength
public override int MinSecretLength
{
get
{
return _secretMasker.MinSecretLength;
return base.MinSecretLength;
}
set
{
if (value > MinSecretLengthLimit)
{
_secretMasker.MinSecretLength = MinSecretLengthLimit;
base.MinSecretLength = MinSecretLengthLimit;
}
else
{
_secretMasker.MinSecretLength = value;
base.MinSecretLength = value;
}
}
}

public void RemoveShortSecretsFromDictionary()
{
this._trace?.Info("Removing short secrets from masking dictionary");
_secretMasker.RemoveShortSecretsFromDictionary();
}

public void AddValueEncoder(ValueEncoder encoder)
{
this._secretMasker.AddValueEncoder(encoder);
base.RemoveShortSecretsFromDictionary();
}


/// <summary>
/// Overloading of AddValueEncoder method with additional logic for logging origin of provided secret
/// </summary>
Expand All @@ -127,16 +107,17 @@ public void AddValueEncoder(ValueEncoder encoder, string origin)
AddValueEncoder(encoder);
}

public ISecretMasker Clone()
{
return new LoggedSecretMasker(this._secretMasker.Clone());
}
ISecretMaskerVSO ISecretMaskerVSO.Clone() => this.Clone();

public string MaskSecrets(string input)
public IDictionary<string, string> GetTelemetry()
{
return this._secretMasker.MaskSecrets(input);
}
var result = new Dictionary<string, string>
{
{ nameof(ElapsedMaskingTime), ElapsedMaskingTime.ToString() },
};

ISecretMaskerVSO ISecretMaskerVSO.Clone() => this.Clone();
result["Redactions"] = string.Join(',', ReplacementTokens);
return result;
}
}
}
20 changes: 20 additions & 0 deletions src/Agent.Sdk/SecretMasking/PatternDescriptor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.


using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace Microsoft.VisualStudio.Services.Agent
{
public class PatternDescriptor
{
public string Regex { get; set; }

public string Moniker { get; set; }

public ISet<string> SniffLiterals { get; set; }

public RegexOptions RegexOptions { get; set; }
}
}
Loading
Loading