private RenderedMarkdownResult GetHtmlFromMarkdownCommonMark(string markdownString, int incrementHeadersBy) { var output = new RenderedMarkdownResult() { ImagesRewritten = false, Content = "", ImageSourceDisallowed = false }; var readmeWithoutBom = markdownString.StartsWith("\ufeff") ? markdownString.Replace("\ufeff", "") : markdownString; // HTML encode markdown, except for block quotes, to block inline html. var encodedMarkdown = EncodedBlockQuotePattern.Replace(HttpUtility.HtmlEncode(readmeWithoutBom), "> "); var settings = CommonMarkSettings.Default.Clone(); settings.RenderSoftLineBreaksAsLineBreaks = true; // Parse executes CommonMarkConverter's ProcessStage1 and ProcessStage2. var document = CommonMarkConverter.Parse(encodedMarkdown, settings); foreach (var node in document.AsEnumerable()) { if (node.IsOpening) { var block = node.Block; if (block != null) { switch (block.Tag) { // Demote heading tags so they don't overpower expander headings. case BlockTag.AtxHeading: case BlockTag.SetextHeading: var level = (byte)Math.Min(block.Heading.Level + incrementHeadersBy, 6); block.Heading = new HeadingData(level); break; // Decode preformatted blocks to prevent double encoding. // Skip BlockTag.BlockQuote, which are partially decoded upfront. case BlockTag.FencedCode: case BlockTag.IndentedCode: if (block.StringContent != null) { var content = block.StringContent.TakeFromStart(block.StringContent.Length); var unencodedContent = HttpUtility.HtmlDecode(content); block.StringContent.Replace(unencodedContent, 0, unencodedContent.Length); } break; } } var inline = node.Inline; if (inline != null) { if (inline.Tag == InlineTag.Link) { // Allow only http or https links in markdown. Transform link to https for known domains. if (!PackageHelper.TryPrepareUrlForRendering(inline.TargetUrl, out string readyUriString)) { inline.TargetUrl = string.Empty; } else { inline.TargetUrl = readyUriString; } } else if (inline.Tag == InlineTag.Image) { if (_features.IsImageAllowlistEnabled()) { if (!_imageDomainValidator.TryPrepareImageUrlForRendering(inline.TargetUrl, out string readyUriString)) { inline.TargetUrl = string.Empty; output.ImageSourceDisallowed = true; } else { output.ImagesRewritten = output.ImagesRewritten || (inline.TargetUrl != readyUriString); inline.TargetUrl = readyUriString; } } else { if (!PackageHelper.TryPrepareUrlForRendering(inline.TargetUrl, out string readyUriString, rewriteAllHttp: true)) { inline.TargetUrl = string.Empty; } else { output.ImagesRewritten = output.ImagesRewritten || (inline.TargetUrl != readyUriString); inline.TargetUrl = readyUriString; } } } } }