Add HandlebarsSettings (#1271)

* Add HandlebarsSettings class

* DefaultAllowedHandlebarsHelpers

* HB - 2.5.0-preview-01

* readme

* fix

* readme

* Handlebars.Net.Helpers Version="2.5.0"
This commit is contained in:
Stef Heyenrath
2025-04-23 07:47:37 +02:00
committed by GitHub
parent beabba4064
commit fc0f82db33
13 changed files with 127 additions and 27 deletions

View File

@@ -65,6 +65,17 @@ A breaking change is introduced which is related to System.Linq.Dynamic.Core Dyn
- The `LinqMatcher` is not allowed. - The `LinqMatcher` is not allowed.
- The [Handlebars.Net.Helpers.DynamicLinq](https://www.nuget.org/packages/Handlebars.Net.Helpers.DynamicLinq) package is not included anymore. - The [Handlebars.Net.Helpers.DynamicLinq](https://www.nuget.org/packages/Handlebars.Net.Helpers.DynamicLinq) package is not included anymore.
### 1.8.0
Some breaking changes are introduced in this version:
#### Handlebars.Net `File`-helper
By default, the internal Handlebars.Net `File`-helper is not allowed anymore because of potential security issues.
To still enable this feature, you need to set the `AllowedCustomHandlebarHelpers` property to `File` in the `HandlebarsSettings` property in `WireMockServerSettings`.
#### Handlebars.Net `Environment`-helper
By default, the Handlebars.Net `Environment`-helper is not automatically allowed anymore because of potential security issues.
To still enable this feature, you need to add the `Environment` category to the `AllowedHandlebarsHelpers` list-property in the `HandlebarsSettings` property in `WireMockServerSettings`.
--- ---
## :memo: Development ## :memo: Development

View File

@@ -6,7 +6,7 @@ namespace WireMock.Types;
/// A enum defining the supported Handlebar helpers. /// A enum defining the supported Handlebar helpers.
/// </summary> /// </summary>
[Flags] [Flags]
public enum CustomHandlebarHelpers public enum CustomHandlebarsHelpers
{ {
None = 0, None = 0,

View File

@@ -0,0 +1,50 @@
// Copyright © WireMock.Net
using HandlebarsDotNet.Helpers.Enums;
using JetBrains.Annotations;
using WireMock.Types;
namespace WireMock.Settings;
/// <summary>
/// HandlebarsSettings
/// </summary>
[PublicAPI]
public class HandlebarsSettings
{
internal static readonly Category[] DefaultAllowedHandlebarsHelpers =
[
Category.Boolean,
Category.Constants,
Category.DateTime,
Category.Enumerable,
Category.Humanizer,
Category.JsonPath,
Category.Math,
Category.Object,
Category.Random,
Category.Regex,
Category.String,
Category.Url,
Category.Xeger,
Category.XPath,
Category.Xslt
];
/// <summary>
/// Defines the allowed custom HandlebarsHelpers which can be used. Possible values are:
/// - <see cref="CustomHandlebarsHelpers.None"/> (Default)
/// - <see cref="CustomHandlebarsHelpers.File"/>
/// - <see cref="CustomHandlebarsHelpers.All"/>
/// </summary>
[PublicAPI]
public CustomHandlebarsHelpers AllowedCustomHandlebarsHelpers { get; set; } = CustomHandlebarsHelpers.None;
/// <summary>
/// Defines the allowed HandlebarHelpers which can be used.
///
/// By default, all categories except <see cref="Category.DynamicLinq"/> and <see cref="Category.Environment"/> are registered.
/// </summary>
[PublicAPI]
public Category[] AllowedHandlebarsHelpers { get; set; } = DefaultAllowedHandlebarsHelpers;
}

View File

@@ -98,7 +98,7 @@ public class ProxyAndRecordSettings : HttpClientSettings
public bool UseDefinedRequestMatchers { get; set; } public bool UseDefinedRequestMatchers { get; set; }
/// <summary> /// <summary>
/// Append an unique GUID to the filename from the saved mapping file. /// Append a unique GUID to the filename from the saved mapping file.
/// </summary> /// </summary>
public bool AppendGuidToSavedMappingFile { get; set; } public bool AppendGuidToSavedMappingFile { get; set; }

View File

@@ -18,7 +18,7 @@ public class ProxyUrlReplaceSettings
public string NewValue { get; set; } = null!; public string NewValue { get; set; } = null!;
/// <summary> /// <summary>
/// Defines if the case should be ignore when replacing. /// Defines if the case should be ignored when replacing.
/// </summary> /// </summary>
public bool IgnoreCase { get; set; } public bool IgnoreCase { get; set; }
} }

View File

@@ -70,6 +70,11 @@ internal class SimpleSettingsParser
return Arguments.ContainsKey(name); return Arguments.ContainsKey(name);
} }
public bool ContainsAny(params string[] names)
{
return names.Any(Arguments.ContainsKey);
}
public string[] GetValues(string name, string[] defaultValue) public string[] GetValues(string name, string[] defaultValue)
{ {
return Contains(name) ? Arguments[name] : defaultValue; return Contains(name) ? Arguments[name] : defaultValue;
@@ -142,6 +147,28 @@ internal class SimpleSettingsParser
}, defaultValue); }, defaultValue);
} }
public TEnum[] GetEnumValues<TEnum>(string name, TEnum[] defaultValues)
where TEnum : struct
{
var values = GetValues(name);
if (values == null)
{
return defaultValues;
}
var enums = new List<TEnum>();
foreach (var value in values)
{
if (Enum.TryParse<TEnum>(value, true, out var enumValue))
{
enums.Add(enumValue);
}
}
return enums.ToArray();
}
public string GetStringValue(string name, string defaultValue) public string GetStringValue(string name, string defaultValue)
{ {
return GetValue(name, values => values.FirstOrDefault() ?? defaultValue, defaultValue); return GetValue(name, values => values.FirstOrDefault() ?? defaultValue, defaultValue);

View File

@@ -16,13 +16,13 @@ public class WebProxySettings
public string Address { get; set; } = null!; public string Address { get; set; } = null!;
/// <summary> /// <summary>
/// The user name associated with the credentials. /// The username associated with the credentials.
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public string? UserName { get; set; } public string? UserName { get; set; }
/// <summary> /// <summary>
/// The password for the user name associated with the credentials. /// The password for the username associated with the credentials.
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public string? Password { get; set; } public string? Password { get; set; }

View File

@@ -331,11 +331,8 @@ public class WireMockServerSettings
public string? AdminPath { get; set; } public string? AdminPath { get; set; }
/// <summary> /// <summary>
/// Defines the allowed custom HandlebarHelpers which can be used. Possible values are: /// Defines the additional Handlebars Settings.
/// - <see cref="CustomHandlebarHelpers.None"/> (Default)
/// - <see cref="CustomHandlebarHelpers.File"/>
/// - <see cref="CustomHandlebarHelpers.All"/>
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public CustomHandlebarHelpers AllowedCustomHandlebarHelpers { get; set; } = CustomHandlebarHelpers.None; public HandlebarsSettings? HandlebarsSettings { get; set; }
} }

