dependabot-maven 0.85.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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