dependabot-maven 0.85.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/dependabot/maven/file_fetcher.rb +128 -0
- data/lib/dependabot/maven/file_parser/property_value_finder.rb +164 -0
- data/lib/dependabot/maven/file_parser/repositories_finder.rb +186 -0
- data/lib/dependabot/maven/file_parser.rb +253 -0
- data/lib/dependabot/maven/file_updater/declaration_finder.rb +142 -0
- data/lib/dependabot/maven/file_updater/property_value_updater.rb +59 -0
- data/lib/dependabot/maven/file_updater.rb +156 -0
- data/lib/dependabot/maven/metadata_finder.rb +175 -0
- data/lib/dependabot/maven/requirement.rb +110 -0
- data/lib/dependabot/maven/update_checker/property_updater.rb +125 -0
- data/lib/dependabot/maven/update_checker/requirements_updater.rb +90 -0
- data/lib/dependabot/maven/update_checker/version_finder.rb +223 -0
- data/lib/dependabot/maven/update_checker.rb +160 -0
- data/lib/dependabot/maven/version.rb +181 -0
- data/lib/dependabot/maven.rb +11 -0
- metadata +185 -0
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
|