View File

@@ -50,7 +50,6 @@ public static class WireMockServerSettingsParser
AdminPath = parser.GetStringValue(nameof(WireMockServerSettings.AdminPath), "/__admin"), AdminPath = parser.GetStringValue(nameof(WireMockServerSettings.AdminPath), "/__admin"),
AllowBodyForAllHttpMethods = parser.GetBoolValue(nameof(WireMockServerSettings.AllowBodyForAllHttpMethods)), AllowBodyForAllHttpMethods = parser.GetBoolValue(nameof(WireMockServerSettings.AllowBodyForAllHttpMethods)),
AllowCSharpCodeMatcher = parser.GetBoolValue(nameof(WireMockServerSettings.AllowCSharpCodeMatcher)), AllowCSharpCodeMatcher = parser.GetBoolValue(nameof(WireMockServerSettings.AllowCSharpCodeMatcher)),
AllowedCustomHandlebarHelpers = parser.GetEnumValue(nameof(WireMockServerSettings.AllowedCustomHandlebarHelpers), CustomHandlebarHelpers.None),
AllowOnlyDefinedHttpStatusCodeInResponse = parser.GetBoolValue(nameof(WireMockServerSettings.AllowOnlyDefinedHttpStatusCodeInResponse)), AllowOnlyDefinedHttpStatusCodeInResponse = parser.GetBoolValue(nameof(WireMockServerSettings.AllowOnlyDefinedHttpStatusCodeInResponse)),
AllowPartialMapping = parser.GetBoolValue(nameof(WireMockServerSettings.AllowPartialMapping)), AllowPartialMapping = parser.GetBoolValue(nameof(WireMockServerSettings.AllowPartialMapping)),
Culture = parser.GetValue(nameof(WireMockServerSettings.Culture), strings => CultureInfoUtils.Parse(strings.FirstOrDefault()), CultureInfo.CurrentCulture), Culture = parser.GetValue(nameof(WireMockServerSettings.Culture), strings => CultureInfoUtils.Parse(strings.FirstOrDefault()), CultureInfo.CurrentCulture),
@@ -85,6 +84,7 @@ public static class WireMockServerSettingsParser
ParsePortSettings(settings, parser); ParsePortSettings(settings, parser);
ParseProxyAndRecordSettings(settings, parser); ParseProxyAndRecordSettings(settings, parser);
ParseCertificateSettings(settings, parser); ParseCertificateSettings(settings, parser);
ParseHandlebarsSettings(settings, parser);
return true; return true;
} }
@@ -153,7 +153,7 @@ public static class WireMockServerSettingsParser
} }
else if (settings.HostingScheme is null) else if (settings.HostingScheme is null)
{ {
settings.Urls = parser.GetValues("Urls", new[] { "http://*:9091/" }); settings.Urls = parser.GetValues("Urls", ["http://*:9091/"]);
} }
} }
@@ -167,12 +167,25 @@ public static class WireMockServerSettingsParser
X509CertificateFilePath = parser.GetStringValue("X509CertificateFilePath"), X509CertificateFilePath = parser.GetStringValue("X509CertificateFilePath"),
X509CertificatePassword = parser.GetStringValue("X509CertificatePassword") X509CertificatePassword = parser.GetStringValue("X509CertificatePassword")
}; };
if (certificateSettings.IsDefined) if (certificateSettings.IsDefined)
{ {
settings.CertificateSettings = certificateSettings; settings.CertificateSettings = certificateSettings;
} }
} }
private static void ParseHandlebarsSettings(WireMockServerSettings settings, SimpleSettingsParser parser)
{
if (parser.ContainsAny(nameof(HandlebarsSettings.AllowedCustomHandlebarsHelpers), nameof(HandlebarsSettings.AllowedHandlebarsHelpers)))
{
settings.HandlebarsSettings = new HandlebarsSettings
{
AllowedCustomHandlebarsHelpers = parser.GetEnumValue(nameof(HandlebarsSettings.AllowedCustomHandlebarsHelpers), CustomHandlebarsHelpers.None),
AllowedHandlebarsHelpers = parser.GetEnumValues(nameof(HandlebarsSettings.AllowedHandlebarsHelpers), HandlebarsSettings.DefaultAllowedHandlebarsHelpers)
};
}
}
private static void ParseWebProxyAddressSettings(ProxyAndRecordSettings settings, SimpleSettingsParser parser) private static void ParseWebProxyAddressSettings(ProxyAndRecordSettings settings, SimpleSettingsParser parser)
{ {
string? proxyAddress = parser.GetStringValue("WebProxyAddress"); string? proxyAddress = parser.GetStringValue("WebProxyAddress");

View File

@@ -4,8 +4,10 @@ using HandlebarsDotNet;
using HandlebarsDotNet.Helpers.Attributes; using HandlebarsDotNet.Helpers.Attributes;
using HandlebarsDotNet.Helpers.Enums; using HandlebarsDotNet.Helpers.Enums;
using HandlebarsDotNet.Helpers.Helpers; using HandlebarsDotNet.Helpers.Helpers;
using HandlebarsDotNet.Helpers.Options;
using Stef.Validation; using Stef.Validation;
using WireMock.Handlers; using WireMock.Handlers;
using WireMock.Settings;
namespace WireMock.Transformers.Handlebars; namespace WireMock.Transformers.Handlebars;
@@ -15,9 +17,9 @@ internal class FileHelpers : BaseHelpers, IHelpers
private readonly IFileSystemHandler _fileSystemHandler; private readonly IFileSystemHandler _fileSystemHandler;
public FileHelpers(IHandlebars context, IFileSystemHandler fileSystemHandler) : base(context) public FileHelpers(IHandlebars context, WireMockServerSettings settings) : base(context, new HandlebarsHelpersOptions())
{ {
_fileSystemHandler = Guard.NotNull(fileSystemHandler); _fileSystemHandler = Guard.NotNull(settings.FileSystemHandler);
} }
[HandlebarsWriter(WriterType.String, usage: HelperUsage.Both, passContext: true, name: Name)] [HandlebarsWriter(WriterType.String, usage: HelperUsage.Both, passContext: true, name: Name)]

View File

@@ -40,9 +40,9 @@ internal static class WireMockHandlebarsHelpers
o.CustomHelperPaths = paths; o.CustomHelperPaths = paths;
o.CustomHelpers = new Dictionary<string, IHelpers>(); o.CustomHelpers = new Dictionary<string, IHelpers>();
if (settings.AllowedCustomHandlebarHelpers.HasFlag(CustomHandlebarHelpers.File)) if (settings.HandlebarsSettings?.AllowedCustomHandlebarsHelpers.HasFlag(CustomHandlebarsHelpers.File) == true)
{ {
o.CustomHelpers.Add(FileHelpers.Name, new FileHelpers(handlebarsContext, settings.FileSystemHandler)); o.CustomHelpers.Add(FileHelpers.Name, new FileHelpers(handlebarsContext, settings));
} }
}); });
} }

