brew/Library/Homebrew/utils/shared_audits.rb

208 lines
7.3 KiB
Ruby
Raw Normal View History

# typed: strict
2020-04-23 21:16:17 +02:00
# frozen_string_literal: true
require "utils/curl"
require "utils/github/api"
2020-04-23 21:16:17 +02:00
2020-08-26 09:42:39 +02:00
# Auditing functions for rules common to both casks and formulae.
2020-04-23 21:16:17 +02:00
module SharedAudits
URL_TYPE_HOMEPAGE = "homepage URL"
2020-04-23 21:16:17 +02:00
module_function
sig { params(product: String, cycle: String).returns(T.nilable(T::Hash[String, T::Hash[Symbol, T.untyped]])) }
def eol_data(product, cycle)
@eol_data ||= T.let({}, T.nilable(T::Hash[String, T::Hash[String, T.untyped]]))
@eol_data["#{product}/#{cycle}"] ||= begin
out, _, status = Utils::Curl.curl_output("--location", "https://endoflife.date/api/#{product}/#{cycle}.json")
json = JSON.parse(out) if status.success?
json = nil if json&.dig("message")&.include?("Product not found")
json
end
end
sig { params(user: String, repo: String).returns(T.nilable(T::Hash[String, T.untyped])) }
2020-07-20 21:52:35 +02:00
def github_repo_data(user, repo)
@github_repo_data ||= T.let({}, T.nilable(T::Hash[String, T.untyped]))
2020-07-20 21:52:35 +02:00
@github_repo_data["#{user}/#{repo}"] ||= GitHub.repository(user, repo)
2020-07-22 14:21:06 +02:00
@github_repo_data["#{user}/#{repo}"]
2021-02-15 21:48:21 +05:30
rescue GitHub::API::HTTPNotFoundError
2020-07-20 21:52:35 +02:00
nil
rescue GitHub::API::AuthenticationFailedError => e
raise unless e.message.match?(GitHub::API::GITHUB_IP_ALLOWLIST_ERROR)
2020-07-20 21:52:35 +02:00
end
sig { params(user: String, repo: String, tag: String).returns(T.nilable(T::Hash[String, T.untyped])) }
2020-08-13 16:17:47 +02:00
def github_release_data(user, repo, tag)
id = "#{user}/#{repo}/#{tag}"
url = "#{GitHub::API_URL}/repos/#{user}/#{repo}/releases/tags/#{tag}"
@github_release_data ||= T.let({}, T.nilable(T::Hash[String, T.untyped]))
@github_release_data[id] ||= GitHub::API.open_rest(url)
2020-08-13 16:17:47 +02:00
@github_release_data[id]
2021-02-15 21:48:21 +05:30
rescue GitHub::API::HTTPNotFoundError
2020-08-13 16:17:47 +02:00
nil
rescue GitHub::API::AuthenticationFailedError => e
raise unless e.message.match?(GitHub::API::GITHUB_IP_ALLOWLIST_ERROR)
2020-08-13 16:17:47 +02:00
end
sig {
params(
user: String, repo: String, tag: String, formula: T.nilable(Formula), cask: T.nilable(Cask::Cask),
).returns(
T.nilable(String),
)
}
def github_release(user, repo, tag, formula: nil, cask: nil)
release = github_release_data(user, repo, tag)
return unless release
exception, name, version = if formula
2021-10-04 21:45:20 -04:00
[formula.tap&.audit_exception(:github_prerelease_allowlist, formula.name), formula.name, formula.version]
elsif cask
2021-10-04 21:45:20 -04:00
[cask.tap&.audit_exception(:github_prerelease_allowlist, cask.token), cask.token, cask.version]
end
return "#{tag} is a GitHub pre-release." if release["prerelease"] && [version, "all"].exclude?(exception)
2020-12-24 13:36:18 -05:00
if !release["prerelease"] && exception
return "#{tag} is not a GitHub pre-release but '#{name}' is in the GitHub prerelease allowlist."
end
"#{tag} is a GitHub draft." if release["draft"]
end
sig { params(user: String, repo: String).returns(T.nilable(T::Hash[String, T.untyped])) }
2020-07-20 21:52:35 +02:00
def gitlab_repo_data(user, repo)
@gitlab_repo_data ||= T.let({}, T.nilable(T::Hash[String, T.untyped]))
2020-07-20 21:52:35 +02:00
@gitlab_repo_data["#{user}/#{repo}"] ||= begin
out, _, status = Utils::Curl.curl_output("https://gitlab.com/api/v4/projects/#{user}%2F#{repo}")
json = JSON.parse(out) if status.success?
json = nil if json&.dig("message")&.include?("404 Project Not Found")
json
2020-04-23 21:16:17 +02:00
end
2020-07-20 21:52:35 +02:00
end
sig { params(user: String, repo: String, tag: String).returns(T.nilable(T::Hash[String, T.untyped])) }
2020-08-13 16:17:47 +02:00
def gitlab_release_data(user, repo, tag)
id = "#{user}/#{repo}/#{tag}"
@gitlab_release_data ||= T.let({}, T.nilable(T::Hash[String, T.untyped]))
2020-08-13 16:17:47 +02:00
@gitlab_release_data[id] ||= begin
out, _, status = Utils::Curl.curl_output(
"https://gitlab.com/api/v4/projects/#{user}%2F#{repo}/releases/#{tag}", "--fail"
2020-08-13 16:17:47 +02:00
)
JSON.parse(out) if status.success?
2020-08-13 16:17:47 +02:00
end
end
sig {
params(
user: String, repo: String, tag: String, formula: T.nilable(Formula), cask: T.nilable(Cask::Cask),
).returns(
T.nilable(String),
)
}
2020-12-21 12:53:12 -05:00
def gitlab_release(user, repo, tag, formula: nil, cask: nil)
2020-09-05 06:07:55 +02:00
release = gitlab_release_data(user, repo, tag)
return unless release
return if DateTime.parse(release["released_at"]) <= DateTime.now
2020-12-21 12:53:12 -05:00
exception, version = if formula
2021-10-04 21:45:20 -04:00
[formula.tap&.audit_exception(:gitlab_prerelease_allowlist, formula.name), formula.version]
2020-12-21 12:53:12 -05:00
elsif cask
2021-10-04 21:45:20 -04:00
[cask.tap&.audit_exception(:gitlab_prerelease_allowlist, cask.token), cask.version]
2020-12-21 12:53:12 -05:00
end
return if [version, "all"].include?(exception)
2020-09-05 06:07:55 +02:00
"#{tag} is a GitLab pre-release."
end
sig { params(user: String, repo: String).returns(T.nilable(String)) }
2020-07-20 21:52:35 +02:00
def github(user, repo)
metadata = github_repo_data(user, repo)
2020-04-23 21:16:17 +02:00
return if metadata.nil?
2020-12-21 12:53:12 -05:00
return "GitHub fork (not canonical repository)" if metadata["fork"]
2020-09-05 19:08:44 +02:00
2020-04-23 21:16:17 +02:00
if (metadata["forks_count"] < 30) && (metadata["subscribers_count"] < 30) &&
(metadata["stargazers_count"] < 75)
return "GitHub repository not notable enough (<30 forks, <30 watchers and <75 stars)"
end
return if Date.parse(metadata["created_at"]) <= (Date.today - 30)
"GitHub repository too new (<30 days old)"
end
sig { params(user: String, repo: String).returns(T.nilable(String)) }
2020-04-23 21:16:17 +02:00
def gitlab(user, repo)
2020-07-20 21:52:35 +02:00
metadata = gitlab_repo_data(user, repo)
2020-04-23 21:16:17 +02:00
return if metadata.nil?
return "GitLab fork (not canonical repository)" if metadata["fork"]
if (metadata["forks_count"] < 30) && (metadata["star_count"] < 75)
return "GitLab repository not notable enough (<30 forks and <75 stars)"
end
return if Date.parse(metadata["created_at"]) <= (Date.today - 30)
"GitLab repository too new (<30 days old)"
end
sig { params(user: String, repo: String).returns(T.nilable(String)) }
2020-04-23 21:16:17 +02:00
def bitbucket(user, repo)
api_url = "https://api.bitbucket.org/2.0/repositories/#{user}/#{repo}"
out, _, status = Utils::Curl.curl_output("--request", "GET", api_url)
2020-04-23 21:16:17 +02:00
return unless status.success?
metadata = JSON.parse(out)
return if metadata.nil?
2024-02-04 15:10:14 +01:00
return "Uses deprecated Mercurial support in Bitbucket" if metadata["scm"] == "hg"
2020-04-23 21:16:17 +02:00
return "Bitbucket fork (not canonical repository)" unless metadata["parent"].nil?
return "Bitbucket repository too new (<30 days old)" if Date.parse(metadata["created_on"]) >= (Date.today - 30)
forks_out, _, forks_status = Utils::Curl.curl_output("--request", "GET", "#{api_url}/forks")
2020-04-23 21:16:17 +02:00
return unless forks_status.success?
watcher_out, _, watcher_status = Utils::Curl.curl_output("--request", "GET", "#{api_url}/watchers")
2020-04-23 21:16:17 +02:00
return unless watcher_status.success?
forks_metadata = JSON.parse(forks_out)
return if forks_metadata.nil?
watcher_metadata = JSON.parse(watcher_out)
return if watcher_metadata.nil?
return if forks_metadata["size"] >= 30 || watcher_metadata["size"] >= 75
2020-04-23 21:16:17 +02:00
"Bitbucket repository not notable enough (<30 forks and <75 watchers)"
end
2020-09-09 08:57:56 -07:00
sig { params(url: String).returns(T.nilable(String)) }
2020-09-09 08:57:56 -07:00
def github_tag_from_url(url)
url = url.to_s
tag = url.match(%r{^https://github\.com/[\w-]+/[\w-]+/archive/refs/tags/([^/]+)\.(tar\.gz|zip)$})
2020-09-09 08:57:56 -07:00
.to_a
.second
tag ||= url.match(%r{^https://github\.com/[\w-]+/[\w-]+/releases/download/([^/]+)/})
.to_a
.second
tag
end
sig { params(url: String).returns(T.nilable(String)) }
2020-09-09 08:57:56 -07:00
def gitlab_tag_from_url(url)
url = url.to_s
url.match(%r{^https://gitlab\.com/[\w-]+/[\w-]+/-/archive/([^/]+)/})
.to_a
.second
end
2020-04-23 21:16:17 +02:00
end