class Dependabot::Uv::Package::PackageRegistryFinder
def authed_base_url(base_url)
def authed_base_url(base_url) cred = credential_for(base_url) return base_url unless cred builder = AuthedUrlBuilder.authed_url(credential: cred) return base_url unless builder builder.gsub(%r{/*$}, "") + "/" end
def clean_check_and_remove_environment_variables(url)
def clean_check_and_remove_environment_variables(url) url = url.strip.sub(%r{/+$}, "") + "/" return authed_base_url(url) unless url.match?(ENVIRONMENT_VARIABLE_REGEX) config_variable_urls = [ config_variable_index_urls[:main], *config_variable_index_urls[:extra] ] .compact .map { |u| u.strip.gsub(%r{/*$}, "") + "/" } regexp = url .sub(%r{(?<=://).+@}, "") .sub(%r{https?://}, "") .split(ENVIRONMENT_VARIABLE_REGEX) .map { |part| Regexp.quote(part) } .join(".+") authed_url = config_variable_urls.find { |u| u.match?(regexp) } return authed_url if authed_url cleaned_url = url.gsub(%r{#{ENVIRONMENT_VARIABLE_REGEX}/?}o, "") authed_url = authed_base_url(cleaned_url) return authed_url if credential_for(cleaned_url) raise PrivateSourceAuthenticationFailure, url end
def config_variable_index_urls
def config_variable_index_urls urls = { main: T.let(nil, T.nilable(String)), extra: [] } index_url_creds = credentials .select { |cred| cred["type"] == "python_index" } if (main_cred = index_url_creds.find(&:replaces_base?)) urls[:main] = AuthedUrlBuilder.authed_url(credential: main_cred) end urls[:extra] = index_url_creds .reject(&:replaces_base?) .map { |cred| AuthedUrlBuilder.authed_url(credential: cred) } urls end
def credential_for(url)
def credential_for(url) credentials .select { |c| c["type"] == "python_index" } .find do |c| cred_url = c.fetch("index-url").gsub(%r{/*$}, "") + "/" cred_url.include?(url) end end
def initialize(dependency_files:, credentials:, dependency:)
def initialize(dependency_files:, credentials:, dependency:) @dependency_files = dependency_files @credentials = credentials @dependency = dependency end
def main_index_url
def main_index_url url = config_variable_index_urls[:main] || pipfile_index_urls[:main] || requirement_file_index_urls[:main] || pip_conf_index_urls[:main] || pyproject_index_urls[:main] || PYPI_BASE_URL clean_check_and_remove_environment_variables(url) end
def pip_compile_files
def pip_compile_files dependency_files.select { |f| f.name.end_with?(".in") } end
def pip_conf
def pip_conf dependency_files.find { |f| f.name == "pip.conf" } end
def pip_conf_index_urls
def pip_conf_index_urls urls = { main: nil, extra: [] } return urls unless pip_conf content = pip_conf.content if content.match?(/^index-url\s*=/x) urls[:main] = content.match(/^index-url\s*=\s*(.+)/) .captures.first end urls[:extra] += content.scan(/^extra-index-url\s*=(.+)/).flatten urls end
def pipfile
def pipfile dependency_files.find { |f| f.name == "Pipfile" } end
def pipfile_index_urls
def pipfile_index_urls urls = { main: nil, extra: [] } return urls unless pipfile pipfile_object = TomlRB.parse(pipfile.content) urls[:main] = pipfile_object["source"]&.first&.fetch("url", nil) pipfile_object["source"]&.each do |source| urls[:extra] << source.fetch("url") if source["url"] end urls[:extra] = urls[:extra].uniq urls rescue TomlRB::ParseError, TomlRB::ValueOverwriteError urls end
def pyproject
def pyproject dependency_files.find { |f| f.name == "pyproject.toml" } end
def pyproject_index_urls
def pyproject_index_urls urls = { main: nil, extra: [] } return urls unless pyproject sources = TomlRB.parse(pyproject.content).dig("tool", "poetry", "source") || [] sources.each do |source| # If source is PyPI, skip it, and let it pick the default URI next if source["name"].casecmp?("PyPI") if @dependency.all_sources.include?(source["name"]) # If dependency has specified this source, use it return { main: source["url"], extra: [] } elsif source["default"] urls[:main] = source["url"] elsif source["priority"] != "explicit" # if source is not explicit, add it to extra urls[:extra] << source["url"] end end urls[:extra] = urls[:extra].uniq urls rescue TomlRB::ParseError, TomlRB::ValueOverwriteError urls end
def registry_urls
def registry_urls extra_index_urls = config_variable_index_urls[:extra] + pipfile_index_urls[:extra] + requirement_file_index_urls[:extra] + pip_conf_index_urls[:extra] + pyproject_index_urls[:extra] extra_index_urls = extra_index_urls.map do |url| clean_check_and_remove_environment_variables(url) end # URL encode any `@` characters within registry URL creds. # TODO: The test that fails if the `map` here is removed is likely a # bug in Ruby's URI parser, and should be fixed there. [main_index_url, *extra_index_urls].map do |url| url.rpartition("@").tap { |a| a.first.gsub!("@", "%40") }.join end.uniq end
def requirement_file_index_urls
def requirement_file_index_urls urls = { main: nil, extra: [] } requirements_files.each do |file| if file.content.match?(/^--index-url\s+['"]?([^\s'"]+)['"]?/) urls[:main] = file.content.match(/^--index-url\s+['"]?([^\s'"]+)['"]?/) .captures.first&.strip end urls[:extra] += file.content .scan(/^--extra-index-url\s+['"]?([^\s'"]+)['"]?/) .flatten .map(&:strip) end urls end
def requirements_files
def requirements_files dependency_files.select { |f| f.name.match?(/requirements/x) } end