View File

@@ -182,13 +182,13 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Handlebars.Net.Helpers" Version="2.4.10" /> <PackageReference Include="Handlebars.Net.Helpers" Version="2.5.0" />
<!--<PackageReference Include="Handlebars.Net.Helpers.DynamicLinq" Version="2.4.10" />--> <!--<PackageReference Include="Handlebars.Net.Helpers.DynamicLinq" Version="2.5.0" />-->
<PackageReference Include="Handlebars.Net.Helpers.Humanizer" Version="2.4.10" /> <PackageReference Include="Handlebars.Net.Helpers.Humanizer" Version="2.5.0" />
<PackageReference Include="Handlebars.Net.Helpers.Json" Version="2.4.10" /> <PackageReference Include="Handlebars.Net.Helpers.Json" Version="2.5.0" />
<PackageReference Include="Handlebars.Net.Helpers.Random" Version="2.4.10" /> <PackageReference Include="Handlebars.Net.Helpers.Random" Version="2.5.0" />
<PackageReference Include="Handlebars.Net.Helpers.Xeger" Version="2.4.10" /> <PackageReference Include="Handlebars.Net.Helpers.Xeger" Version="2.5.0" />
<PackageReference Include="Handlebars.Net.Helpers.XPath" Version="2.4.10" /> <PackageReference Include="Handlebars.Net.Helpers.XPath" Version="2.5.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'netstandard1.3' and '$(TargetFramework)' != 'net451' and '$(TargetFramework)' != 'net452' "> <ItemGroup Condition="'$(TargetFramework)' != 'netstandard1.3' and '$(TargetFramework)' != 'net451' and '$(TargetFramework)' != 'net452' ">

