dependabot-maven 0.85.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c70fcdd9db80bcb5c781271979123b194482cef46189bd23e833408e2becc307
4
+ data.tar.gz: 5befedbb46de560a815ce47ee3fba871a48017a0b6a17714bdd2fd1f0191cf5c
5
+ SHA512:
6
+ metadata.gz: 55f03fced39293f82040504860e05a342c8cc334495d5ffd9865d7c8e3e0a858b3e9c9368cfb340a911ed17634e2b1a2f45edc2a6aee09cb3e714b89ee8bb095
7
+ data.tar.gz: e7e1a8793c35b0f8f44bfcedd310367cea396c60002939798394e94fc56fa7da17f91ef260f8503fa18a721324cf0c00e14def849ed0002b0d5575b03fd01adf
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nokogiri"
4
+ require "dependabot/file_fetchers"
5
+ require "dependabot/file_fetchers/base"
6
+
7
+ module Dependabot
8
+ module Maven
9
+ class FileFetcher < Dependabot::FileFetchers::Base
10
+ MODULE_SELECTOR = "project > modules > module"
11
+
12
+ def self.required_files_in?(filenames)
13
+ (%w(pom.xml) - filenames).empty?
14
+ end
15
+
16
+ def self.required_files_message
17
+ "Repo must contain a pom.xml."
18
+ end
19
+
20
+ private
21
+
22
+ def fetch_files
23
+ fetched_files = []
24
+ fetched_files << pom
25
+ fetched_files += child_poms
26
+ fetched_files += relative_path_parents(fetched_files)
27
+ fetched_files.uniq
28
+ end
29
+
30
+ def pom
31
+ @pom ||= fetch_file_from_host("pom.xml")
32
+ end
33
+
34
+ def child_poms
35
+ recursively_fetch_child_poms(pom, fetched_filenames: ["pom.xml"])
36
+ end
37
+
38
+ def relative_path_parents(fetched_files)
39
+ fetched_files.flat_map do |file|
40
+ recursively_fetch_relative_path_parents(
41
+ file,
42
+ fetched_filenames: fetched_files.map(&:name)
43
+ )
44
+ end
45
+ end
46
+
47
+ def recursively_fetch_child_poms(pom, fetched_filenames:)
48
+ base_path = pom.name.gsub(/pom\.xml$/, "")
49
+ doc = Nokogiri::XML(pom.content)
50
+
51
+ doc.css(MODULE_SELECTOR).flat_map do |module_node|
52
+ relative_path = module_node.content.strip
53
+ name_parts = [
54
+ base_path,
55
+ relative_path,
56
+ relative_path.end_with?("pom.xml") ? nil : "pom.xml"
57
+ ].compact.reject(&:empty?)
58
+ path = Pathname.new(File.join(*name_parts)).cleanpath.to_path
59
+
60
+ next [] if fetched_filenames.include?(path)
61
+
62
+ child_pom = fetch_file_from_host(path)
63
+ fetched_filenames += [child_pom.name]
64
+ [
65
+ child_pom,
66
+ recursively_fetch_child_poms(
67
+ child_pom,
68
+ fetched_filenames: fetched_filenames
69
+ )
70
+ ].flatten
71
+ rescue Dependabot::DependencyFileNotFound
72
+ raise unless fetch_file_from_host_or_submodule(path)
73
+
74
+ [] # Ignore any child submodules (since we can't update them)
75
+ end
76
+ end
77
+
78
+ def recursively_fetch_relative_path_parents(pom, fetched_filenames:)
79
+ path = parent_path_for_pom(pom)
80
+
81
+ if fetched_filenames.include?(path) ||
82
+ fetched_filenames.include?(path.gsub("pom.xml", "pom_parent.xml"))
83
+ return []
84
+ end
85
+
86
+ full_path_parts =
87
+ [directory.gsub(%r{^/}, ""), path].reject(&:empty?).compact
88
+
89
+ full_path = Pathname.new(File.join(*full_path_parts)).
90
+ cleanpath.to_path
91
+
92
+ return [] if full_path.start_with?("..")
93
+
94
+ parent_pom = fetch_file_from_host(path)
95
+ parent_pom.support_file = true
96
+ parent_pom.name = parent_pom.name.gsub("pom.xml", "pom_parent.xml")
97
+
98
+ [
99
+ parent_pom,
100
+ recursively_fetch_relative_path_parents(
101
+ parent_pom,
102
+ fetched_filenames: fetched_filenames + [parent_pom.name]
103
+ )
104
+ ].flatten
105
+ rescue Dependabot::DependencyFileNotFound
106
+ []
107
+ end
108
+
109
+ def parent_path_for_pom(pom)
110
+ doc = Nokogiri::XML(pom.content)
111
+ doc.remove_namespaces!
112
+
113
+ relative_parent_path =
114
+ doc.at_xpath("/project/parent/relativePath")&.content&.strip || ".."
115
+
116
+ name_parts = [
117
+ pom.name.gsub(/pom\.xml$/, "").gsub(/pom_parent\.xml$/, ""),
118
+ relative_parent_path,
119
+ relative_parent_path.end_with?("pom.xml") ? nil : "pom.xml"
120
+ ].compact.reject(&:empty?)
121
+
122
+ Pathname.new(File.join(*name_parts)).cleanpath.to_path
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ Dependabot::FileFetchers.register("maven", Dependabot::Maven::FileFetcher)
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nokogiri"
4
+
5
+ require "dependabot/dependency_file"
6
+ require "dependabot/maven/file_parser"
7
+ require "dependabot/shared_helpers"
8
+
9
+ # For documentation, see:
10
+ # - http://maven.apache.org/guides/introduction/introduction-to-the-pom.html
11
+ # - http://maven.apache.org/pom.html#Properties
12
+ module Dependabot
13
+ module Maven
14
+ class FileParser
15
+ class PropertyValueFinder
16
+ require_relative "repositories_finder"
17
+
18
+ DOT_SEPARATOR_REGEX = %r{\.(?:(?!\d+[.\/])+)}.freeze
19
+
20
+ def initialize(dependency_files:)
21
+ @dependency_files = dependency_files
22
+ end
23
+
24
+ def property_details(property_name:, callsite_pom:)
25
+ pom = callsite_pom
26
+ doc = Nokogiri::XML(pom.content)
27
+ doc.remove_namespaces!
28
+
29
+ # Loop through the paths that would satisfy this property name,
30
+ # looking for one that exists in this POM
31
+ nm = sanitize_property_name(property_name)
32
+ node =
33
+ loop do
34
+ candidate_node =
35
+ doc.at_xpath("/project/#{nm}") ||
36
+ doc.at_xpath("/project/properties/#{nm}") ||
37
+ doc.at_xpath("/project/profiles/profile/properties/#{nm}")
38
+ break candidate_node if candidate_node
39
+ break unless nm.match?(DOT_SEPARATOR_REGEX)
40
+
41
+ nm = nm.sub(DOT_SEPARATOR_REGEX, "/")
42
+ end
43
+
44
+ # If we found a property, return it
45
+ if node
46
+ return { file: pom.name, node: node, value: node.content.strip }
47
+ end
48
+
49
+ # Otherwise, look for a value in this pom's parent
50
+ return unless (parent = parent_pom(pom))
51
+
52
+ property_details(
53
+ property_name: property_name,
54
+ callsite_pom: parent
55
+ )
56
+ end
57
+
58
+ private
59
+
60
+ attr_reader :dependency_files
61
+
62
+ def internal_dependency_poms
63
+ return @internal_dependency_poms if @internal_dependency_poms
64
+
65
+ @internal_dependency_poms = {}
66
+ dependency_files.each do |pom|
67
+ doc = Nokogiri::XML(pom.content)
68
+ group_id = doc.at_css("project > groupId") ||
69
+ doc.at_css("project > parent > groupId")
70
+ artifact_id = doc.at_css("project > artifactId")
71
+
72
+ next unless group_id && artifact_id
73
+
74
+ dependency_name = [
75
+ group_id.content.strip,
76
+ artifact_id.content.strip
77
+ ].join(":")
78
+
79
+ @internal_dependency_poms[dependency_name] = pom
80
+ end
81
+
82
+ @internal_dependency_poms
83
+ end
84
+
85
+ def sanitize_property_name(property_name)
86
+ property_name.sub(/^pom\./, "").sub(/^project\./, "")
87
+ end
88
+
89
+ def parent_pom(pom)
90
+ doc = Nokogiri::XML(pom.content)
91
+ doc.remove_namespaces!
92
+ group_id = doc.at_xpath("/project/parent/groupId")&.content&.strip
93
+ artifact_id =
94
+ doc.at_xpath("/project/parent/artifactId")&.content&.strip
95
+ version = doc.at_xpath("/project/parent/version")&.content&.strip
96
+
97
+ return unless group_id && artifact_id
98
+
99
+ name = [group_id, artifact_id].join(":")
100
+
101
+ if internal_dependency_poms[name]
102
+ return internal_dependency_poms[name]
103
+ end
104
+
105
+ return unless version && !version.include?(",")
106
+
107
+ fetch_remote_parent_pom(group_id, artifact_id, version, pom)
108
+ end
109
+
110
+ def parent_repository_urls(pom)
111
+ repositories_finder.repository_urls(
112
+ pom: pom,
113
+ exclude_inherited: true
114
+ )
115
+ end
116
+
117
+ def repositories_finder
118
+ @repositories_finder ||=
119
+ RepositoriesFinder.new(
120
+ dependency_files: dependency_files,
121
+ evaluate_properties: false
122
+ )
123
+ end
124
+
125
+ def fetch_remote_parent_pom(group_id, artifact_id, version, pom)
126
+ parent_repository_urls(pom).each do |base_url|
127
+ url = remote_pom_url(group_id, artifact_id, version, base_url)
128
+
129
+ @maven_responses ||= {}
130
+ @maven_responses[url] ||= Excon.get(
131
+ url,
132
+ idempotent: true,
133
+ **SharedHelpers.excon_defaults
134
+ )
135
+ next unless @maven_responses[url].status == 200
136
+ next unless pom?(@maven_responses[url].body)
137
+
138
+ dependency_file = DependencyFile.new(
139
+ name: "remote_pom.xml",
140
+ content: @maven_responses[url].body
141
+ )
142
+
143
+ return dependency_file
144
+ rescue Excon::Error::Socket, Excon::Error::Timeout
145
+ nil
146
+ end
147
+
148
+ # If a parent POM couldn't be found, return `nil`
149
+ nil
150
+ end
151
+
152
+ def remote_pom_url(group_id, artifact_id, version, base_repo_url)
153
+ "#{base_repo_url}/"\
154
+ "#{group_id.tr('.', '/')}/#{artifact_id}/#{version}/"\
155
+ "#{artifact_id}-#{version}.pom"
156
+ end
157
+
158
+ def pom?(content)
159
+ !Nokogiri::XML(content).at_css("project > artifactId").nil?
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nokogiri"
4
+
5
+ require "dependabot/dependency_file"
6
+ require "dependabot/maven/file_parser"
7
+ require "dependabot/shared_helpers"
8
+ require "dependabot/errors"
9
+
10
+ # For documentation, see:
11
+ # - http://maven.apache.org/pom.html#Repositories
12
+ # - http://maven.apache.org/guides/mini/guide-multiple-repositories.html
13
+ module Dependabot
14
+ module Maven
15
+ class FileParser
16
+ class RepositoriesFinder
17
+ require_relative "property_value_finder"
18
+ # In theory we should check the artifact type and either look in
19
+ # <repositories> or <pluginRepositories>. In practice it's unlikely
20
+ # anyone makes this distinction.
21
+ REPOSITORY_SELECTOR = "repositories > repository, "\
22
+ "pluginRepositories > pluginRepository"
23
+
24
+ # The Central Repository is included in the Super POM, which is
25
+ # always inherited from.
26
+ CENTRAL_REPO_URL = "https://repo.maven.apache.org/maven2"
27
+
28
+ def initialize(dependency_files:, evaluate_properties: true)
29
+ @dependency_files = dependency_files
30
+
31
+ # We need the option not to evaluate properties so as not to have a
32
+ # circular dependency between this class and the PropertyValueFinder
33
+ # class
34
+ @evaluate_properties = evaluate_properties
35
+ end
36
+
37
+ # Collect all repository URLs from this POM and its parents
38
+ def repository_urls(pom:, exclude_inherited: false)
39
+ repo_urls_in_pom =
40
+ Nokogiri::XML(pom.content).
41
+ css(REPOSITORY_SELECTOR).
42
+ map { |node| node.at_css("url").content.strip.gsub(%r{/$}, "") }.
43
+ reject { |url| contains_property?(url) && !evaluate_properties? }.
44
+ select { |url| url.start_with?("http") }.
45
+ map { |url| evaluated_value(url, pom) }
46
+
47
+ return repo_urls_in_pom + [CENTRAL_REPO_URL] if exclude_inherited
48
+
49
+ unless (parent = parent_pom(pom, repo_urls_in_pom))
50
+ return repo_urls_in_pom + [CENTRAL_REPO_URL]
51
+ end
52
+
53
+ repo_urls_in_pom + repository_urls(pom: parent)
54
+ end
55
+
56
+ private
57
+
58
+ attr_reader :dependency_files
59
+
60
+ def evaluate_properties?
61
+ @evaluate_properties
62
+ end
63
+
64
+ def parent_pom(pom, repo_urls)
65
+ doc = Nokogiri::XML(pom.content)
66
+ doc.remove_namespaces!
67
+ group_id = doc.at_xpath("/project/parent/groupId")&.content&.strip
68
+ artifact_id =
69
+ doc.at_xpath("/project/parent/artifactId")&.content&.strip
70
+ version = doc.at_xpath("/project/parent/version")&.content&.strip
71
+
72
+ return unless group_id && artifact_id
73
+
74
+ name = [group_id, artifact_id].join(":")
75
+
76
+ if internal_dependency_poms[name]
77
+ return internal_dependency_poms[name]
78
+ end
79
+
80
+ return unless version && !version.include?(",")
81
+
82
+ fetch_remote_parent_pom(group_id, artifact_id, version, repo_urls)
83
+ end
84
+
85
+ def internal_dependency_poms
86
+ return @internal_dependency_poms if @internal_dependency_poms
87
+
88
+ @internal_dependency_poms = {}
89
+ dependency_files.each do |pom|
90
+ doc = Nokogiri::XML(pom.content)
91
+ group_id = doc.at_css("project > groupId") ||
92
+ doc.at_css("project > parent > groupId")
93
+ artifact_id = doc.at_css("project > artifactId")
94
+
95
+ next unless group_id && artifact_id
96
+
97
+ dependency_name = [
98
+ group_id.content.strip,
99
+ artifact_id.content.strip
100
+ ].join(":")
101
+
102
+ @internal_dependency_poms[dependency_name] = pom
103
+ end
104
+
105
+ @internal_dependency_poms
106
+ end
107
+
108
+ def fetch_remote_parent_pom(group_id, artifact_id, version, repo_urls)
109
+ (repo_urls + [CENTRAL_REPO_URL]).uniq.each do |base_url|
110
+ url = remote_pom_url(group_id, artifact_id, version, base_url)
111
+
112
+ @maven_responses ||= {}
113
+ @maven_responses[url] ||= Excon.get(
114
+ url,
115
+ idempotent: true,
116
+ **SharedHelpers.excon_defaults
117
+ )
118
+ next unless @maven_responses[url].status == 200
119
+ next unless pom?(@maven_responses[url].body)
120
+
121
+ dependency_file = DependencyFile.new(
122
+ name: "remote_pom.xml",
123
+ content: @maven_responses[url].body
124
+ )
125
+
126
+ return dependency_file
127
+ rescue Excon::Error::Socket, Excon::Error::Timeout
128
+ nil
129
+ end
130
+
131
+ # If a parent POM couldn't be found, return `nil`
132
+ nil
133
+ end
134
+
135
+ def remote_pom_url(group_id, artifact_id, version, base_repo_url)
136
+ "#{base_repo_url}/"\
137
+ "#{group_id.tr('.', '/')}/#{artifact_id}/#{version}/"\
138
+ "#{artifact_id}-#{version}.pom"
139
+ end
140
+
141
+ def contains_property?(value)
142
+ value.match?(property_regex)
143
+ end
144
+
145
+ def evaluated_value(value, pom)
146
+ return value unless contains_property?(value)
147
+
148
+ property_name = value.match(property_regex).
149
+ named_captures.fetch("property")
150
+ property_value = value_for_property(property_name, pom)
151
+
152
+ value.gsub(property_regex, property_value)
153
+ end
154
+
155
+ def value_for_property(property_name, pom)
156
+ value =
157
+ property_value_finder.
158
+ property_details(
159
+ property_name: property_name,
160
+ callsite_pom: pom
161
+ )&.fetch(:value)
162
+
163
+ return value if value
164
+
165
+ msg = "Property not found: #{property_name}"
166
+ raise DependencyFileNotEvaluatable, msg
167
+ end
168
+
169
+ # Cached, since this can makes calls to the registry (to get property
170
+ # values from parent POMs)
171
+ def property_value_finder
172
+ @property_value_finder ||=
173
+ PropertyValueFinder.new(dependency_files: dependency_files)
174
+ end
175
+
176
+ def property_regex
177
+ Maven::FileParser::PROPERTY_REGEX
178
+ end
179
+
180
+ def pom?(content)
181
+ !Nokogiri::XML(content).at_css("project > artifactId").nil?
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end