diff --git a/maven/lib/dependabot/maven/file_fetcher.rb b/maven/lib/dependabot/maven/file_fetcher.rb index 245ac5336e5..eb0c8ee5f0a 100644 --- a/maven/lib/dependabot/maven/file_fetcher.rb +++ b/maven/lib/dependabot/maven/file_fetcher.rb @@ -25,6 +25,7 @@ def fetch_files fetched_files << pom fetched_files += child_poms fetched_files += relative_path_parents(fetched_files) + fetched_files += targetfiles fetched_files << extensions if extensions fetched_files.uniq end @@ -39,6 +40,13 @@ def extensions fetch_file_if_present(".mvn/extensions.xml") end + def targetfiles + @targetfiles ||= + repo_contents(raise_errors: false). + select { |f| f.type == "file" && f.name.end_with?(".target") }. + map { |f| fetch_file_from_host(f.name) } + end + def child_poms recursively_fetch_child_poms(pom, fetched_filenames: ["pom.xml"]) end diff --git a/maven/lib/dependabot/maven/file_parser.rb b/maven/lib/dependabot/maven/file_parser.rb index 43fa15d0e99..dfcd9a33ab5 100644 --- a/maven/lib/dependabot/maven/file_parser.rb +++ b/maven/lib/dependabot/maven/file_parser.rb @@ -21,12 +21,14 @@ class FileParser < Dependabot::FileParsers::Base # - Any dependencies (incl. those in dependencyManagement or plugins) # - Any plugins (incl. those in pluginManagement) # - Any extensions + # - Any eclipse-target with a location of type Maven DEPENDENCY_SELECTOR = "project > parent, " \ "dependencies > dependency, " \ "extensions > extension, " \ "annotationProcessorPaths > path" PLUGIN_SELECTOR = "plugins > plugin" EXTENSION_SELECTOR = "extensions > extension" + TARGET_SELECTOR = "target > locations > location[type='Maven'] > dependencies > dependency" # Regex to get the property name from a declaration that uses a property PROPERTY_REGEX = /\$\{(?.*?)\}/ @@ -35,6 +37,7 @@ def parse dependency_set = DependencySet.new pomfiles.each { |pom| dependency_set += pomfile_dependencies(pom) } extensionfiles.each { |extension| dependency_set += extensionfile_dependencies(extension) } + targetfiles.each { |target| dependency_set += targetfile_dependencies(target) } dependency_set.dependencies end @@ -85,6 +88,25 @@ def extensionfile_dependencies(extension) dependency_set end + def targetfile_dependencies(target) + dependency_set = DependencySet.new + + errors = [] + doc = Nokogiri::XML(target.content) + doc.remove_namespaces! + + doc.css(TARGET_SELECTOR).each do |dependency_node| + dep = dependency_from_dependency_node(target, 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 + def dependency_from_dependency_node(pom, dependency_node) return unless (name = dependency_name(dependency_node, pom)) return if internal_dependency_names.include?(name) @@ -283,6 +305,11 @@ def extensionfiles dependency_files.select { |f| f.name.end_with?("extensions.xml") } end + def targetfiles + @targetfiles ||= + dependency_files.select { |f| f.name.end_with?(".target") } + end + def internal_dependency_names @internal_dependency_names ||= dependency_files.filter_map do |pom| diff --git a/maven/spec/dependabot/maven/file_fetcher_spec.rb b/maven/spec/dependabot/maven/file_fetcher_spec.rb index a31327259d7..73bab1aa77a 100644 --- a/maven/spec/dependabot/maven/file_fetcher_spec.rb +++ b/maven/spec/dependabot/maven/file_fetcher_spec.rb @@ -15,7 +15,7 @@ ) end let(:file_fetcher_instance) do - described_class.new(source: source, credentials: credentials) + described_class.new(source: source, credentials: credentials, repo_contents_path: nil) end let(:directory) { "/" } let(:github_url) { "https://api.github.com/" } @@ -71,6 +71,11 @@ to_return( status: 404 ) + stub_request(:get, /.*\?ref=sha/). + with(headers: { "Authorization" => "token token" }). + to_return( + status: 404 + ) end context "with a basic pom" do diff --git a/maven/spec/dependabot/maven/file_parser_spec.rb b/maven/spec/dependabot/maven/file_parser_spec.rb index 5a5afd8b79c..2e3e4fabc4d 100644 --- a/maven/spec/dependabot/maven/file_parser_spec.rb +++ b/maven/spec/dependabot/maven/file_parser_spec.rb @@ -114,6 +114,33 @@ end end + context "with target-file" do + let(:files) { [targetfile, pom] } + let(:targetfile) do + Dependabot::DependencyFile.new(name: "releng/myproject.target", content: targetfile_body) + end + let(:targetfile_body) { fixture("target-files", "example.target") } + + describe "the sole dependency" do + subject(:dependency) { dependencies[3] } + + it "has the right details" do + expect(dependency).to be_a(Dependabot::Dependency) + expect(dependency.name).to eq("commons-io:commons-io") + expect(dependency.version).to eq("2.11.0") + expect(dependency.requirements).to eq( + [{ + requirement: "2.11.0", + file: "releng/myproject.target", + groups: [], + source: nil, + metadata: { packaging_type: "jar" } + }] + ) + end + end + end + context "with rogue whitespace" do let(:pom_body) { fixture("poms", "whitespace.xml") } diff --git a/maven/spec/fixtures/target-files/example.target b/maven/spec/fixtures/target-files/example.target new file mode 100644 index 00000000000..86ef8ab39db --- /dev/null +++ b/maven/spec/fixtures/target-files/example.target @@ -0,0 +1,18 @@ + + + + + + + + + commons-io + commons-io + 2.11.0 + jar + + + + + + \ No newline at end of file