Logging
ValueEncoders.cs
using System;
using System.ComponentModel;
using System.Security;
using System.Text;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
namespace GitHub.DistributedTask.Logging
{
[EditorBrowsable(EditorBrowsableState.Never)]
public delegate String ValueEncoder(String value);
[EditorBrowsable(EditorBrowsableState.Never)]
public static clast ValueEncoders
{
public static String Base64StringEscape(String value)
{
return Convert.ToBase64String(Encoding.UTF8.GetBytes(value));
}
// Base64 is 6 bits -> char
// A byte is 8 bits
// When end user doing somthing like base64(user:pastword)
// The length of the leading content will cause different base64 encoding result on the pastword
// So we add base64(value shifted 1 and two bytes) as secret as well.
// B1 B2 B3 B4 B5 B6 B7
// 000000|00 0000|0000 00|000000| 000000|00 0000|0000 00|000000|
// Char1 Char2 Char3 Char4
// See the above, the first byte has a character beginning at index 0, the second byte has a character beginning at index 4, the third byte has a character beginning at index 2 and then the pattern repeats
// We register byte offsets for all these possible values
public static String Base64StringEscapeShift1(String value)
{
return Base64StringEscapeShift(value, 1);
}
public static String Base64StringEscapeShift2(String value)
{
return Base64StringEscapeShift(value, 2);
}
// Used when we past environment variables to docker to escape " with \"
public static String CommandLineArgumentEscape(String value)
{
return value.Replace("\"", "\\\"");
}
public static String ExpressionStringEscape(String value)
{
return Expressions2.Sdk.ExpressionUtility.StringEscape(value);
}
public static String JsonStringEscape(String value)
{
// Convert to a JSON string and then remove the leading/trailing double-quote.
String jsonString = JsonConvert.ToString(value);
String jsonEscapedValue = jsonString.Substring(startIndex: 1, length: jsonString.Length - 2);
return jsonEscapedValue;
}
public static String UriDataEscape(String value)
{
return UriDataEscape(value, 65519);
}
public static String XmlDataEscape(String value)
{
return SecurityElement.Escape(value);
}
public static String TrimDoubleQuotes(String value)
{
var trimmed = string.Empty;
if (!string.IsNullOrEmpty(value) &&
value.Length > 8 &&
value.StartsWith('"') &&
value.EndsWith('"'))
{
trimmed = value.Substring(1, value.Length - 2);
}
return trimmed;
}
public static String PowerShellPreAmpersandEscape(String value)
{
// if the secret is pasted to PS as a command and it causes an error, sections of it can be surrounded by color codes
// or printed individually.
// The secret secretpart1&secretpart2&secretpart3 would be split into 2 sections:
// 'secretpart1&secretpart2&' and 'secretpart3'. This method masks for the first section.
// The secret secretpart1&+secretpart2&secretpart3 would be split into 2 sections:
// 'secretpart1&+' and (no 's') 'ecretpart2&secretpart3'. This method masks for the first section.
var trimmed = string.Empty;
if (!string.IsNullOrEmpty(value) && value.Contains("&"))
{
var secretSection = string.Empty;
if (value.Contains("&+"))
{
secretSection = value.Substring(0, value.IndexOf("&+") + "&+".Length);
}
else
{
secretSection = value.Substring(0, value.LastIndexOf("&") + "&".Length);
}
// Don't mask short secrets
if (secretSection.Length >= 6)
{
trimmed = secretSection;
}
}
return trimmed;
}
public static String PowerShellPostAmpersandEscape(String value)
{
var trimmed = string.Empty;
if (!string.IsNullOrEmpty(value) && value.Contains("&"))
{
var secretSection = string.Empty;
if (value.Contains("&+"))
{
// +1 to skip the letter that got colored
secretSection = value.Substring(value.IndexOf("&+") + "&+".Length + 1);
}
else
{
secretSection = value.Substring(value.LastIndexOf("&") + "&".Length);
}
if (secretSection.Length >= 6)
{
trimmed = secretSection;
}
}
return trimmed;
}
private static string Base64StringEscapeShift(String value, int shift)
{
var bytes = Encoding.UTF8.GetBytes(value);
if (bytes.Length > shift)
{
var shiftArray = new byte[bytes.Length - shift];
Array.Copy(bytes, shift, shiftArray, 0, bytes.Length - shift);
return Convert.ToBase64String(shiftArray);
}
else
{
return Convert.ToBase64String(bytes);
}
}
private static String UriDataEscape(
String value,
Int32 maxSegmentSize)
{
if (value.Length 1)
{
length--;
}
result.Append(Uri.EscapeDataString(value.Substring(i, length)));
i += length;
}
while (i < value.Length);
return result.ToString();
}
}
}