dependabot-core 0.84.1 → 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.
@@ -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