Example #1
0
        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;
                                }
                            }
                        }
                    }
                }