Class: Dependabot::Gradle::Package::PackageDetailsFetcher

Inherits:
Object
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/dependabot/gradle/package/package_details_fetcher.rb

Constant Summary collapse

CENTRAL_REPO_URL =
"https://repo.maven.apache.org/maven2"
KOTLIN_PLUGIN_REPO_PREFIX =
"org.jetbrains.kotlin"
TYPE_SUFFICES =
%w(jre android java native_mt agp).freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dependency:, dependency_files:, credentials:, forbidden_urls:) ⇒ PackageDetailsFetcher

Returns a new instance of PackageDetailsFetcher.



37
38
39
40
41
42
43
44
45
46
47
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 37

def initialize(dependency:, dependency_files:, credentials:, forbidden_urls:)
  @dependency = dependency
  @dependency_files = dependency_files
  @credentials = credentials
  @forbidden_urls = forbidden_urls
  @repositories = T.let(nil, T.nilable(T::Array[T::Hash[String, T.untyped]]))
  @google_version_details = T.let(nil, T.nilable(T::Array[T::Hash[String, T.untyped]]))
  @dependency_repository_details = T.let(nil, T.nilable(T::Array[T::Hash[String, T.untyped]]))
  @release_details = T.let(nil, T.nilable(T::Hash[String, T::Hash[Symbol, T.untyped]]))
  @version_release_date_fallback_fetcher = T.let(nil, T.nilable(VersionReleaseDateFallbackFetcher))
end

Instance Attribute Details

#credentialsObject (readonly)

Returns the value of attribute credentials.



56
57
58
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 56

def credentials
  @credentials
end

#dependencyObject (readonly)

Returns the value of attribute dependency.



50
51
52
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 50

def dependency
  @dependency
end

#dependency_filesObject (readonly)

Returns the value of attribute dependency_files.



53
54
55
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 53

def dependency_files
  @dependency_files
end

#forbidden_urlsObject (readonly)

Returns the value of attribute forbidden_urls.



59
60
61
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 59

def forbidden_urls
  @forbidden_urls
end

Instance Method Details

#auth_headers(maven_repo_url) ⇒ Object



422
423
424
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 422

def auth_headers(maven_repo_url)
  auth_headers_finder.auth_headers(maven_repo_url)
end

#auth_headers_finderObject



414
415
416
417
418
419
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 414

def auth_headers_finder
  @auth_headers_finder ||= T.let(
    Dependabot::Maven::Utils::AuthHeadersFinder.new(credentials),
    T.nilable(Dependabot::Maven::Utils::AuthHeadersFinder)
  )
end

#build_release_with_date(release, release_date) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 140

def build_release_with_date(release, release_date)
  Dependabot::Package::PackageRelease.new(
    version: release.version,
    released_at: release_date,
    latest: release.latest,
    yanked: release.yanked,
    yanked_reason: release.yanked_reason,
    downloads: release.downloads,
    url: release.url,
    package_type: release.package_type,
    language: release.language,
    tag: release.tag,
    details: release.details
  )
end

#central_repo_urlsObject



400
401
402
403
404
405
406
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 400

