Skip to content

Commit

Permalink
Merge pull request #556 from leniency/master
Browse files Browse the repository at this point in the history
Added option to allow custom CSS properties (variables).
  • Loading branch information
mganss authored Jul 26, 2024
2 parents f71dbb5 + 28c725c commit 27e4297
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 2 deletions.
22 changes: 21 additions & 1 deletion src/HtmlSanitizer/HtmlSanitizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ public HtmlSanitizer(HtmlSanitizerOptions options)
AllowedClasses = new HashSet<string>(options.AllowedCssClasses, StringComparer.OrdinalIgnoreCase);
AllowedCssProperties = new HashSet<string>(options.AllowedCssProperties, StringComparer.OrdinalIgnoreCase);
AllowedAtRules = new HashSet<CssRuleType>(options.AllowedAtRules);
AllowCssCustomProperties = options.AllowCssCustomProperties;
AllowDataAttributes = options.AllowDataAttributes;
}

/// <summary>
Expand Down Expand Up @@ -204,6 +206,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 +733,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 +756,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
10 changes: 10 additions & 0 deletions src/HtmlSanitizer/HtmlSanitizerOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,15 @@ public class HtmlSanitizerOptions
/// Gets or sets the HTML attributes that can contain a URI such as "href".
/// </summary>
public ISet<string> UriAttributes { get; set; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

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

/// <summary>
/// Allow all HTML5 data attributes; the attributes prefixed with <c>data-</c>.
/// </summary>
public bool AllowDataAttributes { get; set; }
}
}
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 27e4297

Please sign in to comment.