View File

@@ -4,7 +4,6 @@ using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using HandlebarsDotNet; using HandlebarsDotNet;
using HandlebarsDotNet.Helpers;
using Moq; using Moq;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using NFluent; using NFluent;
@@ -12,7 +11,6 @@ using WireMock.Handlers;
using WireMock.Models; using WireMock.Models;
using WireMock.ResponseBuilders; using WireMock.ResponseBuilders;
using WireMock.Settings; using WireMock.Settings;
using WireMock.Transformers.Handlebars;
using WireMock.Types; using WireMock.Types;
using Xunit; using Xunit;
@@ -35,7 +33,10 @@ public class ResponseWithHandlebarsFileTests
_settings = new() _settings = new()
{ {
AllowedCustomHandlebarHelpers = CustomHandlebarHelpers.File, HandlebarsSettings = new HandlebarsSettings
{
AllowedCustomHandlebarsHelpers = CustomHandlebarsHelpers.File
},
FileSystemHandler = _filesystemHandlerMock.Object FileSystemHandler = _filesystemHandlerMock.Object
}; };
} }
@@ -117,7 +118,6 @@ public class ResponseWithHandlebarsFileTests
// Assign // Assign
var settings = new WireMockServerSettings var settings = new WireMockServerSettings
{ {
AllowedCustomHandlebarHelpers = CustomHandlebarHelpers.None,
FileSystemHandler = _filesystemHandlerMock.Object FileSystemHandler = _filesystemHandlerMock.Object
}; };
var request = new RequestMessage(new UrlDetails("http://localhost:1234?id=x"), "GET", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost:1234?id=x"), "GET", ClientIp);