class Dependabot::PullRequestCreator::MessageBuilder::LinkAndMentionSanitizer
Constants
- COMMONMARKER_EXTENSIONS
- COMMONMARKER_OPTIONS
- EOS_REGEX
End of string
- GITHUB_REF_REGEX
- GITHUB_USERNAME
- MENTION_REGEX
- TEAM_MENTION_REGEX
regex to match a team mention on github
Attributes
github_redirection_service[R]
Public Class Methods
new(github_redirection_service:)
click to toggle source
# File lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb, line 31 def initialize(github_redirection_service:) @github_redirection_service = github_redirection_service end
Public Instance Methods
sanitize_links_and_mentions(text:, unsafe: false)
click to toggle source
# File lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb, line 35 def sanitize_links_and_mentions(text:, unsafe: false) doc = CommonMarker.render_doc( text, :LIBERAL_HTML_TAG, COMMONMARKER_EXTENSIONS ) sanitize_team_mentions(doc) sanitize_mentions(doc) sanitize_links(doc) mode = unsafe ? :UNSAFE : :DEFAULT doc.to_html(([mode] + COMMONMARKER_OPTIONS), COMMONMARKER_EXTENSIONS) end
Private Instance Methods
build_mention_link_text_nodes(text)
click to toggle source
# File lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb, line 168 def build_mention_link_text_nodes(text) code_node = CommonMarker::Node.new(:code) code_node.string_content = insert_zero_width_space_in_mention(text) [code_node] end
build_mention_nodes(text)
click to toggle source
# File lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb, line 118 def build_mention_nodes(text) nodes = [] scan = StringScanner.new(text) until scan.eos? line = scan.scan_until(MENTION_REGEX) || scan.scan_until(EOS_REGEX) line_match = line.match(MENTION_REGEX) mention = line_match&.to_s text_node = CommonMarker::Node.new(:text) if mention && !mention.end_with?("/") text_node.string_content = line_match.pre_match nodes << text_node nodes << create_link_node( "https://github.com/#{mention.tr('@', '')}", mention.to_s ) else text_node.string_content = line nodes << text_node end end nodes end
build_team_mention_nodes(text)
click to toggle source
# File lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb, line 144 def build_team_mention_nodes(text) nodes = [] scan = StringScanner.new(text) until scan.eos? line = scan.scan_until(TEAM_MENTION_REGEX) || scan.scan_until(EOS_REGEX) line_match = line.match(TEAM_MENTION_REGEX) mention = line_match&.to_s text_node = CommonMarker::Node.new(:text) if mention text_node.string_content = line_match.pre_match nodes << text_node nodes += build_mention_link_text_nodes(mention.to_s) else text_node.string_content = line nodes << text_node end end nodes end
create_link_node(url, text)
click to toggle source
# File lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb, line 174 def create_link_node(url, text) link_node = CommonMarker::Node.new(:link) code_node = CommonMarker::Node.new(:code) link_node.url = url code_node.string_content = insert_zero_width_space_in_mention(text) link_node.append_child(code_node) link_node end
insert_zero_width_space_in_mention(mention)
click to toggle source
NOTE: Add a zero-width space between the @ and the username to prevent email replies on dependabot pull requests triggering notifications to users who've been mentioned in changelogs etc. PR email replies parse the content of the pull request body in plain text.
# File lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb, line 187 def insert_zero_width_space_in_mention(mention) mention.sub("@", "@\u200B").encode("utf-8") end
parent_node_link?(node)
click to toggle source
# File lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb, line 191 def parent_node_link?(node) node.type == :link || node.parent && parent_node_link?(node.parent) end
replace_github_host(text)
click to toggle source
# File lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb, line 112 def replace_github_host(text) text.gsub( /(www\.)?github.com/, github_redirection_service || "github.com" ) end
sanitize_links(doc)
click to toggle source
# File lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb, line 89 def sanitize_links(doc) doc.walk do |node| if node.type == :link && node.url.match?(GITHUB_REF_REGEX) node.each do |subnode| unless subnode.type == :text && subnode.string_content.match?(GITHUB_REF_REGEX) next end last_match = subnode.string_content.match(GITHUB_REF_REGEX) number = last_match.named_captures.fetch("number") repo = last_match.named_captures.fetch("repo") subnode.string_content = "#{repo}##{number}" end node.url = replace_github_host(node.url) elsif node.type == :text && node.string_content.match?(GITHUB_REF_REGEX) node.string_content = replace_github_host(node.string_content) end end end
sanitize_mentions(doc)
click to toggle source
# File lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb, line 50 def sanitize_mentions(doc) doc.walk do |node| if node.type == :text && node.string_content.match?(MENTION_REGEX) nodes = if parent_node_link?(node) build_mention_link_text_nodes(node.string_content) else build_mention_nodes(node.string_content) end nodes.each do |n| node.insert_before(n) end node.delete end end end
sanitize_team_mentions(doc)
click to toggle source
When we come across something that looks like a team mention (e.g. @dependabot/reviewers), we replace it with a text node. This is because there are ecosystems that have packages that follow the same pattern (e.g. @angular/angular-cli), and we don't want to create an invalid link, since team mentions link to `github.com/org/:organization_name/teams/:team_name`.
# File lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb, line 74 def sanitize_team_mentions(doc) doc.walk do |node| if node.type == :text && node.string_content.match?(TEAM_MENTION_REGEX) nodes = build_team_mention_nodes(node.string_content) nodes.each do |n| node.insert_before(n) end node.delete end end end