Validate dynamic text placeholder names (#6899)

This commit is contained in:
2026-06-03 07:01:37 -07:00
committed by GitHub
parent b49132e3de
commit 2c2e405ebc
3 changed files with 34 additions and 21 deletions
@@ -1,19 +1,28 @@
using System;
using System.Text.RegularExpressions;
namespace eagle {
public readonly struct DynamicTextPlaceholderName : IEquatable<DynamicTextPlaceholderName> {
private static readonly Regex ValidName =
new(@"^\p{L}[\p{L}\p{Nd}\p{Mn}\p{Mc}\p{Pc}\p{Cf}]*$",
RegexOptions.CultureInvariant);
public string Value { get; }
public DynamicTextPlaceholderName(string value) {
if (string.IsNullOrEmpty(value)) {
if (!IsValidName(value)) {
throw new ArgumentException(
"Dynamic text placeholder name cannot be empty",
$"Invalid dynamic text placeholder name: {value}",
nameof(value));
}
Value = value;
}
public static bool IsValidName(string value) {
return !string.IsNullOrEmpty(value) && ValidName.IsMatch(value);
}
public bool Equals(DynamicTextPlaceholderName other) { return Value == other.Value; }
public override bool Equals(object obj) {
@@ -44,28 +44,12 @@ namespace eagle {
if (closeBraceIndex < 0) { break; }
var name = ParsedText.Substring(i + 1, closeBraceIndex - i - 1);
if (IsPlaceholderName(name)) { names.Add(new DynamicTextPlaceholderName(name)); }
if (DynamicTextPlaceholderName.IsValidName(name)) {
names.Add(new DynamicTextPlaceholderName(name));
}
i = closeBraceIndex;
}
return names;
}
private static bool IsPlaceholderName(string name) {
if (string.IsNullOrEmpty(name)) { return false; }
if (!IsPlaceholderStart(name[0])) { return false; }
for (int i = 1; i < name.Length; i++) {
if (!IsPlaceholderPart(name[i])) { return false; }
}
return true;
}
private static bool IsPlaceholderStart(char c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
}
private static bool IsPlaceholderPart(char c) {
return IsPlaceholderStart(c) || (c >= '0' && c <= '9') || c == '_';
}
}
}
@@ -96,6 +96,26 @@ namespace eagle0.Tests {
Assert.AreEqual("HeroName2", Placeholders.HeroName.WithNumericSuffix(2).Value);
}
[Test]
public void PlaceholderName_ThrowsForInvalidNames() {
Assert.Throws<ArgumentException>(() => new DynamicTextPlaceholderName(""));
Assert.Throws<ArgumentException>(() => new DynamicTextPlaceholderName("{HeroName}"));
Assert.Throws<ArgumentException>(() => new DynamicTextPlaceholderName("Hero.Name"));
Assert.Throws<ArgumentException>(() => new DynamicTextPlaceholderName("1HeroName"));
}
[Test]
public void PlaceholderName_AllowsUnicodeLetters() {
var placeholderName = new DynamicTextPlaceholderName("\u82f1\u96c4Name");
var binding = new DynamicTextBinding(
DynamicTextTemplate.Parsed("{\u82f1\u96c4Name} arrived."),
new List<DynamicPlaceholder> {
DynamicPlaceholder.StaticText(placeholderName, "Zhao Yun"),
});
Assert.AreEqual("Zhao Yun arrived.", binding.Text);
}
[Test]
public void Notifications_DoNotParseTemplatesInlineAtCallBoundary() {
var offenders = new List<string>();