2020-11-25 17:03:23 +01:00
|
|
|
# typed: true
|
2020-11-18 10:25:12 +01:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
module Homebrew
|
|
|
|
# Auditor for checking common violations in {Resource}s.
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
class ResourceAuditor
|
|
|
|
attr_reader :name, :version, :checksum, :url, :mirrors, :using, :specs, :owner, :spec_name, :problems
|
|
|
|
|
|
|
|
def initialize(resource, spec_name, options = {})
|
|
|
|
@name = resource.name
|
|
|
|
@version = resource.version
|
|
|
|
@checksum = resource.checksum
|
|
|
|
@url = resource.url
|
|
|
|
@mirrors = resource.mirrors
|
|
|
|
@using = resource.using
|
|
|
|
@specs = resource.specs
|
|
|
|
@owner = resource.owner
|
|
|
|
@spec_name = spec_name
|
|
|
|
@online = options[:online]
|
|
|
|
@strict = options[:strict]
|
2021-06-15 09:55:28 -04:00
|
|
|
@only = options[:only]
|
|
|
|
@except = options[:except]
|
2021-07-26 12:39:25 +02:00
|
|
|
@use_homebrew_curl = options[:use_homebrew_curl]
|
|
|
|
@problems = []
|
2020-11-18 10:25:12 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def audit
|
2021-06-15 09:55:28 -04:00
|
|
|
only_audits = @only
|
|
|
|
except_audits = @except
|
|
|
|
|
|
|
|
methods.map(&:to_s).grep(/^audit_/).each do |audit_method_name|
|
|
|
|
name = audit_method_name.delete_prefix("audit_")
|
|
|
|
next if only_audits&.exclude?(name)
|
|
|
|
next if except_audits&.include?(name)
|
|
|
|
|
|
|
|
send(audit_method_name)
|
|
|
|
end
|
|
|
|
|
2020-11-18 10:25:12 +01:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
def audit_version
|
|
|
|
if version.nil?
|
|
|
|
problem "missing version"
|
|
|
|
elsif !version.detected_from_url?
|
|
|
|
version_text = version
|
|
|
|
version_url = Version.detect(url, **specs)
|
|
|
|
if version_url.to_s == version_text.to_s && version.instance_of?(Version)
|
|
|
|
problem "version #{version_text} is redundant with version scanned from URL"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def audit_download_strategy
|
|
|
|
url_strategy = DownloadStrategyDetector.detect(url)
|
|
|
|
|
|
|
|
if (using == :git || url_strategy == GitDownloadStrategy) && specs[:tag] && !specs[:revision]
|
|
|
|
problem "Git should specify :revision when a :tag is specified."
|
|
|
|
end
|
|
|
|
|
|
|
|
return unless using
|
|
|
|
|
|
|
|
if using == :cvs
|
|
|
|
mod = specs[:module]
|
|
|
|
|
|
|
|
problem "Redundant :module value in URL" if mod == name
|
|
|
|
|
|
|
|
if url.match?(%r{:[^/]+$})
|
|
|
|
mod = url.split(":").last
|
|
|
|
|
|
|
|
if mod == name
|
|
|
|
problem "Redundant CVS module appended to URL"
|
|
|
|
else
|
|
|
|
problem "Specify CVS module as `:module => \"#{mod}\"` instead of appending it to the URL"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-04-18 15:06:50 -07:00
|
|
|
return if url_strategy != DownloadStrategyDetector.detect("", using)
|
2020-11-18 10:25:12 +01:00
|
|
|
|
|
|
|
problem "Redundant :using value in URL"
|
|
|
|
end
|
|
|
|
|
2020-12-08 23:26:52 +00:00
|
|
|
def audit_checksum
|
|
|
|
return if spec_name == :head
|
2023-04-18 15:07:38 -07:00
|
|
|
# rubocop:disable Style/InvertibleUnlessCondition (non-invertible)
|
|
|
|
return unless DownloadStrategyDetector.detect(url, using) <= CurlDownloadStrategy
|
|
|
|
# rubocop:enable Style/InvertibleUnlessCondition
|
2020-12-08 23:26:52 +00:00
|
|
|
|
|
|
|
problem "Checksum is missing" if checksum.blank?
|
|
|
|
end
|
|
|
|
|
2021-10-01 14:20:39 +01:00
|
|
|
def self.curl_deps
|
|
|
|
@curl_deps ||= begin
|
|
|
|
["curl"] + Formula["curl"].recursive_dependencies.map(&:name).uniq
|
2020-11-18 10:25:12 +01:00
|
|
|
rescue FormulaUnavailableError
|
|
|
|
[]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def audit_urls
|
2021-10-01 14:20:39 +01:00
|
|
|
urls = [url] + mirrors
|
|
|
|
|
|
|
|
curl_dep = self.class.curl_deps.include?(owner.name)
|
|
|
|
# Ideally `ca-certificates` would not be excluded here, but sourcing a HTTP mirror was tricky.
|
|
|
|
# Instead, we have logic elsewhere to pass `--insecure` to curl when downloading the certs.
|
|
|
|
# TODO: try remove the OS/env conditional
|
2022-07-28 14:52:19 -04:00
|
|
|
if Homebrew::SimulateSystem.simulating_or_running_on_macos? && spec_name == :stable &&
|
2021-10-01 14:20:39 +01:00
|
|
|
owner.name != "ca-certificates" && curl_dep && !urls.find { |u| u.start_with?("http://") }
|
|
|
|
problem "should always include at least one HTTP mirror"
|
|
|
|
end
|
|
|
|
|
2020-11-18 10:25:12 +01:00
|
|
|
return unless @online
|
|
|
|
|
|
|
|
urls.each do |url|
|
|
|
|
next if !@strict && mirrors.include?(url)
|
|
|
|
|
|
|
|
strategy = DownloadStrategyDetector.detect(url, using)
|
|
|
|
if strategy <= CurlDownloadStrategy && !url.start_with?("file")
|
2021-07-26 12:39:25 +02:00
|
|
|
|
|
|
|
raise HomebrewCurlDownloadStrategyError, url if
|
|
|
|
strategy <= HomebrewCurlDownloadStrategy && !Formula["curl"].any_version_installed?
|
|
|
|
|
2023-09-04 22:17:57 -04:00
|
|
|
if (http_content_problem = Utils::Curl.curl_check_http_content(
|
|
|
|
url,
|
|
|
|
"source URL",
|
|
|
|
specs: specs,
|
|
|
|
use_homebrew_curl: @use_homebrew_curl,
|
|
|
|
))
|
2020-11-18 10:25:12 +01:00
|
|
|
problem http_content_problem
|
|
|
|
end
|
|
|
|
elsif strategy <= GitDownloadStrategy
|
|
|
|
problem "The URL #{url} is not a valid git URL" unless Utils::Git.remote_exists? url
|
|
|
|
elsif strategy <= SubversionDownloadStrategy
|
|
|
|
next unless DevelopmentTools.subversion_handles_most_https_certificates?
|
|
|
|
next unless Utils::Svn.available?
|
|
|
|
|
|
|
|
problem "The URL #{url} is not a valid svn URL" unless Utils::Svn.remote_exists? url
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-07-14 23:23:08 +05:30
|
|
|
def audit_head_branch
|
2021-07-23 15:33:44 +05:30
|
|
|
return unless @online
|
|
|
|
return unless @strict
|
2021-08-12 19:28:06 +05:30
|
|
|
return if spec_name != :head
|
2021-07-23 15:33:44 +05:30
|
|
|
return unless Utils::Git.remote_exists?(url)
|
2021-10-20 11:50:52 -04:00
|
|
|
return if specs[:tag].present?
|
2021-07-14 23:23:08 +05:30
|
|
|
|
|
|
|
branch = Utils.popen_read("git", "ls-remote", "--symref", url, "HEAD")
|
2022-10-17 13:06:52 -04:00
|
|
|
.match(%r{ref: refs/heads/(.*?)\s+HEAD})&.to_a&.second
|
|
|
|
return if branch.blank? || branch == specs[:branch]
|
2021-07-14 23:23:08 +05:30
|
|
|
|
2021-07-23 15:33:44 +05:30
|
|
|
problem "Use `branch: \"#{branch}\"` to specify the default branch"
|
2021-07-14 23:23:08 +05:30
|
|
|
end
|
|
|
|
|
2020-11-18 10:25:12 +01:00
|
|
|
def problem(text)
|
|
|
|
@problems << text
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|