class Dependabot::NpmAndYarn::UpdateChecker::RegistryFinder

Constants

CENTRAL_REGISTRIES
NPM_AUTH_TOKEN_REGEX
NPM_GLOBAL_REGISTRY_REGEX
YARN_GLOBAL_REGISTRY_REGEX

Attributes

credentials[R]
dependency[R]
npmrc_file[R]
yarnrc_file[R]

Public Class Methods

central_registry?(registry) click to toggle source
# File lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb, line 43
def self.central_registry?(registry)
  CENTRAL_REGISTRIES.any? do |r|
    r.include?(registry)
  end
end
new(dependency:, credentials:, npmrc_file: nil, yarnrc_file: nil) click to toggle source
# File lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb, line 23
def initialize(dependency:, credentials:, npmrc_file: nil,
               yarnrc_file: nil)
  @dependency = dependency
  @credentials = credentials
  @npmrc_file = npmrc_file
  @yarnrc_file = yarnrc_file
end

Public Instance Methods

auth_headers() click to toggle source
# File lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb, line 35
def auth_headers
  auth_header_for(auth_token)
end
dependency_url() click to toggle source
# File lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb, line 39
def dependency_url
  "#{registry_url.gsub(%r{/+$}, '')}/#{escaped_dependency_name}"
end
registry() click to toggle source
# File lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb, line 31
def registry
  locked_registry || first_registry_with_dependency_details
end

Private Instance Methods

auth_header_for(token) click to toggle source
# File lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb, line 85
def auth_header_for(token)
  return {} unless token

  if token.include?(":")
    encoded_token = Base64.encode64(token).delete("\n")
    { "Authorization" => "Basic #{encoded_token}" }
  elsif Base64.decode64(token).ascii_only? &&
        Base64.decode64(token).include?(":")
    { "Authorization" => "Basic #{token.delete("\n")}" }
  else
    { "Authorization" => "Bearer #{token}" }
  end
end
auth_token() click to toggle source
# File lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb, line 99
def auth_token
  known_registries.
    find { |cred| cred["registry"] == registry }&.
    fetch("token", nil)
end
escaped_dependency_name() click to toggle source

npm registries expect slashes to be escaped

# File lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb, line 220
def escaped_dependency_name
  dependency.name.gsub("/", "%2F")
end
first_registry_with_dependency_details() click to toggle source
# File lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb, line 53
def first_registry_with_dependency_details
  @first_registry_with_dependency_details ||=
    known_registries.find do |details|
      response = Excon.get(
        "https://#{details['registry'].gsub(%r{/+$}, '')}/"\
        "#{escaped_dependency_name}",
        idempotent: true,
        **SharedHelpers.excon_defaults(
          headers: auth_header_for(details["token"])
        )
      )
      response.status < 400 && JSON.parse(response.body)
    rescue Excon::Error::Timeout,
           Excon::Error::Socket,
           JSON::ParserError
      nil
    end&.fetch("registry")

  @first_registry_with_dependency_details ||= global_registry
end
global_registry() click to toggle source
# File lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb, line 197
def global_registry
  npmrc_file&.content.to_s.scan(NPM_GLOBAL_REGISTRY_REGEX) do
    next if Regexp.last_match[:registry].include?("${")

    registry = Regexp.last_match[:registry].strip.
               sub(%r{/+$}, "").
               sub(%r{^.*?//}, "")
    return registry
  end

  yarnrc_file&.content.to_s.scan(YARN_GLOBAL_REGISTRY_REGEX) do
    next if Regexp.last_match[:registry].include?("${")

    registry = Regexp.last_match[:registry].strip.
               sub(%r{/+$}, "").
               sub(%r{^.*?//}, "")
    return registry
  end

  "registry.npmjs.org"
end
known_registries() click to toggle source
# File lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb, line 120
def known_registries
  @known_registries ||=
    begin
      registries = []
      registries += credentials.
                    select { |cred| cred["type"] == "npm_registry" }.
                    tap { |arr| arr.each { |c| c["token"] ||= nil } }
      registries += npmrc_registries
      registries += yarnrc_registries

      unique_registries(registries)
    end
end
locked_registry() click to toggle source
# File lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb, line 105
def locked_registry
  return unless registry_source_url

  lockfile_registry =
    registry_source_url.
    gsub("https://", "").
    gsub("http://", "")
  detailed_registry =
    known_registries.
    find { |h| h["registry"].include?(lockfile_registry) }&.
    fetch("registry")

  detailed_registry || lockfile_registry
end
npmrc_registries() click to toggle source
# File lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb, line 134
def npmrc_registries
  return [] unless npmrc_file

  registries = []
  npmrc_file.content.scan(NPM_AUTH_TOKEN_REGEX) do
    next if Regexp.last_match[:registry].include?("${")

    registries << {
      "type" => "npm_registry",
      "registry" => Regexp.last_match[:registry],
      "token" => Regexp.last_match[:token]&.strip
    }
  end

  npmrc_file.content.scan(NPM_GLOBAL_REGISTRY_REGEX) do
    next if Regexp.last_match[:registry].include?("${")

    registry = Regexp.last_match[:registry].strip.
               sub(%r{/+$}, "").
               sub(%r{^.*?//}, "")
    next if registries.map { |r| r["registry"] }.include?(registry)

    registries << {
      "type" => "npm_registry",
      "registry" => registry,
      "token" => nil
    }
  end

  registries
end
registry_source_url() click to toggle source
# File lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb, line 224
def registry_source_url
  sources = dependency.requirements.
            map { |r| r.fetch(:source) }.uniq.compact.
            sort_by { |source| self.class.central_registry?(source[:url]) ? 1 : 0 }

  sources.find { |s| s[:type] == "registry" }&.fetch(:url)
end
registry_url() click to toggle source
# File lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb, line 74
def registry_url
  protocol =
    if registry_source_url
      registry_source_url.split("://").first
    else
      "https"
    end

  "#{protocol}://#{registry}"
end
unique_registries(registries) click to toggle source
# File lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb, line 186
def unique_registries(registries)
  registries.uniq.reject do |registry|
    next if registry["token"]

    # Reject this entry if an identical one with a token exists
    registries.any? do |r|
      r["token"] && r["registry"] == registry["registry"]
    end
  end
end
yarnrc_registries() click to toggle source
# File lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb, line 166
def yarnrc_registries
  return [] unless yarnrc_file

  registries = []
  yarnrc_file.content.scan(YARN_GLOBAL_REGISTRY_REGEX) do
    next if Regexp.last_match[:registry].include?("${")

    registry = Regexp.last_match[:registry].strip.
               sub(%r{/+$}, "").
               sub(%r{^.*?//}, "")
    registries << {
      "type" => "npm_registry",
      "registry" => registry,
      "token" => nil
    }
  end

  registries
end