mirror of
https://github.com/actions/runner.git
synced 2025-12-27 20:13:15 +08:00
GitHub Actions Runner
This commit is contained in:
28
src/Sdk/DTPipelines/Pipelines/Expressions/CounterNode.cs
Normal file
28
src/Sdk/DTPipelines/Pipelines/Expressions/CounterNode.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using GitHub.DistributedTask.Expressions;
|
||||
|
||||
namespace GitHub.DistributedTask.Pipelines.Expressions
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public sealed class CounterNode : FunctionNode
|
||||
{
|
||||
protected override Object EvaluateCore(EvaluationContext evaluationContext)
|
||||
{
|
||||
int seed = 0;
|
||||
var prefix = String.Empty;
|
||||
if (Parameters.Count > 0)
|
||||
{
|
||||
prefix = Parameters[0].EvaluateString(evaluationContext);
|
||||
}
|
||||
|
||||
if (Parameters.Count > 1)
|
||||
{
|
||||
seed = Convert.ToInt32(Parameters[1].EvaluateNumber(evaluationContext));
|
||||
}
|
||||
|
||||
var context = evaluationContext.State as IPipelineContext;
|
||||
return context.CounterStore?.Increment(context, prefix, seed) ?? seed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using GitHub.DistributedTask.Expressions;
|
||||
|
||||
namespace GitHub.DistributedTask.Pipelines.Expressions
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static class ExpressionConstants
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the variables node.
|
||||
/// </summary>
|
||||
public static readonly String Variables = "variables";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pipeline context available in pipeline expressions.
|
||||
/// </summary>
|
||||
public static readonly INamedValueInfo PipelineNamedValue = new NamedValueInfo<PipelineContextNode>("pipeline");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the variable context available in pipeline expressions.
|
||||
/// </summary>
|
||||
public static readonly INamedValueInfo VariablesNamedValue = new NamedValueInfo<VariablesContextNode>("variables");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the counter function available in pipeline expressions.
|
||||
/// </summary>
|
||||
public static readonly IFunctionInfo CounterFunction = new FunctionInfo<CounterNode>("counter", 0, 2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using GitHub.DistributedTask.Expressions;
|
||||
|
||||
namespace GitHub.DistributedTask.Pipelines.Expressions
|
||||
{
|
||||
internal static class InputValidationConstants
|
||||
{
|
||||
public static readonly String IsEmail = "isEmail";
|
||||
public static readonly String IsInRange = "isInRange";
|
||||
public static readonly String IsIPv4Address = "isIPv4Address";
|
||||
public static readonly String IsSha1 = "isSha1";
|
||||
public static readonly String IsUrl = "isUrl";
|
||||
public static readonly String IsMatch = "isMatch";
|
||||
public static readonly String Length = "length";
|
||||
|
||||
public static readonly IFunctionInfo[] Functions = new IFunctionInfo[]
|
||||
{
|
||||
new FunctionInfo<IsEmailNode>(InputValidationConstants.IsEmail, IsEmailNode.minParameters, IsEmailNode.maxParameters),
|
||||
new FunctionInfo<IsInRangeNode>(InputValidationConstants.IsInRange, IsInRangeNode.minParameters, IsInRangeNode.maxParameters),
|
||||
new FunctionInfo<IsIPv4AddressNode>(InputValidationConstants.IsIPv4Address, IsIPv4AddressNode.minParameters, IsIPv4AddressNode.maxParameters),
|
||||
new FunctionInfo<IsMatchNode>(InputValidationConstants.IsMatch, IsMatchNode.minParameters, IsMatchNode.maxParameters),
|
||||
new FunctionInfo<IsSHA1Node>(InputValidationConstants.IsSha1, IsSHA1Node.minParameters, IsSHA1Node.maxParameters),
|
||||
new FunctionInfo<IsUrlNode>(InputValidationConstants.IsUrl, IsUrlNode.minParameters, IsUrlNode.maxParameters),
|
||||
new FunctionInfo<LengthNode>(InputValidationConstants.Length, LengthNode.minParameters, LengthNode.maxParameters),
|
||||
};
|
||||
|
||||
public static readonly INamedValueInfo[] NamedValues = new INamedValueInfo[]
|
||||
{
|
||||
new NamedValueInfo<InputValueNode>("value"),
|
||||
};
|
||||
}
|
||||
}
|
||||
15
src/Sdk/DTPipelines/Pipelines/Expressions/InputValueNode.cs
Normal file
15
src/Sdk/DTPipelines/Pipelines/Expressions/InputValueNode.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using GitHub.DistributedTask.Expressions;
|
||||
using GitHub.DistributedTask.Pipelines.Validation;
|
||||
|
||||
namespace GitHub.DistributedTask.Pipelines.Expressions
|
||||
{
|
||||
internal class InputValueNode : NamedValueNode
|
||||
{
|
||||
protected sealed override Object EvaluateCore(EvaluationContext evaluationContext)
|
||||
{
|
||||
var validationContext = evaluationContext.State as InputValidationContext;
|
||||
return validationContext.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/Sdk/DTPipelines/Pipelines/Expressions/IsEmailNode.cs
Normal file
22
src/Sdk/DTPipelines/Pipelines/Expressions/IsEmailNode.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using GitHub.DistributedTask.Expressions;
|
||||
|
||||
namespace GitHub.DistributedTask.Pipelines.Expressions
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public sealed class IsEmailNode : FunctionNode
|
||||
{
|
||||
protected sealed override Boolean TraceFullyRealized => false;
|
||||
|
||||
public static Int32 minParameters = 1;
|
||||
public static Int32 maxParameters = 1;
|
||||
|
||||
protected sealed override Object EvaluateCore(EvaluationContext context)
|
||||
{
|
||||
// isEmail(value: string)
|
||||
String value = Parameters[0].EvaluateString(context) ?? String.Empty;
|
||||
return RegexUtility.IsMatch(value, WellKnownRegularExpressions.Email);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using GitHub.DistributedTask.Expressions;
|
||||
|
||||
namespace GitHub.DistributedTask.Pipelines.Expressions
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public sealed class IsIPv4AddressNode : FunctionNode
|
||||
{
|
||||
protected sealed override Boolean TraceFullyRealized => false;
|
||||
|
||||
public static Int32 minParameters = 1;
|
||||
public static Int32 maxParameters = 1;
|
||||
|
||||
protected sealed override Object EvaluateCore(EvaluationContext context)
|
||||
{
|
||||
// isIpV4Address(value: string)
|
||||
String value = Parameters[0].EvaluateString(context) ?? String.Empty;
|
||||
return RegexUtility.IsMatch(value, WellKnownRegularExpressions.IPv4Address);
|
||||
}
|
||||
}
|
||||
}
|
||||
24
src/Sdk/DTPipelines/Pipelines/Expressions/IsInRangeNode.cs
Normal file
24
src/Sdk/DTPipelines/Pipelines/Expressions/IsInRangeNode.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using GitHub.DistributedTask.Expressions;
|
||||
|
||||
namespace GitHub.DistributedTask.Pipelines.Expressions
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public sealed class IsInRangeNode : FunctionNode
|
||||
{
|
||||
protected sealed override Boolean TraceFullyRealized => false;
|
||||
|
||||
public static Int32 minParameters = 3;
|
||||
public static Int32 maxParameters = 3;
|
||||
|
||||
protected sealed override Object EvaluateCore(EvaluationContext context)
|
||||
{
|
||||
// isInRange(value: string, min: string, max: string)
|
||||
decimal value = Parameters[0].EvaluateNumber(context);
|
||||
decimal min = Parameters[1].EvaluateNumber(context);
|
||||
decimal max = Parameters[2].EvaluateNumber(context);
|
||||
return value >= min && value <= max;
|
||||
}
|
||||
}
|
||||
}
|
||||
30
src/Sdk/DTPipelines/Pipelines/Expressions/IsMatchNode.cs
Normal file
30
src/Sdk/DTPipelines/Pipelines/Expressions/IsMatchNode.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using GitHub.DistributedTask.Expressions;
|
||||
|
||||
namespace GitHub.DistributedTask.Pipelines.Expressions
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public sealed class IsMatchNode : FunctionNode
|
||||
{
|
||||
protected sealed override Boolean TraceFullyRealized => false;
|
||||
|
||||
public static Int32 minParameters = 2;
|
||||
public static Int32 maxParameters = 3;
|
||||
|
||||
protected sealed override Object EvaluateCore(EvaluationContext context)
|
||||
{
|
||||
// isMatch(value: string, regEx: string, options?: string)
|
||||
String value = Parameters[0].EvaluateString(context) ?? String.Empty;
|
||||
String regEx = Parameters[1].EvaluateString(context) ?? String.Empty;
|
||||
String regExOptionsString = String.Empty;
|
||||
|
||||
if (Parameters.Count == 3)
|
||||
{
|
||||
regExOptionsString = Parameters[2].EvaluateString(context) ?? String.Empty;
|
||||
}
|
||||
|
||||
return RegexUtility.IsMatch(value, regEx, regExOptionsString);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/Sdk/DTPipelines/Pipelines/Expressions/IsSHA1Node.cs
Normal file
22
src/Sdk/DTPipelines/Pipelines/Expressions/IsSHA1Node.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using GitHub.DistributedTask.Expressions;
|
||||
|
||||
namespace GitHub.DistributedTask.Pipelines.Expressions
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public sealed class IsSHA1Node : FunctionNode
|
||||
{
|
||||
protected sealed override Boolean TraceFullyRealized => false;
|
||||
|
||||
public static Int32 minParameters = 1;
|
||||
public static Int32 maxParameters = 1;
|
||||
|
||||
protected sealed override Object EvaluateCore(EvaluationContext context)
|
||||
{
|
||||
// isSha1(value: string)
|
||||
String value = Parameters[0].EvaluateString(context) ?? String.Empty;
|
||||
return RegexUtility.IsMatch(value, WellKnownRegularExpressions.SHA1);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/Sdk/DTPipelines/Pipelines/Expressions/IsUrlNode.cs
Normal file
22
src/Sdk/DTPipelines/Pipelines/Expressions/IsUrlNode.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using GitHub.DistributedTask.Expressions;
|
||||
|
||||
namespace GitHub.DistributedTask.Pipelines.Expressions
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public sealed class IsUrlNode : FunctionNode
|
||||
{
|
||||
protected sealed override Boolean TraceFullyRealized => false;
|
||||
|
||||
public static Int32 minParameters = 1;
|
||||
public static Int32 maxParameters = 1;
|
||||
|
||||
protected sealed override Object EvaluateCore(EvaluationContext context)
|
||||
{
|
||||
// isUrl(value: string)
|
||||
String value = Parameters[0].EvaluateString(context) ?? String.Empty;
|
||||
return RegexUtility.IsMatch(value, WellKnownRegularExpressions.Url);
|
||||
}
|
||||
}
|
||||
}
|
||||
63
src/Sdk/DTPipelines/Pipelines/Expressions/LengthNode.cs
Normal file
63
src/Sdk/DTPipelines/Pipelines/Expressions/LengthNode.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using GitHub.DistributedTask.Expressions;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace GitHub.DistributedTask.Pipelines.Expressions
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public sealed class LengthNode : FunctionNode
|
||||
{
|
||||
protected sealed override Boolean TraceFullyRealized => false;
|
||||
|
||||
public static Int32 minParameters = 1;
|
||||
public static Int32 maxParameters = 1;
|
||||
|
||||
protected sealed override Object EvaluateCore(EvaluationContext context)
|
||||
{
|
||||
// Length(value: object)
|
||||
var evaluationResult = Parameters[0].Evaluate(context);
|
||||
bool kindNotSupported = false;
|
||||
Int32 length = -1;
|
||||
|
||||
switch (evaluationResult.Kind)
|
||||
{
|
||||
case ValueKind.Array:
|
||||
length = ((JArray)evaluationResult.Value).Count;
|
||||
break;
|
||||
case ValueKind.String:
|
||||
length = ((String)evaluationResult.Value).Length;
|
||||
break;
|
||||
case ValueKind.Object:
|
||||
if (evaluationResult.Value is IReadOnlyDictionary<String, Object>)
|
||||
{
|
||||
length = ((IReadOnlyDictionary<String, Object>)evaluationResult.Value).Count;
|
||||
}
|
||||
else if (evaluationResult.Value is ICollection)
|
||||
{
|
||||
length = ((ICollection)evaluationResult.Value).Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
kindNotSupported = true;
|
||||
}
|
||||
break;
|
||||
case ValueKind.Boolean:
|
||||
case ValueKind.Null:
|
||||
case ValueKind.Number:
|
||||
case ValueKind.Version:
|
||||
kindNotSupported = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (kindNotSupported)
|
||||
{
|
||||
throw new NotSupportedException(PipelineStrings.InvalidTypeForLengthFunction(evaluationResult.Kind));
|
||||
}
|
||||
|
||||
return new Decimal(length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Collections.Generic;
|
||||
using GitHub.DistributedTask.Expressions;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
|
||||
namespace GitHub.DistributedTask.Pipelines.Expressions
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
internal sealed class PipelineContextNode : NamedValueNode
|
||||
{
|
||||
protected override Object EvaluateCore(EvaluationContext context)
|
||||
{
|
||||
var state = context.State as IPipelineContext;
|
||||
var result = new Dictionary<String, Object>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// startTime
|
||||
if (state.Variables.TryGetValue(WellKnownDistributedTaskVariables.PipelineStartTime, out VariableValue startTimeVariable) &&
|
||||
!String.IsNullOrEmpty(startTimeVariable.Value))
|
||||
{
|
||||
// Leverage the expression SDK to convert to datetime
|
||||
var startTimeResult = EvaluationResult.CreateIntermediateResult(context, startTimeVariable.Value, out _);
|
||||
if (startTimeResult.TryConvertToDateTime(context, out DateTimeOffset startTime))
|
||||
{
|
||||
result["startTime"] = startTime;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
153
src/Sdk/DTPipelines/Pipelines/Expressions/RegexUtility.cs
Normal file
153
src/Sdk/DTPipelines/Pipelines/Expressions/RegexUtility.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
|
||||
namespace GitHub.DistributedTask.Pipelines.Expressions
|
||||
{
|
||||
public static class RegexUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets default timeout for regex
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static TimeSpan GetRegexTimeOut()
|
||||
{
|
||||
return s_regexTimeout;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs regex single match with ECMAScript-complaint behavior
|
||||
/// Will throw RegularExpressionFailureException if regular expression parsing error occurs or if regular expression takes more than allotted time to execute
|
||||
/// Supported regex options - 'i' (ignorecase), 'm' (multiline)
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="regex"></param>
|
||||
/// <param name="regexOptionsString"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsMatch(
|
||||
String value,
|
||||
String regexPattern,
|
||||
String regexOptionsString)
|
||||
{
|
||||
return IsSafeMatch(value, regexPattern, ConvertToRegexOptions(regexOptionsString));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs regex single match with ECMAScript-complaint behavior
|
||||
/// Will throw RegularExpressionFailureException if regular expression parsing error occurs or if regular expression takes more than allotted time to execute
|
||||
/// If the key is not known, returns true
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="wellKnownRegexKey">One of WellKnownRegularExpressionKeys</param>
|
||||
/// <returns></returns>
|
||||
public static bool IsMatch(
|
||||
String value,
|
||||
String wellKnownRegexKey)
|
||||
{
|
||||
Lazy<Regex> lazyRegex = WellKnownRegularExpressions.GetRegex(wellKnownRegexKey);
|
||||
if (lazyRegex == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Regex regex = lazyRegex.Value;
|
||||
return IsSafeMatch(value, x => regex.Match(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts regex in string to RegExOptions, valid flags are "i", "m"
|
||||
/// Throws RegularExpressionInvalidOptionsException if there are any invalid options
|
||||
/// </summary>
|
||||
/// <param name="regexOptions"></param>
|
||||
/// <returns></returns>
|
||||
public static RegexOptions ConvertToRegexOptions(String regexOptions)
|
||||
{
|
||||
RegexOptions result;
|
||||
if (TryConvertToRegexOptions(regexOptions, out result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
throw new RegularExpressionInvalidOptionsException(PipelineStrings.InvalidRegexOptions(regexOptions, String.Join(",", WellKnownRegexOptions.All)));
|
||||
}
|
||||
|
||||
private static bool TryConvertToRegexOptions(
|
||||
String regexOptions,
|
||||
out RegexOptions result)
|
||||
{
|
||||
// Eg: "IgnoreCase, MultiLine" or "IgnoreCase"
|
||||
result = RegexOptions.ECMAScript | RegexOptions.CultureInvariant;
|
||||
|
||||
if (String.IsNullOrEmpty(regexOptions))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
String[] regexOptionValues = regexOptions.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
for (int i = 0; i < regexOptionValues.Length; i++)
|
||||
{
|
||||
String option = regexOptionValues[i];
|
||||
|
||||
if (String.Equals(option, WellKnownRegexOptions.IgnoreCase, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result = result | RegexOptions.IgnoreCase;
|
||||
}
|
||||
else if (String.Equals(option, WellKnownRegexOptions.Multiline, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result = result | RegexOptions.Multiline;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Boolean IsSafeMatch(
|
||||
String value,
|
||||
Func<String, Match> getSafeMatch)
|
||||
{
|
||||
Boolean result = true;
|
||||
try
|
||||
{
|
||||
var match = getSafeMatch(value);
|
||||
result = match.Success;
|
||||
}
|
||||
catch (Exception ex) when (ex is RegexMatchTimeoutException || ex is ArgumentException)
|
||||
{
|
||||
throw new RegularExpressionValidationFailureException(PipelineStrings.RegexFailed(value, ex.Message), ex);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Boolean IsSafeMatch(
|
||||
String value,
|
||||
String regex,
|
||||
RegexOptions regexOptions)
|
||||
{
|
||||
return IsSafeMatch(value, x => GetSafeMatch(x, regex, regexOptions));
|
||||
}
|
||||
|
||||
private static Match GetSafeMatch(
|
||||
String value,
|
||||
String regex,
|
||||
RegexOptions regexOptions)
|
||||
{
|
||||
return Regex.Match(value, regex, regexOptions, s_regexTimeout);
|
||||
}
|
||||
|
||||
// 2 seconds should be enough mostly, per DataAnnotations class - http://index/?query=REGEX_DEFAULT_MATCH_TIMEOUT
|
||||
private static TimeSpan s_regexTimeout = TimeSpan.FromSeconds(2);
|
||||
|
||||
private static class WellKnownRegexOptions
|
||||
{
|
||||
public static String IgnoreCase = nameof(IgnoreCase);
|
||||
public static String Multiline = nameof(Multiline);
|
||||
public static String[] All = new String[] { IgnoreCase, Multiline };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using GitHub.DistributedTask.Expressions;
|
||||
|
||||
namespace GitHub.DistributedTask.Pipelines.Expressions
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public sealed class VariablesContextNode : NamedValueNode
|
||||
{
|
||||
protected override Object EvaluateCore(EvaluationContext context)
|
||||
{
|
||||
var executionContext = context.State as IPipelineContext;
|
||||
return executionContext.Variables;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace GitHub.DistributedTask.Pipelines.Expressions
|
||||
{
|
||||
public static class WellKnownRegularExpressions
|
||||
{
|
||||
public const String Email = nameof(Email);
|
||||
public const String IPv4Address = nameof(IPv4Address);
|
||||
public const String SHA1 = nameof(SHA1);
|
||||
public const String Url = nameof(Url);
|
||||
|
||||
/// <summary>
|
||||
/// Returns null if it's not a well-known type
|
||||
/// </summary>
|
||||
/// <param name="regexType"></param>
|
||||
/// <returns></returns>
|
||||
public static Lazy<Regex> GetRegex(String regexType)
|
||||
{
|
||||
switch (regexType)
|
||||
{
|
||||
case Email:
|
||||
return s_validEmail;
|
||||
case IPv4Address:
|
||||
return s_validIPv4Address;
|
||||
case SHA1:
|
||||
return s_validSha1;
|
||||
case Url:
|
||||
return s_validUrl;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// regex from http://index/?leftProject=System.ComponentModel.DataAnnotations&leftSymbol=cmnlm5e7vdio&file=DataAnnotations%5CEmailAddressAttribute.cs&rightSymbol=jfeiathypuap
|
||||
private static readonly Lazy<Regex> s_validEmail = new Lazy<Regex>(() => new Regex(
|
||||
@"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$",
|
||||
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled, RegexUtility.GetRegexTimeOut()
|
||||
)
|
||||
);
|
||||
|
||||
// simple check - {1 to 3 digits}.{1 to 3 digits}.{1 to 3 digits}.{1 to 3 digits}
|
||||
private static readonly Lazy<Regex> s_validIPv4Address = new Lazy<Regex>(() => new Regex(
|
||||
@"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}",
|
||||
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled, RegexUtility.GetRegexTimeOut()
|
||||
)
|
||||
);
|
||||
|
||||
// 40 hex characters
|
||||
private static readonly Lazy<Regex> s_validSha1 = new Lazy<Regex>(() => new Regex(
|
||||
@"\b[0-9a-f]{40}\b",
|
||||
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled, RegexUtility.GetRegexTimeOut()
|
||||
)
|
||||
);
|
||||
|
||||
// regex from http://index/?leftProject=System.ComponentModel.DataAnnotations&leftSymbol=gk29yrysvq6y&file=DataAnnotations%5CUrlAttribute.cs&line=11
|
||||
private static readonly Lazy<Regex> s_validUrl = new Lazy<Regex>(() => new Regex(
|
||||
@"^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$",
|
||||
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled, RegexUtility.GetRegexTimeOut()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user