Browse Source

Cms-Kit: PreventXSS option for markdown renderer

pull/10792/head
Yunus Emre Kalkan 5 years ago
parent
commit
e618ec1b4c
  1. 4
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs
  2. 2
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Renderers/IMarkdownToHtmlRenderer.cs
  3. 116
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Renderers/MarkdownToHtmlRenderer.cs
  4. 1
      modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.csproj

4
modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs

@ -65,12 +65,12 @@ public class CommentingViewComponent : AbpViewComponent
foreach (var comment in viewModel.Comments)
{
viewModel.RawCommentTexts.Add(comment.Id, comment.Text);
comment.Text = await MarkdownToHtmlRenderer.RenderAsync(comment.Text);
comment.Text = await MarkdownToHtmlRenderer.RenderAsync(comment.Text, true);
foreach (var reply in comment.Replies)
{
viewModel.RawCommentTexts.Add(reply.Id, reply.Text);
reply.Text = await MarkdownToHtmlRenderer.RenderAsync(reply.Text);
reply.Text = await MarkdownToHtmlRenderer.RenderAsync(reply.Text, true);
}
}
}

2
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<string> RenderAsync(string rawMarkdown);
Task<string> RenderAsync(string rawMarkdown, bool preventXSS = true);
}

116
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<string> RenderAsync(string rawMarkdown)
public async Task<string> 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<CodeBlockIndexPair> 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<CodeBlockIndexPair>();
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;
}
/// <summary>
/// Encodes html tags.
/// </summary>
private static string EncodeHtmlTags(string text, bool dontEncodeCodeBlocks = true)
{
List<CodeBlockIndexPair> 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;
}
}
}

1
modules/cms-kit/src/Volo.CmsKit.Public.Web/Volo.CmsKit.Public.Web.csproj

@ -17,6 +17,7 @@
<ProjectReference Include="..\Volo.CmsKit.Public.Application.Contracts\Volo.CmsKit.Public.Application.Contracts.csproj" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(MicrosoftPackageVersion)" />
<PackageReference Include="Markdig.Signed" Version="0.26.0" />
<PackageReference Include="HtmlSanitizer" Version="5.0.331" />
</ItemGroup>
<ItemGroup>

Loading…
Cancel
Save