dependabot-core 0.84.1 → 0.85.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,166 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "nokogiri"
4
-
5
- require "dependabot/dependency_file"
6
- require "dependabot/file_parsers/java/maven"
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 FileParsers
14
- module Java
15
- class Maven
16
- class PropertyValueFinder
17
- require_relative "repositories_finder"
18
-
19
- DOT_SEPARATOR_REGEX = %r{\.(?:(?!\d+[.\/])+)}.freeze
20
-
21
- def initialize(dependency_files:)
22
- @dependency_files = dependency_files
23
- end
24
-
25
- def property_details(property_name:, callsite_pom:)
26
- pom = callsite_pom
27
- doc = Nokogiri::XML(pom.content)
28
- doc.remove_namespaces!
29
-
30
- # Loop through the paths that would satisfy this property name,
31
- # looking for one that exists in this POM
32
- nm = sanitize_property_name(property_name)
33
- node =
34
- loop do
35
- candidate_node =
36
- doc.at_xpath("/project/#{nm}") ||
37
- doc.at_xpath("/project/properties/#{nm}") ||
38
- doc.at_xpath("/project/profiles/profile/properties/#{nm}")
39
- break candidate_node if candidate_node
40
- break unless nm.match?(DOT_SEPARATOR_REGEX)
41
-
42
- nm = nm.sub(DOT_SEPARATOR_REGEX, "/")
43
- end
44
-
45
- # If we found a property, return it
46
- if node
47
- return { file: pom.name, node: node, value: node.content.strip }
48
- end
49
-
50
- # Otherwise, look for a value in this pom's parent
51
- return unless (parent = parent_pom(pom))
52
-
53
- property_details(
54
- property_name: property_name,
55
- callsite_pom: parent
56
- )
57
- end
58
-
59
- private
60
-
61
- attr_reader :dependency_files
62
-
63
- def internal_dependency_poms
64
- return @internal_dependency_poms if @internal_dependency_poms
65
-
66
- @internal_dependency_poms = {}
67
- dependency_files.each do |pom|
68
- doc = Nokogiri::XML(pom.content)
69
- group_id = doc.at_css("project > groupId") ||
70
- doc.at_css("project > parent > groupId")
71
- artifact_id = doc.at_css("project > artifactId")
72
-
73
- next unless group_id && artifact_id
74
-
75
- dependency_name = [
76
- group_id.content.strip,
77
- artifact_id.content.strip
78
- ].join(":")
79
-
80
- @internal_dependency_poms[dependency_name] = pom
81
- end
82
-
83
- @internal_dependency_poms
84
- end
85
-
86
- def sanitize_property_name(property_name)
87
- property_name.sub(/^pom\./, "").sub(/^project\./, "")
88
- end
89
-
90
- def parent_pom(pom)
91
- doc = Nokogiri::XML(pom.content)
92
- doc.remove_namespaces!
93
- group_id = doc.at_xpath("/project/parent/groupId")&.content&.strip
94
- artifact_id =
95
- doc.at_xpath("/project/parent/artifactId")&.content&.strip
96
- version = doc.at_xpath("/project/parent/version")&.content&.strip
97
-
98
- return unless group_id && artifact_id
99
-
100
- name = [group_id, artifact_id].join(":")
101
-
102
- if internal_dependency_poms[name]
103
- return internal_dependency_poms[name]
104
- end
105
-
106
- return unless version && !version.include?(",")
107
-
108
- fetch_remote_parent_pom(group_id, artifact_id, version, pom)
109
- end
110
-
111
- def parent_repository_urls(pom)
112
- repositories_finder.repository_urls(
113
- pom: pom,
114
- exclude_inherited: true
115
- )
116
- end
117
-
118
- def repositories_finder
119
- @repositories_finder ||=
120
- RepositoriesFinder.new(
121
- dependency_files: dependency_files,
122
- evaluate_properties: false
123
- )
124
- end
125
-
126
- def fetch_remote_parent_pom(group_id, artifact_id, version, pom)
127
- parent_repository_urls(pom).each do |base_url|
128
- url = remote_pom_url(group_id, artifact_id, version, base_url)
129
-
130
- @maven_responses ||= {}
131
- @maven_responses[url] ||= Excon.get(
132
- url,
133
- idempotent: true,
134
- **SharedHelpers.excon_defaults
135
- )
136
- next unless @maven_responses[url].status == 200
137
- next unless pom?(@maven_responses[url].body)
138
-
139
- dependency_file = DependencyFile.new(
140
- name: "remote_pom.xml",
141
- content: @maven_responses[url].body
142
- )
143
-
144
- return dependency_file
145
- rescue Excon::Error::Socket, Excon::Error::Timeout
146
- nil
147
- end
148
-
149
- # If a parent POM couldn't be found, return `nil`
150
- nil
151
- end
152
-
153
- def remote_pom_url(group_id, artifact_id, version, base_repo_url)
154
- "#{base_repo_url}/"\
155
- "#{group_id.tr('.', '/')}/#{artifact_id}/#{version}/"\
156
- "#{artifact_id}-#{version}.pom"
157
- end
158
-
159
- def pom?(content)
160
- !Nokogiri::XML(content).at_css("project > artifactId").nil?
161
- end
162
- end
163
- end
164
- end
165
- end
166
- end
@@ -1,188 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "nokogiri"
4
-
5
- require "dependabot/dependency_file"
6
- require "dependabot/file_parsers/java/maven"
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 FileParsers
15
- module Java
16
- class Maven
17
- class RepositoriesFinder
18
- require_relative "property_value_finder"
19
- # In theory we should check the artifact type and either look in
20
- # <repositories> or <pluginRepositories>. In practice it's unlikely
21
- # anyone makes this distinction.
22
- REPOSITORY_SELECTOR = "repositories > repository, "\
23
- "pluginRepositories > pluginRepository"
24
-
25
- # The Central Repository is included in the Super POM, which is
26
- # always inherited from.
27
- CENTRAL_REPO_URL = "https://repo.maven.apache.org/maven2"
28
-
29
- def initialize(dependency_files:, evaluate_properties: true)
30
- @dependency_files = dependency_files
31
-
32
- # We need the option not to evaluate properties so as not to have a
33
- # circular dependency between this class and the PropertyValueFinder
34
- # class
35
- @evaluate_properties = evaluate_properties
36
- end
37
-
38
- # Collect all repository URLs from this POM and its parents
39
- def repository_urls(pom:, exclude_inherited: false)
40
- repo_urls_in_pom =
41
- Nokogiri::XML(pom.content).
42
- css(REPOSITORY_SELECTOR).
43
- map { |node| node.at_css("url").content.strip.gsub(%r{/$}, "") }.
44
- reject { |url| contains_property?(url) && !evaluate_properties? }.
45
- select { |url| url.start_with?("http") }.
46
- map { |url| evaluated_value(url, pom) }
47
-
48
- return repo_urls_in_pom + [CENTRAL_REPO_URL] if exclude_inherited
49
-
50
- unless (parent = parent_pom(pom, repo_urls_in_pom))
51
- return repo_urls_in_pom + [CENTRAL_REPO_URL]
52
- end
53
-
54
- repo_urls_in_pom + repository_urls(pom: parent)
55
- end
56
-
57
- private
58
-
59
- attr_reader :dependency_files
60
-
61
- def evaluate_properties?
62
- @evaluate_properties
63
- end
64
-
65
- def parent_pom(pom, repo_urls)
66
- doc = Nokogiri::XML(pom.content)
67
- doc.remove_namespaces!
68
- group_id = doc.at_xpath("/project/parent/groupId")&.content&.strip
69
- artifact_id =
70
- doc.at_xpath("/project/parent/artifactId")&.content&.strip
71
- version = doc.at_xpath("/project/parent/version")&.content&.strip
72
-
73
- return unless group_id && artifact_id
74
-
75
- name = [group_id, artifact_id].join(":")
76
-
77
- if internal_dependency_poms[name]
78
- return internal_dependency_poms[name]
79
- end
80
-
81
- return unless version && !version.include?(",")
82
-
83
- fetch_remote_parent_pom(group_id, artifact_id, version, repo_urls)
84
- end
85
-
86
- def internal_dependency_poms
87
- return @internal_dependency_poms if @internal_dependency_poms
88
-
89
- @internal_dependency_poms = {}
90
- dependency_files.each do |pom|
91
- doc = Nokogiri::XML(pom.content)
92
- group_id = doc.at_css("project > groupId") ||
93
- doc.at_css("project > parent > groupId")
94
- artifact_id = doc.at_css("project > artifactId")
95
-
96
- next unless group_id && artifact_id
97
-
98
- dependency_name = [
99
- group_id.content.strip,
100
- artifact_id.content.strip
101
- ].join(":")
102
-
103
- @internal_dependency_poms[dependency_name] = pom
104
- end
105
-
106
- @internal_dependency_poms
107
- end
108
-
109
- def fetch_remote_parent_pom(group_id, artifact_id, version, repo_urls)
110
- (repo_urls + [CENTRAL_REPO_URL]).uniq.each do |base_url|
111
- url = remote_pom_url(group_id, artifact_id, version, base_url)
112
-
113
- @maven_responses ||= {}
114
- @maven_responses[url] ||= Excon.get(
115
- url,
116
- idempotent: true,
117
- **SharedHelpers.excon_defaults
118
- )
119
- next unless @maven_responses[url].status == 200
120
- next unless pom?(@maven_responses[url].body)
121
-
122
- dependency_file = DependencyFile.new(
123
- name: "remote_pom.xml",
124
- content: @maven_responses[url].body
125
- )
126
-
127
- return dependency_file
128
- rescue Excon::Error::Socket, Excon::Error::Timeout
129
- nil
130
- end
131
-
132
- # If a parent POM couldn't be found, return `nil`
133
- nil
134
- end
135
-
136
- def remote_pom_url(group_id, artifact_id, version, base_repo_url)
137
- "#{base_repo_url}/"\
138
- "#{group_id.tr('.', '/')}/#{artifact_id}/#{version}/"\
139
- "#{artifact_id}-#{version}.pom"
140
- end
141
-
142
- def contains_property?(value)
143
- value.match?(property_regex)
144
- end
145
-
146
- def evaluated_value(value, pom)
147
- return value unless contains_property?(value)
148
-
149
- property_name = value.match(property_regex).
150
- named_captures.fetch("property")
151
- property_value = value_for_property(property_name, pom)
152
-
153
- value.gsub(property_regex, property_value)
154
- end
155
-
156
- def value_for_property(property_name, pom)
157
- value =
158
- property_value_finder.
159
- property_details(
160
- property_name: property_name,
161
- callsite_pom: pom
162
- )&.fetch(:value)
163
-
164
- return value if value
165
-
166
- msg = "Property not found: #{property_name}"
167
- raise DependencyFileNotEvaluatable, msg
168
- end
169
-
170
- # Cached, since this can makes calls to the registry (to get property
171
- # values from parent POMs)
172
- def property_value_finder
173
- @property_value_finder ||=
174
- PropertyValueFinder.new(dependency_files: dependency_files)
175
- end
176
-
177
- def property_regex
178
- FileParsers::Java::Maven::PROPERTY_REGEX
179
- end
180
-
181
- def pom?(content)
182
- !Nokogiri::XML(content).at_css("project > artifactId").nil?
183
- end
184
- end
185
- end
186
- end
187
- end
188
- end
@@ -1,155 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "nokogiri"
4
- require "dependabot/file_updaters/base"
5
-
6
- module Dependabot
7
- module FileUpdaters
8
- module Java
9
- class Maven < Dependabot::FileUpdaters::Base
10
- require_relative "maven/declaration_finder"
11
- require_relative "maven/property_value_updater"
12
-
13
- def self.updated_files_regex
14
- [/^pom\.xml$/, %r{/pom\.xml$}]
15
- end
16
-
17
- def updated_dependency_files
18
- updated_files = dependency_files.dup
19
-
20
- # Loop through each of the changed requirements, applying changes to
21
- # all pomfiles for that change. Note that the logic is different here
22
- # to other languages because Java has property inheritance across
23
- # files
24
- dependencies.each do |dependency|
25
- updated_files = update_pomfiles_for_dependency(
26
- pomfiles: updated_files,
27
- dependency: dependency
28
- )
29
- end
30
-
31
- updated_files.select! { |f| f.name.end_with?("pom.xml") }
32
- updated_files.reject! { |f| original_pomfiles.include?(f) }
33
-
34
- raise "No files changed!" if updated_files.none?
35
- if updated_files.any? { |f| f.name.end_with?("pom_parent.xml") }
36
- raise "Updated a supporting POM!"
37
- end
38
-
39
- updated_files
40
- end
41
-
42
- private
43
-
44
- def check_required_files
45
- raise "No pom.xml!" unless get_original_file("pom.xml")
46
- end
47
-
48
- def update_pomfiles_for_dependency(pomfiles:, dependency:)
49
- files = pomfiles.dup
50
-
51
- # The UpdateChecker ensures the order of requirements is preserved
52
- # when updating, so we can zip them together in new/old pairs.
53
- reqs = dependency.requirements.zip(dependency.previous_requirements).
54
- reject { |new_req, old_req| new_req == old_req }
55
-
56
- # Loop through each changed requirement and update the pomfiles
57
- reqs.each do |new_req, old_req|
58
- raise "Bad req match" unless new_req[:file] == old_req[:file]
59
- next if new_req[:requirement] == old_req[:requirement]
60
-
61
- if new_req.dig(:metadata, :property_name)
62
- files = update_pomfiles_for_property_change(files, new_req)
63
- pom = files.find { |f| f.name == new_req.fetch(:file) }
64
- files[files.index(pom)] =
65
- remove_property_suffix_in_pom(dependency, pom, old_req)
66
- else
67
- pom = files.find { |f| f.name == new_req.fetch(:file) }
68
- files[files.index(pom)] =
69
- update_version_in_pom(dependency, pom, old_req, new_req)
70
- end
71
- end
72
-
73
- files
74
- end
75
-
76
- def update_pomfiles_for_property_change(pomfiles, req)
77
- property_name = req.fetch(:metadata).fetch(:property_name)
78
-
79
- PropertyValueUpdater.new(dependency_files: pomfiles).
80
- update_pomfiles_for_property_change(
81
- property_name: property_name,
82
- callsite_pom: pomfiles.find { |f| f.name == req.fetch(:file) },
83
- updated_value: req.fetch(:requirement)
84
- )
85
- end
86
-
87
- def update_version_in_pom(dependency, pom, previous_req, requirement)
88
- updated_content = pom.content
89
-
90
- original_pom_declarations(dependency, previous_req).each do |old_dec|
91
- updated_content = updated_content.gsub(
92
- old_dec,
93
- updated_pom_declaration(old_dec, previous_req, requirement)
94
- )
95
- end
96
-
97
- raise "Expected content to change!" if updated_content == pom.content
98
-
99
- updated_file(file: pom, content: updated_content)
100
- end
101
-
102
- def remove_property_suffix_in_pom(dep, pom, req)
103
- updated_content = pom.content
104
-
105
- original_pom_declarations(dep, req).each do |old_declaration|
106
- updated_content = updated_content.gsub(old_declaration) do |old_dec|
107
- version_string =
108
- old_dec.match(%r{(?<=\<version\>).*(?=\</version\>)})
109
- cleaned_version_string = version_string.to_s.gsub(/(?<=\}).*/, "")
110
-
111
- old_dec.gsub(
112
- "<version>#{version_string}</version>",
113
- "<version>#{cleaned_version_string}</version>"
114
- )
115
- end
116
- end
117
-
118
- updated_file(file: pom, content: updated_content)
119
- end
120
-
121
- def original_pom_declarations(dependency, requirement)
122
- declaration_finder(dependency, requirement).declaration_strings
123
- end
124
-
125
- # The declaration finder may need to make remote calls (to get parent
126
- # POMs if it's searching for the value of a property), so we cache it.
127
- def declaration_finder(dependency, requirement)
128
- @declaration_finders ||= {}
129
- @declaration_finders[dependency.hash + requirement.hash] ||=
130
- begin
131
- DeclarationFinder.new(
132
- dependency: dependency,
133
- declaring_requirement: requirement,
134
- dependency_files: dependency_files
135
- )
136
- end
137
- end
138
-
139
- def updated_pom_declaration(old_declaration, previous_req, requirement)
140
- original_req_string = previous_req.fetch(:requirement)
141
-
142
- old_declaration.gsub(
143
- %r{<version>\s*#{Regexp.quote(original_req_string)}\s*</version>},
144
- "<version>#{requirement.fetch(:requirement)}</version>"
145
- )
146
- end
147
-
148
- def original_pomfiles
149
- @original_pomfiles ||=
150
- dependency_files.select { |f| f.name.end_with?("pom.xml") }
151
- end
152
- end
153
- end
154
- end
155
- end