diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-11-17 14:33:21 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-11-17 14:33:21 +0300 |
commit | 7021455bd1ed7b125c55eb1b33c5a01f2bc55ee0 (patch) | |
tree | 5bdc2229f5198d516781f8d24eace62fc7e589e9 /lib/banzai | |
parent | 185b095e93520f96e9cfc31d9c3e69b498cdab7c (diff) |
Add latest changes from gitlab-org/gitlab@15-6-stable-eev15.6.0-rc42
Diffstat (limited to 'lib/banzai')
-rw-r--r-- | lib/banzai/filter/external_link_filter.rb | 12 | ||||
-rw-r--r-- | lib/banzai/filter/footnote_filter.rb | 28 | ||||
-rw-r--r-- | lib/banzai/filter/kroki_filter.rb | 21 | ||||
-rw-r--r-- | lib/banzai/filter/math_filter.rb | 36 | ||||
-rw-r--r-- | lib/banzai/filter/plantuml_filter.rb | 10 | ||||
-rw-r--r-- | lib/banzai/filter/repository_link_filter.rb | 1 | ||||
-rw-r--r-- | lib/banzai/filter/syntax_highlight_filter.rb | 50 | ||||
-rw-r--r-- | lib/banzai/filter/table_of_contents_filter.rb | 16 | ||||
-rw-r--r-- | lib/banzai/pipeline/ascii_doc_pipeline.rb | 2 | ||||
-rw-r--r-- | lib/banzai/pipeline/gfm_pipeline.rb | 4 | ||||
-rw-r--r-- | lib/banzai/reference_parser/base_parser.rb | 10 | ||||
-rw-r--r-- | lib/banzai/reference_parser/commit_parser.rb | 7 | ||||
-rw-r--r-- | lib/banzai/reference_parser/commit_range_parser.rb | 7 |
13 files changed, 117 insertions, 87 deletions
diff --git a/lib/banzai/filter/external_link_filter.rb b/lib/banzai/filter/external_link_filter.rb index d1a0f8e5859..0a76c84efe5 100644 --- a/lib/banzai/filter/external_link_filter.rb +++ b/lib/banzai/filter/external_link_filter.rb @@ -22,12 +22,12 @@ module Banzai addressable_uri = nil end - unless internal_url?(addressable_uri) - punycode_autolink_node!(addressable_uri, node) - sanitize_link_text!(node) - add_malicious_tooltip!(addressable_uri, node) - add_nofollow!(addressable_uri, node) - end + next if internal_url?(addressable_uri) + + punycode_autolink_node!(addressable_uri, node) + sanitize_link_text!(node) + add_malicious_tooltip!(addressable_uri, node) + add_nofollow!(addressable_uri, node) end doc diff --git a/lib/banzai/filter/footnote_filter.rb b/lib/banzai/filter/footnote_filter.rb index f5c4b788ad8..f10efdccdf1 100644 --- a/lib/banzai/filter/footnote_filter.rb +++ b/lib/banzai/filter/footnote_filter.rb @@ -44,25 +44,25 @@ module Banzai node_xpath = Gitlab::Utils::Nokogiri.css_to_xpath(css) footnote_node = doc.at_xpath(node_xpath) - if footnote_node || modified_footnotes[ref_num] - link_node[:href] += rand_suffix - link_node[:id] += rand_suffix + next unless footnote_node || modified_footnotes[ref_num] - # Sanitization stripped off class - add it back in - link_node.parent.append_class('footnote-ref') + link_node[:href] += rand_suffix + link_node[:id] += rand_suffix - unless modified_footnotes[ref_num] - footnote_node[:id] += rand_suffix - backref_node = footnote_node.at_css("a[href=\"##{fnref_id(ref_num)}\"]") + # Sanitization stripped off class - add it back in + link_node.parent.append_class('footnote-ref') - if backref_node - backref_node[:href] += rand_suffix - backref_node.append_class('footnote-backref') - end + next if modified_footnotes[ref_num] - modified_footnotes[ref_num] = true - end + footnote_node[:id] += rand_suffix + backref_node = footnote_node.at_css("a[href=\"##{fnref_id(ref_num)}\"]") + + if backref_node + backref_node[:href] += rand_suffix + backref_node.append_class('footnote-backref') end + + modified_footnotes[ref_num] = true end doc diff --git a/lib/banzai/filter/kroki_filter.rb b/lib/banzai/filter/kroki_filter.rb index 713ff2439fc..26f42c6b194 100644 --- a/lib/banzai/filter/kroki_filter.rb +++ b/lib/banzai/filter/kroki_filter.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true -require "nokogiri" -require "asciidoctor/extensions/asciidoctor_kroki/extension" +require 'nokogiri' +require 'asciidoctor/extensions/asciidoctor_kroki/version' +require 'asciidoctor/extensions/asciidoctor_kroki/extension' module Banzai module Filter @@ -31,16 +32,16 @@ module Banzai img_tag = Nokogiri::HTML::DocumentFragment.parse(%(<img src="#{image_src}" />)) img_tag = img_tag.children.first - unless img_tag.nil? - lazy_load = diagram_src.length > MAX_CHARACTER_LIMIT - img_tag.set_attribute('hidden', '') if lazy_load - img_tag.set_attribute('class', 'js-render-kroki') + next if img_tag.nil? - img_tag.set_attribute('data-diagram', diagram_type) - img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(diagram_src)}") + lazy_load = diagram_src.length > MAX_CHARACTER_LIMIT + img_tag.set_attribute('hidden', '') if lazy_load + img_tag.set_attribute('class', 'js-render-kroki') - node.parent.replace(img_tag) - end + img_tag.set_attribute('data-diagram', diagram_type) + img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(diagram_src)}") + + node.parent.replace(img_tag) end doc diff --git a/lib/banzai/filter/math_filter.rb b/lib/banzai/filter/math_filter.rb index 1ca4b2c89db..1d854d6599b 100644 --- a/lib/banzai/filter/math_filter.rb +++ b/lib/banzai/filter/math_filter.rb @@ -10,7 +10,7 @@ module Banzai # HTML filter that implements our math syntax, adding class="code math" # class MathFilter < HTML::Pipeline::Filter - CSS_MATH = 'pre.code.language-math' + CSS_MATH = 'pre[lang="math"] > code' XPATH_MATH = Gitlab::Utils::Nokogiri.css_to_xpath(CSS_MATH).freeze CSS_CODE = 'code' XPATH_CODE = Gitlab::Utils::Nokogiri.css_to_xpath(CSS_CODE).freeze @@ -32,7 +32,7 @@ module Banzai # Corresponds to the $$\n...\n$$ syntax DOLLAR_DISPLAY_BLOCK_PATTERN = %r{ ^(?<matched>\$\$\ *\n(?<math>.*)\n\$\$\ *)$ - }x.freeze + }mx.freeze # Order dependent. Handle the `$$` syntax before the `$` syntax DOLLAR_MATH_PIPELINE = [ @@ -107,27 +107,27 @@ module Banzai # We need a sibling before and after. # They should end and start with $ respectively. - if closing && opening && - closing.text? && opening.text? && - closing.content.first == DOLLAR_SIGN && - opening.content.last == DOLLAR_SIGN - - code[:class] = MATH_CLASSES - code[STYLE_ATTRIBUTE] = 'inline' - closing.content = closing.content[1..] - opening.content = opening.content[0..-2] - - @nodes_count += 1 - break if @nodes_count >= RENDER_NODES_LIMIT - end + next unless closing && opening && + closing.text? && opening.text? && + closing.content.first == DOLLAR_SIGN && + opening.content.last == DOLLAR_SIGN + + code[:class] = MATH_CLASSES + code[STYLE_ATTRIBUTE] = 'inline' + closing.content = closing.content[1..] + opening.content = opening.content[0..-2] + + @nodes_count += 1 + break if @nodes_count >= RENDER_NODES_LIMIT end end # corresponds to the "```math...```" syntax def process_math_codeblock - doc.xpath(XPATH_MATH).each do |el| - el[STYLE_ATTRIBUTE] = 'display' - el[:class] += " #{TAG_CLASS}" + doc.xpath(XPATH_MATH).each do |node| + pre_node = node.parent + pre_node[STYLE_ATTRIBUTE] = 'display' + pre_node[:class] = TAG_CLASS end end diff --git a/lib/banzai/filter/plantuml_filter.rb b/lib/banzai/filter/plantuml_filter.rb index 82f6247cf03..6a1fa64fb76 100644 --- a/lib/banzai/filter/plantuml_filter.rb +++ b/lib/banzai/filter/plantuml_filter.rb @@ -17,12 +17,12 @@ module Banzai img_tag = Nokogiri::HTML::DocumentFragment.parse( Asciidoctor::PlantUml::Processor.plantuml_content(node.content, {})).css('img').first - unless img_tag.nil? - img_tag.set_attribute('data-diagram', 'plantuml') - img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(node.content)}") + next if img_tag.nil? - node.parent.replace(img_tag) - end + img_tag.set_attribute('data-diagram', 'plantuml') + img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(node.content)}") + + node.parent.replace(img_tag) end doc diff --git a/lib/banzai/filter/repository_link_filter.rb b/lib/banzai/filter/repository_link_filter.rb index f5cf1833304..e95da735647 100644 --- a/lib/banzai/filter/repository_link_filter.rb +++ b/lib/banzai/filter/repository_link_filter.rb @@ -101,6 +101,7 @@ module Banzai if uri.relative? && uri.path.present? html_attr.value = rebuild_relative_uri(uri).to_s + html_attr.parent.add_class('gfm') end rescue URI::Error, Addressable::URI::InvalidURIError # noop diff --git a/lib/banzai/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb index 7175e99f1c7..766715d9e39 100644 --- a/lib/banzai/filter/syntax_highlight_filter.rb +++ b/lib/banzai/filter/syntax_highlight_filter.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true require 'rouge/plugins/common_mark' -require "asciidoctor/extensions/asciidoctor_kroki/extension" +require 'asciidoctor/extensions/asciidoctor_kroki/version' +require 'asciidoctor/extensions/asciidoctor_kroki/extension' # Generated HTML is transformed back to GFM by app/assets/javascripts/behaviors/markdown/nodes/code_block.js module Banzai @@ -13,8 +14,9 @@ module Banzai LANG_PARAMS_DELIMITER = ':' LANG_PARAMS_ATTR = 'data-lang-params' + CSS_CLASSES = 'code highlight js-syntax-highlight' - CSS = 'pre:not([data-math-style]):not([data-mermaid-style]):not([data-kroki-style]) > code:only-child' + CSS = 'pre:not([data-kroki-style]) > code:only-child' XPATH = Gitlab::Utils::Nokogiri.css_to_xpath(CSS).freeze def call @@ -26,9 +28,9 @@ module Banzai end def highlight_node(node) - css_classes = +'code highlight js-syntax-highlight' + return if node.parent&.parent.nil? + lang, lang_params = parse_lang_params(node) - sourcepos = node.parent.attr('data-sourcepos') retried = false if use_rouge?(lang) @@ -41,7 +43,6 @@ module Banzai begin code = Rouge::Formatters::HTMLGitlab.format(lex(lexer, node.text), tag: language) - css_classes << " language-#{language}" if language rescue StandardError # Gracefully handle syntax highlighter bugs/errors to ensure users can # still access an issue/comment/etc. First, retry with the plain text @@ -56,16 +57,26 @@ module Banzai retry end - sourcepos_attr = sourcepos ? "data-sourcepos=\"#{escape_once(sourcepos)}\"" : '' + # maintain existing attributes already added. e.g math and mermaid nodes + node.children = code + pre_node = node.parent + + # ensure there are no extra children, such as a text node that might + # show up from an XSS attack + pre_node.children = node + + pre_node[:lang] = language + pre_node.add_class(CSS_CLASSES) + pre_node.add_class("language-#{language}") if language + pre_node.set_attribute('data-canonical-lang', escape_once(lang)) if lang != language + pre_node.set_attribute(LANG_PARAMS_ATTR, escape_once(lang_params)) if lang_params.present? + pre_node.set_attribute('v-pre', 'true') + pre_node.remove_attribute('data-meta') - highlighted = %(<div class="gl-relative markdown-code-block js-markdown-code"><pre #{sourcepos_attr} class="#{css_classes}" - lang="#{language}" - #{lang != language ? "data-canonical-lang=\"#{escape_once(lang)}\"" : ""} - #{lang_params} - v-pre="true"><code>#{code}</code></pre><copy-code></copy-code></div>) + highlighted = %(<div class="gl-relative markdown-code-block js-markdown-code">#{pre_node.to_html}<copy-code></copy-code></div>) # Extracted to a method to measure it - replace_parent_pre_element(node, highlighted) + replace_pre_element(pre_node, highlighted) end private @@ -93,9 +104,8 @@ module Banzai language, language_params = language.split(LANG_PARAMS_DELIMITER, 2) language_params = [node.attr('data-meta'), language_params].compact.join(' ') - formatted_language_params = format_language_params(language_params) - [language, formatted_language_params] + [language, language_params] end # Separate method so it can be instrumented. @@ -107,20 +117,14 @@ module Banzai (Rouge::Lexer.find(language) || Rouge::Lexers::PlainText).new end - # Replace the parent `pre` element with the entire highlighted block - def replace_parent_pre_element(node, highlighted) - node.parent.replace(highlighted) + # Replace the `pre` element with the entire highlighted block + def replace_pre_element(pre_node, highlighted) + pre_node.replace(highlighted) end def use_rouge?(language) (%w(math suggestion) + ::AsciidoctorExtensions::Kroki::SUPPORTED_DIAGRAM_NAMES).exclude?(language) end - - def format_language_params(language_params) - return if language_params.blank? - - %(#{LANG_PARAMS_ATTR}="#{escape_once(language_params)}") - end end end end diff --git a/lib/banzai/filter/table_of_contents_filter.rb b/lib/banzai/filter/table_of_contents_filter.rb index 1c794a81d9d..d76009d08e1 100644 --- a/lib/banzai/filter/table_of_contents_filter.rb +++ b/lib/banzai/filter/table_of_contents_filter.rb @@ -33,17 +33,17 @@ module Banzai header_root = current_header = HeaderNode.new doc.xpath(XPATH).each do |node| - if header_content = node.children.first - id = string_to_anchor(node.text[0...255]) + next unless header_content = node.children.first - uniq = headers[id] > 0 ? "-#{headers[id]}" : '' - headers[id] += 1 - href = "#{id}#{uniq}" + id = string_to_anchor(node.text[0...255]) - current_header = HeaderNode.new(node: node, href: href, previous_header: current_header) + uniq = headers[id] > 0 ? "-#{headers[id]}" : '' + headers[id] += 1 + href = "#{id}#{uniq}" - header_content.add_previous_sibling(anchor_tag(href)) - end + current_header = HeaderNode.new(node: node, href: href, previous_header: current_header) + + header_content.add_previous_sibling(anchor_tag(href)) end push_toc(header_root.children, root: true) diff --git a/lib/banzai/pipeline/ascii_doc_pipeline.rb b/lib/banzai/pipeline/ascii_doc_pipeline.rb index b652d8d89cf..afd5802de22 100644 --- a/lib/banzai/pipeline/ascii_doc_pipeline.rb +++ b/lib/banzai/pipeline/ascii_doc_pipeline.rb @@ -7,13 +7,13 @@ module Banzai FilterArray[ Filter::AsciiDocSanitizationFilter, Filter::AssetProxyFilter, - Filter::SyntaxHighlightFilter, Filter::ExternalLinkFilter, Filter::PlantumlFilter, Filter::ColorFilter, Filter::ImageLazyLoadFilter, Filter::ImageLinkFilter, Filter::WikiLinkFilter, + Filter::SyntaxHighlightFilter, Filter::AsciiDocPostProcessingFilter ] end diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb index 5e7c2f64c92..9b73e413d44 100644 --- a/lib/banzai/pipeline/gfm_pipeline.rb +++ b/lib/banzai/pipeline/gfm_pipeline.rb @@ -17,7 +17,6 @@ module Banzai Filter::SanitizationFilter, Filter::KrokiFilter, Filter::AssetProxyFilter, - Filter::SyntaxHighlightFilter, Filter::MathFilter, Filter::ColorFilter, Filter::MermaidFilter, @@ -37,7 +36,8 @@ module Banzai Filter::CustomEmojiFilter, Filter::TaskListFilter, Filter::InlineDiffFilter, - Filter::SetDirectionFilter + Filter::SetDirectionFilter, + Filter::SyntaxHighlightFilter ] end diff --git a/lib/banzai/reference_parser/base_parser.rb b/lib/banzai/reference_parser/base_parser.rb index 831baa9a778..19d91876892 100644 --- a/lib/banzai/reference_parser/base_parser.rb +++ b/lib/banzai/reference_parser/base_parser.rb @@ -66,6 +66,8 @@ module Banzai projects = lazy { projects_for_nodes(nodes) } project_attr = 'data-project' + preload_associations(projects, user) + nodes.select do |node| if node.has_attribute?(project_attr) can_read_reference?(user, projects[node], node) @@ -261,6 +263,14 @@ module Banzai hash[key] = {} end end + + # For any preloading of project associations + # needed to avoid N+1s. + # Note: `projects` param is a hash of { node => project }. + # See #projects_for_nodes for more information. + def preload_associations(projects, user) + ::Preloaders::ProjectPolicyPreloader.new(projects.values, user).execute + end end end end diff --git a/lib/banzai/reference_parser/commit_parser.rb b/lib/banzai/reference_parser/commit_parser.rb index 88896970bc6..c51f4976c28 100644 --- a/lib/banzai/reference_parser/commit_parser.rb +++ b/lib/banzai/reference_parser/commit_parser.rb @@ -32,6 +32,13 @@ module Banzai commits end + def nodes_visible_to_user(user, nodes) + projects = lazy { projects_for_nodes(nodes) } + user.preloaded_member_roles_for_projects(projects.values) if user + + super + end + private def can_read_reference?(user, ref_project, node) diff --git a/lib/banzai/reference_parser/commit_range_parser.rb b/lib/banzai/reference_parser/commit_range_parser.rb index fb4a392105f..3d09bc83151 100644 --- a/lib/banzai/reference_parser/commit_range_parser.rb +++ b/lib/banzai/reference_parser/commit_range_parser.rb @@ -38,6 +38,13 @@ module Banzai range.valid_commits? ? range : nil end + def nodes_visible_to_user(user, nodes) + projects = lazy { projects_for_nodes(nodes) } + user.preloaded_member_roles_for_projects(projects.values) if user + + super + end + private def can_read_reference?(user, ref_project, node) |