class Dependabot::Maven::FileParser

Constants

DEPENDENCY_SELECTOR

The following “dependencies” are candidates for updating:

  • The project's parent

  • Any dependencies (incl. those in dependencyManagement or plugins)

  • Any plugins (incl. those in pluginManagement)

  • Any extensions

EXTENSION_SELECTOR
PLUGIN_SELECTOR
PROPERTY_REGEX

Regex to get the property name from a declaration that uses a property

Public Instance Methods

parse() click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 33
def parse
  dependency_set = DependencySet.new
  pomfiles.each { |pom| dependency_set += pomfile_dependencies(pom) }
  extensionfiles.each { |extension| dependency_set += extensionfile_dependencies(extension) }
  dependency_set.dependencies
end

Private Instance Methods

build_dependency(pom, dependency_node, name) click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 104
def build_dependency(pom, dependency_node, name)
  property_details =
    {
      property_name: version_property_name(dependency_node),
      property_source: property_source(dependency_node, pom)
    }.compact

  Dependency.new(
    name: name,
    version: dependency_version(pom, dependency_node),
    package_manager: "maven",
    requirements: [{
      requirement: dependency_requirement(pom, dependency_node),
      file: pom.name,
      groups: dependency_groups(pom, dependency_node),
      source: nil,
      metadata: {
        packaging_type: packaging_type(pom, dependency_node)
      }.merge(property_details)
    }]
  )
end
check_required_files() click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 298
def check_required_files
  raise "No pom.xml!" unless get_original_file("pom.xml")
end
dependency_classifier(dependency_node, pom) click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 143
def dependency_classifier(dependency_node, pom)
  return unless dependency_node.at_xpath("./classifier")

  evaluated_value(
    dependency_node.at_xpath("./classifier").content.strip,
    pom
  )
end
dependency_from_dependency_node(pom, dependency_node) click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 87
def dependency_from_dependency_node(pom, dependency_node)
  return unless (name = dependency_name(dependency_node, pom))
  return if internal_dependency_names.include?(name)

  classifier = dependency_classifier(dependency_node, pom)
  name = classifier ? "#{name}:#{classifier}" : name

  build_dependency(pom, dependency_node, name)
end
dependency_from_plugin_node(pom, dependency_node) click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 97
def dependency_from_plugin_node(pom, dependency_node)
  return unless (name = plugin_name(dependency_node, pom))
  return if internal_dependency_names.include?(name)

  build_dependency(pom, dependency_node, name)
end
dependency_groups(pom, dependency_node) click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 194
def dependency_groups(pom, dependency_node)
  dependency_scope(pom, dependency_node) == "test" ? ["test"] : []
end
dependency_name(dependency_node, pom) click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 127
def dependency_name(dependency_node, pom)
  return unless dependency_node.at_xpath("./groupId")
  return unless dependency_node.at_xpath("./artifactId")

  [
    evaluated_value(
      dependency_node.at_xpath("./groupId").content.strip,
      pom
    ),
    evaluated_value(
      dependency_node.at_xpath("./artifactId").content.strip,
      pom
    )
  ].join(":")
end
dependency_requirement(pom, dependency_node) click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 185
def dependency_requirement(pom, dependency_node)
  return unless dependency_node.at_xpath("./version")

  version_content = dependency_node.at_xpath("./version").content.strip
  version_content = evaluated_value(version_content, pom)

  version_content.empty? ? nil : version_content
end
dependency_scope(pom, dependency_node) click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 198
def dependency_scope(pom, dependency_node)
  return "compile" unless dependency_node.at_xpath("./scope")

  scope_content = dependency_node.at_xpath("./scope").content.strip
  scope_content = evaluated_value(scope_content, pom)

  scope_content.empty? ? "compile" : scope_content
end
dependency_version(pom, dependency_node) click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 174
def dependency_version(pom, dependency_node)
  requirement = dependency_requirement(pom, dependency_node)
  return nil unless requirement

  # If a range is specified then we can't tell the exact version
  return nil if requirement.include?(",")

  # Remove brackets if present (and not denoting a range)
  requirement.gsub(/[\(\)\[\]]/, "").strip
end
evaluated_value(value, pom) click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 228
def evaluated_value(value, pom)
  return value unless value.match?(PROPERTY_REGEX)

  property_name = value.match(PROPERTY_REGEX).
                  named_captures.fetch("property")
  property_value = value_for_property(property_name, pom)

  new_value = value.gsub(value.match(PROPERTY_REGEX).to_s, property_value)
  evaluated_value(new_value, pom)
