From bca5bc7292cd0db4cf19cc7c875b03e13fdd6701 Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Sun, 9 Mar 2025 06:05:41 -0700 Subject: [PATCH] Valid HTML Input name in FormInputElementPart (#17567) --- .../FormInputElementPartDisplayDriver.cs | 12 +++++++++- .../OrchardCore.Forms/Migrations.cs | 17 ++++++++++---- .../FormInputElementPart.Fields.Edit.cshtml | 4 ++-- .../Utilities/StringExtensions.cs | 23 +++++++++++++++++++ 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/FormInputElementPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/FormInputElementPartDisplayDriver.cs index f999ee103ab..c6fb7990af9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/FormInputElementPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/FormInputElementPartDisplayDriver.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Localization; using OrchardCore.ContentManagement.Display.ContentDisplay; using OrchardCore.ContentManagement.Display.Models; +using OrchardCore.ContentManagement.Utilities; using OrchardCore.DisplayManagement.Views; using OrchardCore.Forms.Models; using OrchardCore.Forms.ViewModels; @@ -35,8 +36,17 @@ public override async Task UpdateAsync(FormInputElementPart part { context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Name), S["A value is required for Name."]); } + else + { + var safeName = viewModel.Name.GetSafeHTMLInputName(); + + if (viewModel.Name != safeName) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Name), S["A Name contains invalid characters."]); + } + } - part.Name = viewModel.Name?.Trim(); + part.Name = viewModel.Name; part.ContentItem.DisplayText = part.Name; return Edit(part, context); diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Migrations.cs index 06000b541b0..3a699b03e0b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Migrations.cs @@ -23,14 +23,21 @@ await _contentDefinitionManager.AlterPartDefinitionAsync("FormPart", part => par await _contentDefinitionManager.AlterTypeDefinitionAsync("Form", type => type .WithPart("TitlePart", part => part - .WithSettings(new TitlePartSettings { RenderTitle = false }) + .WithSettings(new TitlePartSettings + { + RenderTitle = false, + }) .WithPosition("0") ) - .WithPart("FormElementPart", part => - part.WithPosition("1") + .WithPart("FormElementPart", part => part + .WithPosition("1") + ) + .WithPart("FormPart", part => part + .WithPosition("2") + ) + .WithPart("FlowPart", part => part + .WithPosition("3") ) - .WithPart("FormPart") - .WithPart("FlowPart") .Stereotype("Widget")); // FormElement diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Views/Items/FormInputElementPart.Fields.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.Forms/Views/Items/FormInputElementPart.Fields.Edit.cshtml index ade7324759d..a519a12f4f9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Views/Items/FormInputElementPart.Fields.Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Views/Items/FormInputElementPart.Fields.Edit.cshtml @@ -3,9 +3,9 @@ @model FormInputElementPartEditViewModel
- +
- @T["The name to render on this form element."] + @T["The valid characters for the HTML input name attribute are letters (a-z, A-Z), digits (0-9), hyphen (-), underscore (_), period (.), and square brackets ([ ])."]
diff --git a/src/OrchardCore/OrchardCore.ContentManagement.Abstractions/Utilities/StringExtensions.cs b/src/OrchardCore/OrchardCore.ContentManagement.Abstractions/Utilities/StringExtensions.cs index b8b13de7723..96ed8b6365f 100644 --- a/src/OrchardCore/OrchardCore.ContentManagement.Abstractions/Utilities/StringExtensions.cs +++ b/src/OrchardCore/OrchardCore.ContentManagement.Abstractions/Utilities/StringExtensions.cs @@ -385,4 +385,27 @@ public static string ReplaceLastOccurrence(this string source, string searchedVa return source.Remove(lastIndex, searchedValue.Length).Insert(lastIndex, replacedValue); } + + private const string _validHtmlInputNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.[]"; + + public static string GetSafeHTMLInputName(this string input) + { + ArgumentException.ThrowIfNullOrEmpty(input); + + var inputSpan = input.AsSpan(); + + var sanitizedName = new StringBuilder(inputSpan.Length); + + foreach (var c in inputSpan) + { + if (!_validHtmlInputNameCharacters.Contains(c)) + { + continue; + } + + sanitizedName.Append(c); + } + + return sanitizedName.ToString(); + } }