From ae0e4a13cb4b3a7e6fed21173b669d8c87acb5bf Mon Sep 17 00:00:00 2001 From: AlekseyMartynov Date: Mon, 20 Apr 2020 20:12:07 +0300 Subject: [PATCH 1/3] HtmlEditorExtender.Decode: fix removal of insecure HTML --- .../HtmlEditorExtenderTests.cs | 36 ++++++++++++- .../HtmlEditorExtender/HtmlEditorExtender.cs | 51 ++++++++++++------- 2 files changed, 69 insertions(+), 18 deletions(-) diff --git a/AjaxControlToolkit.Tests/HtmlEditorExtenderTests.cs b/AjaxControlToolkit.Tests/HtmlEditorExtenderTests.cs index d99ca8ddd..98f20fe68 100644 --- a/AjaxControlToolkit.Tests/HtmlEditorExtenderTests.cs +++ b/AjaxControlToolkit.Tests/HtmlEditorExtenderTests.cs @@ -61,7 +61,7 @@ public void StripScriptTagWithoutAttributes() { var text = @" Z")); + + // was "Hello world!alert()" - see https://github.com/DevExpress/AjaxControlToolkit/issues/525 + Assert.AreEqual( + "Hello world! ", + html.Decode("Hello world! ") + ); + + // was "" - see https://github.com/DevExpress/AjaxControlToolkit/issues/513 + var issue513html = ""; + Assert.AreEqual(issue513html, html.Decode(issue513html)); + + // was "

" + Assert.AreEqual( + "

Click me

", + html.Decode("

Click me

") + ); + + var comment = ""; + Assert.AreEqual(comment, html.Decode(comment)); + } + } } } diff --git a/AjaxControlToolkit/HtmlEditorExtender/HtmlEditorExtender.cs b/AjaxControlToolkit/HtmlEditorExtender/HtmlEditorExtender.cs index ec3e2ff63..a7850f9be 100644 --- a/AjaxControlToolkit/HtmlEditorExtender/HtmlEditorExtender.cs +++ b/AjaxControlToolkit/HtmlEditorExtender/HtmlEditorExtender.cs @@ -173,23 +173,6 @@ public string Decode(string value) { result = Regex.Replace(result, "&", "&", RegexOptions.IgnoreCase); result = Regex.Replace(result, " ", "\xA0", RegexOptions.IgnoreCase); - result = Regex.Replace(result, "[^<]<[^>]*expression[^>]*>", "", RegexOptions.IgnoreCase | RegexOptions.ECMAScript); - - result = Regex.Replace(result, "[^<]<([^>]*)(data\\:[^>]*)>", m => { - var tagGroup = m.Groups[1].Value.ToLower(); - var urlGroup = m.Groups[2].Value.ToLower(); - - if(tagGroup.StartsWith("img") && urlGroup.StartsWith("data:image/")) - return m.Value; - - return ""; - }, RegexOptions.IgnoreCase | RegexOptions.ECMAScript); - - result = Regex.Replace(result, "[^<]<[^>]*script(?!\\w)[^>]*>", "", RegexOptions.IgnoreCase | RegexOptions.ECMAScript); - result = Regex.Replace(result, "[^<]<[^>]*filter[^>]*>", "", RegexOptions.IgnoreCase | RegexOptions.ECMAScript); - result = Regex.Replace(result, "[^<]<[^>]*behavior[^>]*>", "", RegexOptions.IgnoreCase | RegexOptions.ECMAScript); - result = Regex.Replace(result, "[^<]<[^>]*javascript\\:[^>]*>", "", RegexOptions.IgnoreCase | RegexOptions.ECMAScript); - result = Regex.Replace(result, "[^<]<[^>]*position\\:[^>]*>", "", RegexOptions.IgnoreCase | RegexOptions.ECMAScript); // Check Whether EnableSanitization is disabled or not. if(EnableSanitization && Sanitizer != null) { @@ -205,6 +188,8 @@ public string Decode(string value) { elementWhiteList.Add("img", new[] { "src" }); result = Sanitizer.GetSafeHtmlFragment(result, elementWhiteList); + } else { + result = RemoveInsecureHtml(result); } // HtmlAgilityPack vanishes self-closing
tag, so replace it after sanitization @@ -213,6 +198,38 @@ public string Decode(string value) { return result; } + static string RemoveInsecureHtml(string html) { + var reFlags = RegexOptions.IgnoreCase | RegexOptions.Singleline; + + html = Regex.Replace(html, @"(?]*>.*?(]*>|$)", "", reFlags); + + html = Regex.Replace(html, @"(?]+)([^>]*)>", m => { + var tag = m.Groups[1].Value; + + if(tag.StartsWith("!--")) + return m.Value; + + var attrs = m.Groups[2].Value; + var dropAttrs = false; + + // Non-image data URLs + dropAttrs = dropAttrs || Regex.IsMatch(attrs, @"\bdata:(?!image/)", reFlags); + + // Insecure/harmful CSS props + dropAttrs = dropAttrs || Regex.IsMatch(attrs, @"\b(expression|filter|behavior|position)\s*:", reFlags); + + // JavaScript URLs + dropAttrs = dropAttrs || Regex.IsMatch(attrs, @"\bjavascript:", reFlags); + + if(dropAttrs) + return "<" + tag + ">"; + + return m.Value; + }, reFlags); + + return html; + } + // On Init add popup div and ajaxfileupload control to support Add image protected override void OnInit(EventArgs e) { base.OnInit(e); From 5572a81b2268e972a5029399cb973f50512de668 Mon Sep 17 00:00:00 2001 From: AlekseyMartynov Date: Tue, 21 Apr 2020 14:40:26 +0300 Subject: [PATCH 2/3] Test CSS props --- AjaxControlToolkit.Tests/HtmlEditorExtenderTests.cs | 10 ++++++++++ .../HtmlEditorExtender/HtmlEditorExtender.cs | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/AjaxControlToolkit.Tests/HtmlEditorExtenderTests.cs b/AjaxControlToolkit.Tests/HtmlEditorExtenderTests.cs index 98f20fe68..c83db08d8 100644 --- a/AjaxControlToolkit.Tests/HtmlEditorExtenderTests.cs +++ b/AjaxControlToolkit.Tests/HtmlEditorExtenderTests.cs @@ -110,6 +110,16 @@ public void RemoveInsecureHtml() { var comment = ""; Assert.AreEqual(comment, html.Decode(comment)); + + Assert.AreEqual( + "

", + html.Decode( + "

" + + "

" + + "

" + + "