RawCommentTexts { get; set; }
}
}
+
diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml
index ab4bd69b91..9d0438098d 100644
--- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml
+++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml
@@ -49,6 +49,9 @@
}
+
+ @L["MarkdownSupported"]
+
;
@@ -57,16 +60,7 @@
Func GetCommentContentArea(Guid id, string text) =>
@;
}
@@ -114,9 +108,11 @@
+
+ @L["MarkdownSupported"]
+
@@ -161,7 +157,7 @@
- @GetEditArea(comment.Id, comment.Text, comment.ConcurrencyStamp).Invoke(null)
+ @GetEditArea(comment.Id, Model.RawCommentTexts[comment.Id], comment.ConcurrencyStamp).Invoke(null)
@if (comment.Replies.Any())
{
@@ -191,7 +187,7 @@
- @GetEditArea(reply.Id, reply.Text, reply.ConcurrencyStamp).Invoke(null)
+ @GetEditArea(reply.Id, Model.RawCommentTexts[reply.Id], reply.ConcurrencyStamp).Invoke(null)
}
@@ -220,4 +216,3 @@
}
-
\ No newline at end of file
diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Renderers/IMarkdownToHtmlRenderer.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Renderers/IMarkdownToHtmlRenderer.cs
index 9f19d9f3da..d11e76f6dd 100644
--- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Renderers/IMarkdownToHtmlRenderer.cs
+++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Renderers/IMarkdownToHtmlRenderer.cs
@@ -4,5 +4,5 @@ namespace Volo.CmsKit.Public.Web.Renderers;
public interface IMarkdownToHtmlRenderer
{
- Task RenderAsync(string rawMarkdown);
+ Task RenderAsync(string rawMarkdown, bool preventXSS = true);
}
diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Renderers/MarkdownToHtmlRenderer.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Renderers/MarkdownToHtmlRenderer.cs
index 6998a3d828..b6fab3c008 100644
--- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Renderers/MarkdownToHtmlRenderer.cs
+++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Renderers/MarkdownToHtmlRenderer.cs
@@ -1,21 +1,129 @@
-using Markdig;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.RegularExpressions;
+using Markdig;
using System.Threading.Tasks;
+using System.Web;
using Volo.Abp.DependencyInjection;
+using Ganss.XSS;
namespace Volo.CmsKit.Public.Web.Renderers;
public class MarkdownToHtmlRenderer : IMarkdownToHtmlRenderer, ITransientDependency
{
+ private readonly HtmlSanitizer _htmlSanitizer;
protected MarkdownPipeline MarkdownPipeline { get; }
public MarkdownToHtmlRenderer(MarkdownPipeline markdownPipeline)
{
MarkdownPipeline = markdownPipeline;
+ _htmlSanitizer = new HtmlSanitizer();
}
- public Task RenderAsync(string rawMarkdown)
+ public async Task RenderAsync(string rawMarkdown, bool preventXSS = false)
{
- return Task.FromResult(
- Markdown.ToHtml(rawMarkdown, MarkdownPipeline));
+ if (preventXSS)
+ {
+ rawMarkdown = EncodeHtmlTags(rawMarkdown, true);
+ }
+
+ var html = Markdown.ToHtml(rawMarkdown, MarkdownPipeline);
+
+ if (preventXSS)
+ {
+ html = _htmlSanitizer.Sanitize(html);
+ }
+
+ return html;
+ }
+
+
+ private static List GetCodeBlockIndices(string markdownText)
+ {
+ var regexObj = new Regex(@"```(\w)*|`(\w)*", RegexOptions.IgnoreCase |
+ RegexOptions.IgnorePatternWhitespace |
+ RegexOptions.Singleline |
+ RegexOptions.Multiline |
+ RegexOptions.ExplicitCapture);
+
+ var matches = regexObj.Matches(markdownText);
+ var indices = new List();
+
+ for (var i = 0; i < matches.Count; i++)
+ {
+ if (!indices.Any() || indices.Last().EndIndex.HasValue)
+ {
+ indices.Add(new CodeBlockIndexPair(matches[i].Index));
+ }
+ else
+ {
+ indices.Last().EndIndex = matches[i].Index;
+ }
+ }
+
+ return indices;
+ }
+
+ ///
+ /// Encodes html tags.
+ ///
+ private static string EncodeHtmlTags(string text, bool dontEncodeCodeBlocks = true)
+ {
+ List codeBlockIndices = null;
+ if (dontEncodeCodeBlocks)
+ {
+ codeBlockIndices = GetCodeBlockIndices(text);
+ }
+
+ return Regex.Replace(text, @"<[^>]*>", match =>
+ {
+ if (dontEncodeCodeBlocks && codeBlockIndices != null)
+ {
+ var isInCodeBlock = false;
+ foreach (var codeBlock in codeBlockIndices)
+ {
+ if (IsInCodeBlock(match.Index, codeBlock.StartIndex, codeBlock.EndIndex))
+ {
+ isInCodeBlock = true;
+ break;
+ }
+ }
+
+ if (isInCodeBlock)
+ {
+ return match.ToString();
+ }
+ else
+ {
+ return HttpUtility.HtmlEncode(match.ToString());
+ }
+ }
+ else
+ {
+ return HttpUtility.HtmlEncode(match.ToString());
+ }
+ });
+ }
+
+ private static bool IsInCodeBlock(int currentIndex, int codeBlockStartIndex, int? codeBlockEndIndex)
+ {
+ if (codeBlockEndIndex.HasValue)
+ {
+ return (currentIndex >= codeBlockStartIndex && currentIndex <= codeBlockEndIndex);
+ }
+
+ return currentIndex >= codeBlockStartIndex;
+ }
+
+ private class CodeBlockIndexPair
+ {
+ public int StartIndex { get; private set; }
+ public int? EndIndex { get; set; }
+
+ public CodeBlockIndexPair(int startIndex, int? endIndex = null)
+ {
+ StartIndex = startIndex;
+ EndIndex = endIndex;
+ }
}
}
diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.csproj b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.csproj
index 5ab0e7bf44..2dc1fd425c 100644
--- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.csproj
+++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.csproj
@@ -17,6 +17,7 @@
+
- @{ - var lines = text.SplitToLines(); - if (lines.Any()) - { - foreach (var line in lines) - { - @line
- } - } - } + @Html.Raw(text)