end
extensionfile_dependencies(extension) click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 68
def extensionfile_dependencies(extension)
  dependency_set = DependencySet.new

  errors = []
  doc = Nokogiri::XML(extension.content)
  doc.remove_namespaces!

  doc.css(EXTENSION_SELECTOR).each do |dependency_node|
    dep = dependency_from_dependency_node(extension, dependency_node)
    dependency_set << dep if dep
  rescue DependencyFileNotEvaluatable => e
    errors << e
  end

  raise errors.first if errors.any? && dependency_set.dependencies.none?

  dependency_set
end
extensionfiles() click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 279
def extensionfiles
  @extensionfiles ||=
    dependency_files.select { |f| f.name.end_with?("extensions.xml") }
end
internal_dependency_names() click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 284
def internal_dependency_names
  @internal_dependency_names ||=
    dependency_files.map do |pom|
      doc = Nokogiri::XML(pom.content)
      group_id = doc.at_css("project > groupId") ||
                 doc.at_css("project > parent > groupId")
      artifact_id = doc.at_css("project > artifactId")

      next unless group_id && artifact_id

      [group_id.content.strip, artifact_id.content.strip].join(":")
    end.compact
end
packaging_type(pom, dependency_node) click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 207
def packaging_type(pom, dependency_node)
  return "pom" if dependency_node.node_name == "parent"
  return "jar" unless dependency_node.at_xpath("./type")

  packaging_type_content = dependency_node.at_xpath("./type").
                           content.strip

  evaluated_value(packaging_type_content, pom)
end
plugin_group_id(pom, node) click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 165
def plugin_group_id(pom, node)
  return "org.apache.maven.plugins" unless node.at_xpath("./groupId")

  evaluated_value(
    node.at_xpath("./groupId").content.strip,
    pom
  )
end
plugin_name(dependency_node, pom) click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 152
def plugin_name(dependency_node, pom)
  return unless plugin_group_id(pom, dependency_node)
  return unless dependency_node.at_xpath("./artifactId")

  [
    plugin_group_id(pom, dependency_node),
    evaluated_value(
      dependency_node.at_xpath("./artifactId").content.strip,
      pom
    )
  ].join(":")
end
pomfile_dependencies(pom) click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 42
def pomfile_dependencies(pom)
  dependency_set = DependencySet.new

  errors = []
  doc = Nokogiri::XML(pom.content)
  doc.remove_namespaces!

  doc.css(DEPENDENCY_SELECTOR).each do |dependency_node|
    dep = dependency_from_dependency_node(pom, dependency_node)
    dependency_set << dep if dep
  rescue DependencyFileNotEvaluatable => e
    errors << e
  end

  doc.css(PLUGIN_SELECTOR).each do |dependency_node|
    dep = dependency_from_plugin_node(pom, dependency_node)
    dependency_set << dep if dep
  rescue DependencyFileNotEvaluatable => e
    errors << e
  end

  raise errors.first if errors.any? && dependency_set.dependencies.none?

  dependency_set
end
pomfiles() click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 273
def pomfiles
  # NOTE: this (correctly) excludes any parent POMs that were downloaded
  @pomfiles ||=
    dependency_files.select { |f| f.name.end_with?("pom.xml") }
end
property_source(dependency_node, pom) click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 239
def property_source(dependency_node, pom)
  property_name = version_property_name(dependency_node)
  return unless property_name

  declaring_pom =
    property_value_finder.
    property_details(property_name: property_name, callsite_pom: pom)&.
    fetch(:file)

  return declaring_pom if declaring_pom

  msg = "Property not found: #{property_name}"
  raise DependencyFileNotEvaluatable, msg
end
property_value_finder() click to toggle source

Cached, since this can makes calls to the registry (to get property values from parent POMs)

# File lib/dependabot/maven/file_parser.rb, line 268
def property_value_finder
  @property_value_finder ||=
    PropertyValueFinder.new(dependency_files: dependency_files)
end
value_for_property(property_name, pom) click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 254
def value_for_property(property_name, pom)
  value =
    property_value_finder.
    property_details(property_name: property_name, callsite_pom: pom)&.
    fetch(:value)

  return value if value

  msg = "Property not found: #{property_name}"
  raise DependencyFileNotEvaluatable, msg
end
version_property_name(dependency_node) click to toggle source
# File lib/dependabot/maven/file_parser.rb, line 217
def version_property_name(dependency_node)
  return unless dependency_node.at_xpath("./version")

  version_content = dependency_node.at_xpath("./version").content.strip
  return unless version_content.match?(PROPERTY_REGEX)

  version_content.
    match(PROPERTY_REGEX).
    named_captures.fetch("property")
end