def central_repo_urls
  central_url_without_protocol =
    Gradle::FileParser::RepositoriesFinder::CENTRAL_REPO_URL
    .gsub(%r{^.*://}, "")

  %w(http:// https://).map { |p| p + central_url_without_protocol }
end

#check_response(response, repository_url) ⇒ Object



273
274
275
276
277
278
279
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 273

def check_response(response, repository_url)
  return unless response.status == 401 || response.status == 403
  return if T.must(@forbidden_urls).include?(repository_url)
  return if central_repo_urls.include?(repository_url)

  T.must(@forbidden_urls) << repository_url
end

#credentials_repository_detailsObject



282
283
284
285
286
287
288
289
290
291
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 282

def credentials_repository_details
  credentials
    .select { |cred| cred["type"] == "maven_repository" }
    .map do |cred|
    {
      "url" => cred.fetch("url").gsub(%r{/+$}, ""),
      "auth_headers" => auth_headers(cred.fetch("url").gsub(%r{/+$}, ""))
    }
  end
end

#dependency_metadata(repository_details) ⇒ Object



224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 224

def (repository_details)
  @dependency_metadata ||= T.let({}, T.nilable(T::Hash[T.untyped, T.untyped]))
  @dependency_metadata[repository_details.hash] ||=
    begin
      response = Dependabot::RegistryClient.get(
        url: (repository_details.fetch("url")),
        headers: repository_details.fetch("auth_headers")
      )

      check_response(response, repository_details.fetch("url"))
      Nokogiri::XML(response.body)
    rescue URI::InvalidURIError
      Nokogiri::XML("")
    rescue Excon::Error::Socket, Excon::Error::Timeout,
           Excon::Error::TooManyRedirects
      raise if central_repo_urls.include?(repository_details["url"])

      Nokogiri::XML("")
    end
end

#dependency_metadata_url(repository_url) ⇒ Object



352
353
354
355
356
357
358
359
360
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 352

def (repository_url)
  group_id, artifact_id = group_and_artifact_ids
  group_id = "#{Dependabot::Gradle::MetadataFinder::KOTLIN_PLUGIN_REPO_PREFIX}.#{group_id}" if kotlin_plugin?

  "#{repository_url}/" \
    "#{T.must(group_id).tr('.', '/')}/" \
    "#{artifact_id}/" \
    "maven-metadata.xml"
end

#dependency_repository_detailsObject



294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 294

def dependency_repository_details
  requirement_files =
    dependency.requirements
              .map { |r| r.fetch(:file) }
              .map { |nm| dependency_files.find { |f| f.name == nm } }

  @dependency_repository_details ||=
    requirement_files.flat_map do |target_file|
      Gradle::FileParser::RepositoriesFinder.new(
        dependency_files: dependency_files,
        target_dependency_file: target_file,
        credentials: credentials
      ).repository_urls
                                            .map do |url|
        { "url" => url, "auth_headers" => auth_headers(url) }
      end
    end.uniq
end

#distribution?Boolean

Returns:

  • (Boolean)


385
386
387
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 385

def distribution?
  Distributions.distribution_requirements?(dependency.requirements)
end

#distribution_repository_detailsObject



320
321
322
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 320

def distribution_repository_details
  [{ "url" => Gradle::Distributions::DISTRIBUTION_REPOSITORY_URL, "auth_headers" => {} }]
end

#distribution_version_detailsObject



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 177

def distribution_version_details
  DistributionsFetcher.available_versions.map do |info|
    release_date = begin
      Time.parse(info[:build_time])
    rescue StandardError
      nil
    end

    {
      version: info[:version],
      released_at: release_date,
      source_url: Distributions::DISTRIBUTION_REPOSITORY_URL
    }
  end
rescue StandardError
  nil
end

#fetch_available_versionsObject



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 63

def fetch_available_versions
  package_releases = T.let([], T::Array[T::Hash[String, T.untyped]])
  version_details =
    repositories.map do |repository_details|
      url = repository_details.fetch("url")
      next distribution_version_details if url == Gradle::Distributions::DISTRIBUTION_REPOSITORY_URL
      next google_version_details if url == Gradle::FileParser::RepositoriesFinder::GOOGLE_MAVEN_REPO

      (repository_details).css("versions > version")
                                             .select { |node| version_class.correct?(node.content) }
                                             .map { |node| version_class.new(node.content) }
                                             .map do |version|
        { version: version, source_url: url }
      end
    end.flatten.compact

  version_details = version_details.sort_by { |details| details.fetch(:version) }
  release_date_info = release_details
  version_details.each do |info|
    package_releases << {
      version: Gradle::Version.new(info[:version].to_s),
      released_at: info[:released_at] || release_date_info[info[:version].to_s]&.fetch(:release_date),
      source_url: info[:source_url]
    }
  end
  if version_details.none? && T.must(forbidden_urls).any?
    raise PrivateSourceAuthenticationFailure, T.must(forbidden_urls).first
  end

  package_releases
end

#fetch_release_metadata(release:) ⇒ Object



97
98
99
100
101
102
103
104
105
106
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 97

def (release:)
  return release if release.released_at

  release_date = release_details[release.version.to_s]&.fetch(:release_date, nil)
  hydrated_release = build_release_with_date(release, release_date)

  return hydrated_release if hydrated_release.released_at || !plugin?

  build_release_with_date(hydrated_release, version_release_date_fallback(release.version.to_s))
end

#google_version_detailsObject



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 196

def google_version_details
  url = Gradle::FileParser::RepositoriesFinder::GOOGLE_MAVEN_REPO
  group_id, artifact_id = group_and_artifact_ids

   = "#{Gradle::FileParser::RepositoriesFinder::GOOGLE_MAVEN_REPO}/" \
                            "#{T.must(group_id).tr('.', '/')}/" \
                            "group-index.xml"

  @google_version_details ||=
    begin
      response = Dependabot::RegistryClient.get(url: )
      Nokogiri::XML(response.body)
    end

  xpath = "/#{group_id}/#{artifact_id}"
  return unless @google_version_details.at_xpath(xpath)

  @google_version_details.at_xpath(xpath)
                         .attributes.fetch("versions")
                         .value.split(",")
                         .select { |v| version_class.correct?(v) }
                         .map { |v| version_class.new(v) }
                         .map { |version| { version: version, source_url: url } }
rescue Nokogiri::XML::XPath::SyntaxError
  nil
end

#group_and_artifact_idsObject



363
364
365
366
367
368
369
370
371
372
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 363

def group_and_artifact_ids
  if kotlin_plugin?
    [dependency.name,
     "#{Dependabot::Gradle::MetadataFinder::KOTLIN_PLUGIN_REPO_PREFIX}.#{dependency.name}.gradle.plugin"]
  elsif plugin?
    [dependency.name, "#{dependency.name}.gradle.plugin"]
  else
    dependency.name.split(":")
  end
end

#kotlin_plugin?Boolean

Returns:

  • (Boolean)


380
381
382
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 380

def kotlin_plugin?
  plugin? && dependency.requirements.any? { |r| r.fetch(:groups).include? "kotlin" }
end

#matches_dependency_version_type?(comparison_version) ⇒ Boolean

Returns:

  • (Boolean)


325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 325

def matches_dependency_version_type?(comparison_version)
  return true unless dependency.version

  current_type = T.must(dependency.version)
                  .gsub("native-mt", "native_mt")
                  .split(/[.\-]/)
                  .find do |type|
    Dependabot::Gradle::UpdateChecker::VersionFinder::TYPE_SUFFICES.find { |s| type.include?(s) }
  end

  version_type = comparison_version.to_s
                                   .gsub("native-mt", "native_mt")
                                   .split(/[.\-]/)
                                   .find do |type|
    Dependabot::Gradle::UpdateChecker::VersionFinder::TYPE_SUFFICES.find { |s| type.include?(s) }
  end

  current_type == version_type
end

#plugin?Boolean

Returns:

  • (Boolean)


375
376
377
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 375

def plugin?
  dependency.requirements.any? { |r| r.fetch(:groups).include? "plugins" }
end

#plugin_repository_detailsObject



314
315
316
317
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 314

def plugin_repository_details
  [{ "url" => Gradle::FileParser::RepositoriesFinder::GRADLE_PLUGINS_REPO, "auth_headers" => {} }] +
    dependency_repository_details
end

#plugin_version_pom_url(repository_url, version) ⇒ Object



126
127
128
129
130
131
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 126

def plugin_version_pom_url(repository_url, version)
  group_id, artifact_id = group_and_artifact_ids
  group_id = "#{Dependabot::Gradle::MetadataFinder::KOTLIN_PLUGIN_REPO_PREFIX}.#{group_id}" if kotlin_plugin?
  pom_filename = "#{artifact_id}-#{version}.pom"
  File.join(repository_url, T.must(group_id).tr(".", "/"), artifact_id, version, pom_filename)
end

#pomObject



346
347
348
349
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 346

def pom
  filename = T.must(dependency.requirements.first).fetch(:file)
  dependency_files.find { |f| f.name == filename }
end

#release_detailsObject



109
110
111
112
113
114
115
116
117
118
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 109

def release_details
  @release_details ||= begin
    extractor = ReleaseDateExtractor.new(dependency_name: dependency.name, version_class: version_class)
    extractor.extract(
      repositories: repositories,
      dependency_metadata_fetcher: ->(repo) { (repo) },
      release_info_metadata_fetcher: ->(repo) { (repo) }
    )
  end
end

#release_info_metadata(repository_details) ⇒ Object



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 246

def (repository_details)
  @release_info_metadata ||= T.let({}, T.nilable(T::Hash[Integer, T.untyped]))
  @release_info_metadata[repository_details.hash] ||=
    begin
      response = Dependabot::RegistryClient.get(
        url: (repository_details.fetch("url")).gsub("maven-metadata.xml", ""),
        headers: repository_details.fetch("auth_headers")
      )

      check_response(response, repository_details.fetch("url"))
      Nokogiri::HTML(response.body)
    rescue URI::InvalidURIError
      Nokogiri::HTML("")
    rescue Excon::Error::Socket, Excon::Error::Timeout,
           Excon::Error::TooManyRedirects
      raise if central_repo_urls.include?(repository_details["url"])

      Nokogiri::HTML("")
    end
end

#repositoriesObject



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 157

def repositories
  return @repositories if @repositories

  details = if distribution?
              distribution_repository_details
            elsif plugin?
              plugin_repository_details + credentials_repository_details
            else
              dependency_repository_details + credentials_repository_details
            end

  @repositories =
    details.reject do |repo|
      next if repo["auth_headers"]

      details.any? { |r| r["url"] == repo["url"] && r["auth_headers"] != {} }
    end
end

#repository_urlsObject



268
269
270
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 268

def repository_urls
  plugin? ? plugin_repository_details : dependency_repository_details
end

#version_classObject



409
410
411
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 409

def version_class
  dependency.version_class
end

#version_release_date_fallback(version) ⇒ Object



121
122
123
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 121

def version_release_date_fallback(version)
  version_release_date_fallback_fetcher.fetch(version)
end

#version_release_date_fallback_fetcherObject



390
391
392
393
394
395
396
397
# File 'lib/dependabot/gradle/package/package_details_fetcher.rb', line 390

def version_release_date_fallback_fetcher
  @version_release_date_fallback_fetcher ||= VersionReleaseDateFallbackFetcher.new(
    dependency_name: dependency.name,
    repositories: repositories,
    forbidden_urls: T.must(forbidden_urls),
    pom_url_builder: ->(repository_url, version) { plugin_version_pom_url(repository_url, version) }
  )
end