From 83d115ca3fb2700f748a5375beade75a787c9c4a Mon Sep 17 00:00:00 2001 From: leniency Date: Thu, 25 Jul 2024 13:58:39 -0700 Subject: [PATCH 1/2] Added option to allow custom CSS properties (variables) that begin with '--' 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. --- src/HtmlSanitizer/HtmlSanitizer.cs | 20 +++++++++++++- test/HtmlSanitizer.Tests/Tests.cs | 42 +++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/HtmlSanitizer/HtmlSanitizer.cs b/src/HtmlSanitizer/HtmlSanitizer.cs index bb7443e..30690dc 100644 --- a/src/HtmlSanitizer/HtmlSanitizer.cs +++ b/src/HtmlSanitizer/HtmlSanitizer.cs @@ -204,6 +204,11 @@ public HtmlSanitizer(HtmlSanitizerOptions options) /// public ISet AllowedCssProperties { get; private set; } + /// + /// Allow all custom CSS properties (variables) prefixed with --. + /// + public bool AllowCssCustomProperties { get; set; } + /// /// Gets or sets a regex that must not match for legal CSS property values. /// @@ -726,6 +731,19 @@ protected void SanitizeStyle(IElement element, string baseUrl) SanitizeStyleDeclaration(element, styles, baseUrl); } + /// + /// Verify if the given CSS property name is allowed. By default this will + /// check if the property is in the set, + /// or if the property is a custom property and is true. + /// + /// The name of the CSS property. + /// True if the property is allowed or not. + 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>(); @@ -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(style, RemoveReason.NotAllowedStyle)); continue; diff --git a/test/HtmlSanitizer.Tests/Tests.cs b/test/HtmlSanitizer.Tests/Tests.cs index a8ad6d0..aec4a6e 100644 --- a/test/HtmlSanitizer.Tests/Tests.cs +++ b/test/HtmlSanitizer.Tests/Tests.cs @@ -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().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(() => @@ -3589,4 +3589,44 @@ public void KeepChildNodesTextTest() var sanitized = sanitizer.Sanitize(input); Assert.Equal("<img>&lt;img&gt;", sanitized); } + + [Fact] + public void AllowStyleAttributeCssCustomPropertiesTest() + { + var input = "
"; + var sanitizer = new HtmlSanitizer { AllowCssCustomProperties = true }; + sanitizer.AllowedTags.Remove("iframe"); + var sanitized = sanitizer.Sanitize(input); + Assert.Equal("
", sanitized); + } + + [Fact] + public void AllowStyleTagCssCustomPropertiesTest() + { + var input = ""; + var sanitizer = new HtmlSanitizer { AllowCssCustomProperties = true }; + sanitizer.AllowedTags.Add("style"); + var sanitized = sanitizer.Sanitize(input); + Assert.Equal("", sanitized); + } + + [Fact] + public void DisallowStyleAttributeCssCustomPropertiesTest() + { + var input = "
"; + var sanitizer = new HtmlSanitizer(); + sanitizer.AllowedTags.Remove("iframe"); + var sanitized = sanitizer.Sanitize(input); + Assert.Equal("
", sanitized); + } + + [Fact] + public void DisallowStyleTagCssCustomPropertiesTest() + { + var input = ""; + var sanitizer = new HtmlSanitizer(); + sanitizer.AllowedTags.Add("style"); + var sanitized = sanitizer.Sanitize(input); + Assert.Equal("", sanitized); + } } From 28c725cbdb69272d8a9d32893672685337c42879 Mon Sep 17 00:00:00 2001 From: leniency Date: Fri, 26 Jul 2024 07:51:49 -0700 Subject: [PATCH 2/2] Added AllowCssCustomProperties and AllowDataAttributes to the options. --- src/HtmlSanitizer/HtmlSanitizer.cs | 2 ++ src/HtmlSanitizer/HtmlSanitizerOptions.cs | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/HtmlSanitizer/HtmlSanitizer.cs b/src/HtmlSanitizer/HtmlSanitizer.cs index 30690dc..d612581 100644 --- a/src/HtmlSanitizer/HtmlSanitizer.cs +++ b/src/HtmlSanitizer/HtmlSanitizer.cs @@ -99,6 +99,8 @@ public HtmlSanitizer(HtmlSanitizerOptions options) AllowedClasses = new HashSet(options.AllowedCssClasses, StringComparer.OrdinalIgnoreCase); AllowedCssProperties = new HashSet(options.AllowedCssProperties, StringComparer.OrdinalIgnoreCase); AllowedAtRules = new HashSet(options.AllowedAtRules); + AllowCssCustomProperties = options.AllowCssCustomProperties; + AllowDataAttributes = options.AllowDataAttributes; } /// diff --git a/src/HtmlSanitizer/HtmlSanitizerOptions.cs b/src/HtmlSanitizer/HtmlSanitizerOptions.cs index 05e38e1..def25da 100644 --- a/src/HtmlSanitizer/HtmlSanitizerOptions.cs +++ b/src/HtmlSanitizer/HtmlSanitizerOptions.cs @@ -43,5 +43,15 @@ public class HtmlSanitizerOptions /// Gets or sets the HTML attributes that can contain a URI such as "href". /// public ISet UriAttributes { get; set; } = new HashSet(StringComparer.OrdinalIgnoreCase); + + /// + /// Allow all custom CSS properties (variables) prefixed with --. + /// + public bool AllowCssCustomProperties { get; set; } + + /// + /// Allow all HTML5 data attributes; the attributes prefixed with data-. + /// + public bool AllowDataAttributes { get; set; } } }