Skip to content

Commit

Permalink
Added option to allow custom CSS properties (variables) that begin wi…
Browse files Browse the repository at this point in the history
…th '--' characters. Defaults to 'false' so custom properties will be removed. Added protected function to allow for custom evaluation of the custom property name prior to marking it for removal.
  • Loading branch information
leniency committed Jul 25, 2024
1 parent f71dbb5 commit 83d115c
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 2 deletions.
20 changes: 19 additions & 1 deletion src/HtmlSanitizer/HtmlSanitizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ public HtmlSanitizer(HtmlSanitizerOptions options)
/// </value>
public ISet<string> AllowedCssProperties { get; private set; }

/// <summary>
/// Allow all custom CSS properties (variables) prefixed with <c>--</c>.
/// </summary>
public bool AllowCssCustomProperties { get; set; }

/// <summary>
/// Gets or sets a regex that must not match for legal CSS property values.
/// </summary>
Expand Down Expand Up @@ -726,6 +731,19 @@ protected void SanitizeStyle(IElement element, string baseUrl)
SanitizeStyleDeclaration(element, styles, baseUrl);
}

/// <summary>
/// Verify if the given CSS property name is allowed. By default this will
/// check if the property is in the <see cref="AllowedCssProperties"/> set,
/// or if the property is a custom property and <see cref="AllowCssCustomProperties"/> is true.
/// </summary>
/// <param name="propertyName">The name of the CSS property.</param>
/// <returns>True if the property is allowed or not.</returns>
protected virtual bool IsAllowedCssProperty(string propertyName)
{
return AllowedCssProperties.Contains(propertyName)
|| AllowCssCustomProperties && propertyName != null && propertyName.StartsWith("--");
}

private void SanitizeStyleDeclaration(IElement element, ICssStyleDeclaration styles, string baseUrl)
{
var removeStyles = new List<Tuple<ICssProperty, RemoveReason>>();
Expand All @@ -736,7 +754,7 @@ private void SanitizeStyleDeclaration(IElement element, ICssStyleDeclaration sty
var key = DecodeCss(style.Name);
var val = DecodeCss(style.Value);

if (!AllowedCssProperties.Contains(key))
if (!IsAllowedCssProperty(key))
{
removeStyles.Add(new Tuple<ICssProperty, RemoveReason>(style, RemoveReason.NotAllowedStyle));
continue;
Expand Down
42 changes: 41 additions & 1 deletion test/HtmlSanitizer.Tests/Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2903,7 +2903,7 @@ public void ThreadTest()
var waiting = numThreads;
var methods = typeof(HtmlSanitizerTests).GetTypeInfo().GetMethods()
.Where(m => m.GetCustomAttributes(typeof(Xunit.FactAttribute), false).Cast<Xunit.FactAttribute>().Any(f => f.Skip == null))
.Where(m => m.Name != "ThreadTest" && m.Name != "HexColorTest");
.Where(m => m.Name != nameof(ThreadTest) && m.Name != nameof(HexColorTest));
var threads = Shuffle(methods, random)
.Take(numThreads)
.Select(m => new Thread(() =>
Expand Down Expand Up @@ -3589,4 +3589,44 @@ public void KeepChildNodesTextTest()
var sanitized = sanitizer.Sanitize(input);
Assert.Equal("&lt;img&gt;&amp;lt;img&amp;gt;", sanitized);
}

[Fact]
public void AllowStyleAttributeCssCustomPropertiesTest()
{
var input = "<div style=\"--my-var: 1px\"></div>";
var sanitizer = new HtmlSanitizer { AllowCssCustomProperties = true };
sanitizer.AllowedTags.Remove("iframe");
var sanitized = sanitizer.Sanitize(input);
Assert.Equal("<div style=\"--my-var: 1px\"></div>", sanitized);
}

[Fact]
public void AllowStyleTagCssCustomPropertiesTest()
{
var input = "<style>:root { --my-var: 1px }</style>";
var sanitizer = new HtmlSanitizer { AllowCssCustomProperties = true };
sanitizer.AllowedTags.Add("style");
var sanitized = sanitizer.Sanitize(input);
Assert.Equal("<style>:root { --my-var: 1px }</style>", sanitized);
}

[Fact]
public void DisallowStyleAttributeCssCustomPropertiesTest()
{
var input = "<div style=\"--my-var: 1px\"></div>";
var sanitizer = new HtmlSanitizer();
sanitizer.AllowedTags.Remove("iframe");
var sanitized = sanitizer.Sanitize(input);
Assert.Equal("<div></div>", sanitized);
}

[Fact]
public void DisallowStyleTagCssCustomPropertiesTest()
{
var input = "<style>:root { --my-var: 1px }</style>";
var sanitizer = new HtmlSanitizer();
sanitizer.AllowedTags.Add("style");
var sanitized = sanitizer.Sanitize(input);
Assert.Equal("<style>:root { }</style>", sanitized);
}
}

0 comments on commit 83d115c

Please sign in to comment.