Validate workflow rule writes

This commit is contained in:
2026-06-02 10:22:15 +02:00
parent e9825f5bf5
commit a3bad1bdd4
7 changed files with 419 additions and 75 deletions
@@ -3,7 +3,6 @@ using System.Globalization;
using System.Text.RegularExpressions;
using MeetingAssistant.MeetingNotes;
using NCalc;
using RazorLight;
namespace MeetingAssistant.Workflow;
@@ -30,10 +29,6 @@ public sealed class NoopMeetingWorkflowEngine : IMeetingWorkflowEngine
public sealed class MeetingWorkflowEngine : IMeetingWorkflowEngine
{
private static readonly Regex EmailAddressPattern = new(
@"(?<![A-Za-z0-9.!#$%&'*+/=?^_`{|}~-])(?<local>[A-Za-z0-9.!#$%&'*+/=?^_`{|}~-]+)@(?<domain>(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+[A-Za-z]{2,63})(?![A-Za-z0-9-])",
RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
private static readonly string[] ParameterNames =
[
"meeting.attendees.count",
@@ -51,9 +46,7 @@ public sealed class MeetingWorkflowEngine : IMeetingWorkflowEngine
private readonly IMeetingNoteStore meetingNoteStore;
private readonly IMeetingArtifactStore meetingArtifactStore;
private readonly ILogger<MeetingWorkflowEngine> logger;
private readonly RazorLightEngine razorEngine = new RazorLightEngineBuilder()
.UseNoProject()
.Build();
private readonly MeetingWorkflowTemplateRenderer templateRenderer = new();
public MeetingWorkflowEngine(
IMeetingWorkflowRulesProvider rulesProvider,
@@ -256,63 +249,7 @@ public sealed class MeetingWorkflowEngine : IMeetingWorkflowEngine
string template,
MeetingWorkflowTemplateModel model)
{
if (!ContainsRazorTemplateSyntax(template))
{
return template;
}
return await razorEngine.CompileRenderStringAsync(
Guid.NewGuid().ToString("N"),
EscapeEmailAddressAtSigns(template),
model);
}
private static bool ContainsRazorTemplateSyntax(string value)
{
if (!value.Contains('@', StringComparison.Ordinal))
{
return false;
}
var emailAtSigns = EmailAddressPattern
.Matches(value)
.Select(match => match.Index + match.Value.IndexOf('@', StringComparison.Ordinal))
.ToHashSet();
for (var index = 0; index < value.Length; index++)
{
if (value[index] != '@')
{
continue;
}
if (index + 1 < value.Length && value[index + 1] == '@')
{
index++;
continue;
}
if (emailAtSigns.Contains(index))
{
continue;
}
return true;
}
return false;
}
private static string EscapeEmailAddressAtSigns(string value)
{
if (!value.Contains('@', StringComparison.Ordinal))
{
return value;
}
return EmailAddressPattern.Replace(
value,
match => match.Value.Replace("@", "@@", StringComparison.Ordinal));
return await templateRenderer.RenderAsync(template, model);
}
private static bool SetProperty(