2020-10-10 14:16:11 +02:00
# typed: false
2019-04-19 15:38:03 +09:00
# frozen_string_literal: true
2020-06-06 21:10:16 +01:00
require " cask/denylist "
2018-09-03 19:39:07 +01:00
require " cask/download "
2016-08-18 22:11:42 +03:00
require " digest "
2020-12-14 14:30:36 +01:00
require " livecheck/livecheck "
2018-10-10 21:36:02 +00:00
require " utils/curl "
2017-05-07 06:41:40 +02:00
require " utils/git "
2020-08-26 09:42:39 +02:00
require " utils/shared_audits "
2016-08-18 22:11:42 +03:00
2018-09-06 08:29:14 +02:00
module Cask
2020-08-24 21:32:40 +02:00
# Audit a cask for various problems.
#
# @api private
2016-09-24 13:52:43 +02:00
class Audit
2020-10-20 12:03:48 +02:00
extend T :: Sig
2019-05-07 17:06:54 +02:00
extend Predicable
2016-08-18 22:11:42 +03:00
2020-09-04 04:14:37 +02:00
attr_reader :cask , :download
2016-08-18 22:11:42 +03:00
2020-09-04 05:29:56 +02:00
attr_predicate :appcast? , :new_cask? , :strict? , :online? , :token_conflicts?
2019-05-07 17:06:54 +02:00
2020-09-04 04:14:37 +02:00
def initialize ( cask , appcast : nil , download : nil , quarantine : nil ,
token_conflicts : nil , online : nil , strict : nil ,
new_cask : nil )
# `new_cask` implies `online` and `strict`
online = new_cask if online . nil?
strict = new_cask if strict . nil?
# `online` implies `appcast` and `download`
appcast = online if appcast . nil?
download = online if download . nil?
2020-09-14 02:55:47 +02:00
# `new_cask` implies `token_conflicts`
token_conflicts = new_cask if token_conflicts . nil?
2020-09-04 04:14:37 +02:00
2016-09-24 13:52:43 +02:00
@cask = cask
2020-04-23 21:16:17 +02:00
@appcast = appcast
2020-07-21 19:05:55 +02:00
@download = Download . new ( cask , quarantine : quarantine ) if download
2020-04-23 21:16:17 +02:00
@online = online
@strict = strict
@new_cask = new_cask
@token_conflicts = token_conflicts
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
2016-09-24 13:52:43 +02:00
def run!
2020-06-06 21:10:16 +01:00
check_denylist
2021-01-26 01:16:00 -08:00
check_reverse_migration
2016-09-24 13:52:43 +02:00
check_required_stanzas
check_version
check_sha256
2020-08-10 19:34:38 +02:00
check_desc
2016-09-24 13:52:43 +02:00
check_url
2020-09-08 22:12:26 +08:00
check_unnecessary_verified
check_missing_verified
check_no_match
2016-09-24 13:52:43 +02:00
check_generic_artifacts
2020-06-04 23:11:51 +02:00
check_token_valid
check_token_bad_words
2016-09-24 13:52:43 +02:00
check_token_conflicts
2020-06-04 23:37:54 +02:00
check_languages
2016-09-24 13:52:43 +02:00
check_download
2018-10-10 21:36:02 +00:00
check_https_availability
2017-10-30 20:47:22 -03:00
check_single_pre_postflight
2017-10-27 16:53:22 -03:00
check_single_uninstall_zap
2018-03-25 15:30:16 +10:00
check_untrusted_pkg
2018-06-05 16:42:15 +10:00
check_hosting_with_appcast
2021-01-08 03:48:53 +01:00
check_appcast_and_livecheck
2020-12-14 14:30:36 +01:00
check_latest_with_appcast_or_livecheck
2018-07-12 16:13:46 +10:00
check_latest_with_auto_updates
2018-05-19 12:38:47 +10:00
check_stanza_requires_uninstall
2019-05-07 17:06:54 +02:00
check_appcast_contains_version
2020-12-14 14:30:36 +01:00
check_livecheck_version
2020-04-23 21:16:17 +02:00
check_gitlab_repository
2020-08-13 16:17:47 +02:00
check_gitlab_repository_archived
check_gitlab_prerelease_version
check_github_repository
check_github_repository_archived
check_github_prerelease_version
2020-04-23 21:16:17 +02:00
check_bitbucket_repository
2016-09-24 13:52:43 +02:00
self
2018-09-02 20:14:54 +01:00
rescue = > e
2020-07-06 15:29:15 -04:00
odebug e , e . backtrace
2016-09-24 13:52:43 +02:00
add_error " exception while auditing #{ cask } : #{ e . message } "
self
end
2016-08-18 22:11:42 +03:00
2020-07-28 09:08:37 +02:00
def errors
@errors || = [ ]
end
def warnings
@warnings || = [ ]
end
def add_error ( message )
errors << message
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
2020-07-28 09:08:37 +02:00
def add_warning ( message )
2020-09-14 02:55:47 +02:00
if strict?
add_error message
else
warnings << message
end
2020-07-28 09:08:37 +02:00
end
def errors?
errors . any?
end
def warnings?
warnings . any?
end
def result
if errors?
Formatter . error ( " failed " )
elsif warnings?
Formatter . warning ( " warning " )
else
Formatter . success ( " passed " )
end
end
2020-10-20 12:03:48 +02:00
sig { returns ( String ) }
2020-07-28 09:08:37 +02:00
def summary
summary = [ " audit for #{ cask } : #{ result } " ]
errors . each do | error |
summary << " #{ Formatter . error ( " - " ) } #{ error } "
end
warnings . each do | warning |
summary << " #{ Formatter . warning ( " - " ) } #{ warning } "
end
summary . join ( " \n " )
end
def success?
! ( errors? || warnings? )
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
2016-09-24 13:52:43 +02:00
private
2016-08-18 22:11:42 +03:00
2018-03-25 15:30:16 +10:00
def check_untrusted_pkg
odebug " Auditing pkg stanza: allow_untrusted "
return if @cask . sourcefile_path . nil?
tap = @cask . tap
2018-05-15 16:52:10 +02:00
return if tap . nil?
2018-05-25 18:03:16 +02:00
return if tap . user != " Homebrew "
2018-03-25 15:30:16 +10:00
2021-01-07 13:49:05 -08:00
return if cask . artifacts . none? { | k | k . is_a? ( Artifact :: Pkg ) && k . stanza_options . key? ( :allow_untrusted ) }
2018-09-17 02:45:00 +02:00
2020-09-14 01:26:06 +02:00
add_error " allow_untrusted is not permitted in official Homebrew Cask taps "
2018-03-25 15:30:16 +10:00
end
2018-05-19 12:38:47 +10:00
def check_stanza_requires_uninstall
odebug " Auditing stanzas which require an uninstall "
2018-09-06 06:47:29 +02:00
return if cask . artifacts . none? { | k | k . is_a? ( Artifact :: Pkg ) || k . is_a? ( Artifact :: Installer ) }
return if cask . artifacts . any? { | k | k . is_a? ( Artifact :: Uninstall ) }
2018-09-17 02:45:00 +02:00
2020-09-14 01:26:06 +02:00
add_error " installer and pkg stanzas require an uninstall stanza "
2018-05-19 12:38:47 +10:00
end
2017-10-30 20:47:22 -03:00
def check_single_pre_postflight
odebug " Auditing preflight and postflight stanzas "
2018-09-06 06:47:29 +02:00
if cask . artifacts . count { | k | k . is_a? ( Artifact :: PreflightBlock ) && k . directives . key? ( :preflight ) } > 1
2020-09-14 01:26:06 +02:00
add_error " only a single preflight stanza is allowed "
2017-11-01 22:35:41 -03:00
end
2017-10-30 20:47:22 -03:00
2018-09-02 16:15:09 +01:00
count = cask . artifacts . count do | k |
2018-09-06 06:47:29 +02:00
k . is_a? ( Artifact :: PostflightBlock ) &&
2018-09-02 16:15:09 +01:00
k . directives . key? ( :postflight )
end
return unless count > 1
2020-09-14 01:26:06 +02:00
add_error " only a single postflight stanza is allowed "
2017-10-30 20:47:22 -03:00
end
2017-10-27 16:53:22 -03:00
def check_single_uninstall_zap
odebug " Auditing single uninstall_* and zap stanzas "
2018-09-06 06:47:29 +02:00
if cask . artifacts . count { | k | k . is_a? ( Artifact :: Uninstall ) } > 1
2020-09-14 01:26:06 +02:00
add_error " only a single uninstall stanza is allowed "
2017-11-01 22:35:41 -03:00
end
2017-10-27 16:53:22 -03:00
2018-09-02 16:15:09 +01:00
count = cask . artifacts . count do | k |
2018-09-06 06:47:29 +02:00
k . is_a? ( Artifact :: PreflightBlock ) &&
2018-09-02 16:15:09 +01:00
k . directives . key? ( :uninstall_preflight )
end
2020-09-14 01:26:06 +02:00
add_error " only a single uninstall_preflight stanza is allowed " if count > 1
2017-10-27 16:53:22 -03:00
2018-09-02 16:15:09 +01:00
count = cask . artifacts . count do | k |
2018-09-06 06:47:29 +02:00
k . is_a? ( Artifact :: PostflightBlock ) &&
2018-09-02 16:15:09 +01:00
k . directives . key? ( :uninstall_postflight )
end
2020-09-14 01:26:06 +02:00
add_error " only a single uninstall_postflight stanza is allowed " if count > 1
2017-10-27 16:53:22 -03:00
2018-09-06 06:47:29 +02:00
return unless cask . artifacts . count { | k | k . is_a? ( Artifact :: Zap ) } > 1
2018-09-17 02:45:00 +02:00
2020-09-14 01:26:06 +02:00
add_error " only a single zap stanza is allowed "
2017-10-27 16:53:22 -03:00
end
2016-09-24 13:52:43 +02:00
def check_required_stanzas
odebug " Auditing required stanzas "
2016-10-14 20:17:25 +02:00
[ :version , :sha256 , :url , :homepage ] . each do | sym |
2016-09-24 13:52:43 +02:00
add_error " a #{ sym } stanza is required " unless cask . send ( sym )
end
add_error " at least one name stanza is required " if cask . name . empty?
# TODO: specific DSL knowledge should not be spread around in various files like this
2020-09-11 10:29:21 +01:00
rejected_artifacts = [ :uninstall , :zap ]
installable_artifacts = cask . artifacts . reject { | k | rejected_artifacts . include? ( k ) }
2016-09-24 13:52:43 +02:00
add_error " at least one activatable artifact stanza is required " if installable_artifacts . empty?
2016-08-18 22:11:42 +03:00
end
2016-09-24 13:52:43 +02:00
def check_version
return unless cask . version
2018-09-17 02:45:00 +02:00
2016-09-24 13:52:43 +02:00
check_no_string_version_latest
2016-12-31 21:44:42 +01:00
check_no_file_separator_in_version
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
2016-09-24 13:52:43 +02:00
def check_no_string_version_latest
odebug " Verifying version :latest does not appear as a string ('latest') "
return unless cask . version . raw_version == " latest "
2018-09-17 02:45:00 +02:00
2016-09-24 13:52:43 +02:00
add_error " you should use version :latest instead of version 'latest' "
end
2016-08-18 22:11:42 +03:00
2016-12-31 21:44:42 +01:00
def check_no_file_separator_in_version
odebug " Verifying version does not contain ' #{ File :: SEPARATOR } ' "
return unless cask . version . raw_version . is_a? ( String )
return unless cask . version . raw_version . include? ( File :: SEPARATOR )
2018-09-17 02:45:00 +02:00
2016-12-31 21:44:42 +01:00
add_error " version should not contain ' #{ File :: SEPARATOR } ' "
end
2016-09-24 13:52:43 +02:00
def check_sha256
return unless cask . sha256
2018-09-17 02:45:00 +02:00
2016-09-24 13:52:43 +02:00
check_sha256_no_check_if_latest
2020-12-07 23:02:55 +01:00
check_sha256_no_check_if_unversioned
2016-09-24 13:52:43 +02:00
check_sha256_actually_256
check_sha256_invalid
end
2016-08-18 22:11:42 +03:00
2016-09-24 13:52:43 +02:00
def check_sha256_no_check_if_latest
odebug " Verifying sha256 :no_check with version :latest "
2018-09-10 19:35:08 +02:00
return unless cask . version . latest?
return if cask . sha256 == :no_check
2018-09-17 02:45:00 +02:00
2016-09-24 13:52:43 +02:00
add_error " you should use sha256 :no_check when version is :latest "
end
2016-08-18 22:11:42 +03:00
2020-12-07 23:02:55 +01:00
def check_sha256_no_check_if_unversioned
return if cask . sha256 == :no_check
2020-12-11 21:58:09 +01:00
add_error " Use `sha256 :no_check` when URL is unversioned. " if cask . url & . unversioned?
2020-12-07 23:02:55 +01:00
end
2020-11-19 18:12:16 +01:00
def check_sha256_actually_256
odebug " Verifying sha256 string is a legal SHA-256 digest "
return unless cask . sha256 . is_a? ( Checksum )
return if cask . sha256 . length == 64 && cask . sha256 [ / ^[0-9a-f]+$ /i ]
2018-09-17 02:45:00 +02:00
2020-11-19 18:12:16 +01:00
add_error " sha256 string must be of 64 hexadecimal characters "
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
2020-11-19 18:12:16 +01:00
def check_sha256_invalid
odebug " Verifying sha256 is not a known invalid value "
2016-09-24 13:52:43 +02:00
empty_sha256 = " e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 "
2020-11-19 18:12:16 +01:00
return unless cask . sha256 == empty_sha256
2018-09-17 02:45:00 +02:00
2020-11-19 18:12:16 +01:00
add_error " cannot use the sha256 for an empty string: #{ empty_sha256 } "
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
2021-01-08 03:48:53 +01:00
def check_appcast_and_livecheck
return unless cask . appcast
add_error " Cask has a `livecheck`, the `appcast` should be removed. " if cask . livecheckable?
end
2020-12-14 14:30:36 +01:00
def check_latest_with_appcast_or_livecheck
2018-03-27 20:56:01 +10:00
return unless cask . version . latest?
2021-01-08 03:48:53 +01:00
add_error " Casks with an `appcast` should not use `version :latest`. " if cask . appcast
add_error " Casks with a `livecheck` should not use `version :latest`. " if cask . livecheckable?
2018-03-27 20:56:01 +10:00
end
2018-07-12 16:13:46 +10:00
def check_latest_with_auto_updates
return unless cask . version . latest?
return unless cask . auto_updates
2021-01-08 03:48:53 +01:00
add_error " Casks with `version :latest` should not use `auto_updates`. "
2018-07-12 16:13:46 +10:00
end
2018-06-05 16:42:15 +10:00
def check_hosting_with_appcast
2020-12-18 18:15:50 +01:00
return if cask . appcast || cask . livecheckable?
2018-06-05 16:42:15 +10:00
2020-06-25 11:20:57 +01:00
add_appcast = " please add an appcast. See https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/stanzas/appcast.md "
2018-06-15 17:01:27 +10:00
case cask . url . to_s
when %r{ sourceforge.net/( \ S+) }
return if cask . version . latest?
2018-09-17 02:45:00 +02:00
2020-09-14 01:26:06 +02:00
add_error " Download is hosted on SourceForge, #{ add_appcast } "
2018-06-15 17:01:27 +10:00
when %r{ dl.devmate.com/( \ S+) }
2020-09-14 01:26:06 +02:00
add_error " Download is hosted on DevMate, #{ add_appcast } "
2018-06-15 17:01:27 +10:00
when %r{ rink.hockeyapp.net/( \ S+) }
2020-09-14 01:26:06 +02:00
add_error " Download is hosted on HockeyApp, #{ add_appcast } "
2018-06-15 17:01:27 +10:00
end
2018-06-05 16:42:15 +10:00
end
2020-08-10 19:34:38 +02:00
def check_desc
return if cask . desc . present?
2020-09-14 02:55:47 +02:00
add_warning " Cask should have a description. Please add a `desc` stanza. "
2020-08-10 19:34:38 +02:00
end
2016-09-24 13:52:43 +02:00
def check_url
return unless cask . url
2018-09-17 02:45:00 +02:00
2016-09-24 13:52:43 +02:00
check_download_url_format
end
2016-08-18 22:11:42 +03:00
2016-09-24 13:52:43 +02:00
def check_download_url_format
odebug " Auditing URL format "
if bad_sourceforge_url?
2020-09-14 01:26:06 +02:00
add_error " SourceForge URL format incorrect. See https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/stanzas/url.md # sourceforgeosdn-urls "
2016-09-24 13:52:43 +02:00
elsif bad_osdn_url?
2020-09-14 01:26:06 +02:00
add_error " OSDN URL format incorrect. See https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/stanzas/url.md # sourceforgeosdn-urls "
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
end
2016-09-24 13:52:43 +02:00
def bad_url_format? ( regex , valid_formats_array )
2019-10-13 10:01:31 +01:00
return false unless cask . url . to_s . match? ( regex )
2018-09-17 02:45:00 +02:00
2016-09-24 13:52:43 +02:00
valid_formats_array . none? { | format | cask . url . to_s =~ format }
end
2016-08-18 22:11:42 +03:00
2016-09-24 13:52:43 +02:00
def bad_sourceforge_url?
2016-10-14 20:03:34 +02:00
bad_url_format? ( / sourceforge / ,
2016-09-24 13:52:43 +02:00
[
%r{ \ Ahttps://sourceforge \ .net/projects/[^/]+/files/latest/download \ Z } ,
2020-06-02 09:49:23 +01:00
%r{ \ Ahttps://downloads \ .sourceforge \ .net/(?!(project|sourceforge)/) } ,
2016-09-24 13:52:43 +02:00
] )
end
2016-08-18 22:11:42 +03:00
2016-09-24 13:52:43 +02:00
def bad_osdn_url?
2016-10-14 20:03:34 +02:00
bad_url_format? ( / osd / , [ %r{ \ Ahttps?://([^/]+.)?dl \ .osdn \ .jp/ } ] )
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
2020-09-08 22:12:26 +08:00
def homepage
URI ( cask . homepage . to_s ) . host
end
def domain
URI ( cask . url . to_s ) . host
end
def url_match_homepage?
2020-12-12 05:55:39 +01:00
host = cask . url . to_s
2020-09-08 22:12:26 +08:00
host_uri = URI ( host )
host = if host . match? ( / : \ d / ) && host_uri . port != 80
" #{ host_uri . host } : #{ host_uri . port } "
else
host_uri . host
end
home = homepage . downcase
if ( split_host = host . split ( " . " ) ) . length > = 3
host = split_host [ - 2 .. ] . join ( " . " )
end
if ( split_home = homepage . split ( " . " ) ) . length > = 3
home = split_home [ - 2 .. ] . join ( " . " )
end
host == home
end
def strip_url_scheme ( url )
2020-12-12 05:55:39 +01:00
url . sub ( %r{ ^[^:/]+://(www \ .)? } , " " )
2020-09-08 22:12:26 +08:00
end
def url_from_verified
2020-12-12 05:55:39 +01:00
strip_url_scheme ( cask . url . verified )
2020-09-08 22:12:26 +08:00
end
def verified_matches_url?
2020-12-12 05:55:39 +01:00
url_domain , url_path = strip_url_scheme ( cask . url . to_s ) . split ( " / " , 2 )
verified_domain , verified_path = url_from_verified . split ( " / " , 2 )
( url_domain == verified_domain || ( verified_domain && url_domain & . end_with? ( " . #{ verified_domain } " ) ) ) &&
( ! verified_path || url_path & . start_with? ( verified_path ) )
2020-09-08 22:12:26 +08:00
end
def verified_present?
cask . url . verified . present?
end
2020-12-12 05:55:39 +01:00
def file_url?
URI ( cask . url . to_s ) . scheme == " file "
2020-09-08 22:12:26 +08:00
end
def check_unnecessary_verified
return unless verified_present?
return unless url_match_homepage?
return unless verified_matches_url?
2020-12-12 05:55:39 +01:00
add_error " The URL's domain #{ domain } matches the homepage domain #{ homepage } , " \
2021-01-24 21:55:35 -05:00
" the 'verified' parameter of the 'url' stanza is unnecessary. " \
2020-12-12 05:55:39 +01:00
" See https://github.com/Homebrew/homebrew-cask/blob/master/doc/cask_language_reference/stanzas/url.md # when-url-and-homepage-hostnames-differ-add-verified "
2020-09-08 22:12:26 +08:00
end
def check_missing_verified
2020-12-12 06:01:26 +01:00
return if cask . url . from_block?
2020-12-12 05:55:39 +01:00
return if file_url?
2020-09-08 22:12:26 +08:00
return if url_match_homepage?
return if verified_present?
2020-12-12 05:55:39 +01:00
add_error " The URL's domain #{ domain } does not match the homepage domain #{ homepage } , " \
2021-01-24 21:55:35 -05:00
" a 'verified' parameter has to be added to the 'url' stanza. " \
2020-12-12 05:55:39 +01:00
" See https://github.com/Homebrew/homebrew-cask/blob/master/doc/cask_language_reference/stanzas/url.md # when-url-and-homepage-hostnames-differ-add-verified "
2020-09-08 22:12:26 +08:00
end
def check_no_match
return if url_match_homepage?
return unless verified_present?
return if ! url_match_homepage? && verified_matches_url?
2020-12-12 05:55:39 +01:00
add_error " Verified URL #{ url_from_verified } does not match URL #{ strip_url_scheme ( cask . url . to_s ) } . " \
" See https://github.com/Homebrew/homebrew-cask/blob/master/doc/cask_language_reference/stanzas/url.md # when-url-and-homepage-hostnames-differ-add-verified "
2020-09-08 22:12:26 +08:00
end
2016-09-24 13:52:43 +02:00
def check_generic_artifacts
2018-09-06 06:47:29 +02:00
cask . artifacts . select { | a | a . is_a? ( Artifact :: Artifact ) } . each do | artifact |
2017-04-06 00:33:31 +02:00
unless artifact . target . absolute?
add_error " target must be absolute path for #{ artifact . class . english_name } #{ artifact . source } "
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
end
end
2020-06-04 23:37:54 +02:00
def check_languages
@cask . languages . each do | language |
2020-08-12 00:04:20 +02:00
Locale . parse ( language )
rescue Locale :: ParserError
add_error " Locale ' #{ language } ' is invalid. "
2020-06-04 23:37:54 +02:00
end
end
2016-09-24 13:52:43 +02:00
def check_token_conflicts
2020-09-04 05:29:56 +02:00
return unless token_conflicts?
2016-09-24 13:52:43 +02:00
return unless core_formula_names . include? ( cask . token )
2018-09-17 02:45:00 +02:00
2016-09-24 13:52:43 +02:00
add_warning " possible duplicate, cask token conflicts with Homebrew core formula: #{ core_formula_url } "
end
2016-08-18 22:11:42 +03:00
2020-06-04 23:11:51 +02:00
def check_token_valid
2020-09-14 01:26:06 +02:00
add_error " cask token contains non-ascii characters " unless cask . token . ascii_only?
add_error " cask token + should be replaced by -plus- " if cask . token . include? " + "
add_error " cask token whitespace should be replaced by hyphens " if cask . token . include? " "
add_error " cask token @ should be replaced by -at- " if cask . token . include? " @ "
add_error " cask token underscores should be replaced by hyphens " if cask . token . include? " _ "
add_error " cask token should not contain double hyphens " if cask . token . include? " -- "
2020-06-04 23:11:51 +02:00
if cask . token . match? ( / [^a-z0-9 \ -] / )
2020-09-14 01:26:06 +02:00
add_error " cask token should only contain lowercase alphanumeric characters and hyphens "
2020-06-04 23:11:51 +02:00
end
2021-01-07 13:49:05 -08:00
return if ! cask . token . start_with? ( " - " ) && ! cask . token . end_with? ( " - " )
2020-06-04 23:11:51 +02:00
2020-09-14 01:26:06 +02:00
add_error " cask token should not have leading or trailing hyphens "
2020-06-04 23:11:51 +02:00
end
def check_token_bad_words
2020-09-14 02:55:47 +02:00
return unless new_cask?
2020-06-04 23:11:51 +02:00
token = cask . token
2020-09-14 01:26:06 +02:00
add_error " cask token contains .app " if token . end_with? " .app "
2020-06-04 23:11:51 +02:00
2020-09-01 14:05:52 +01:00
if / -(?<designation>alpha|beta|rc|release-candidate)$ / =~ cask . token &&
2020-09-05 04:13:07 +02:00
cask . tap & . official? &&
2020-09-01 14:05:52 +01:00
cask . tap != " homebrew/cask-versions "
2020-09-14 01:26:06 +02:00
add_error " cask token contains version designation ' #{ designation } ' "
2020-06-04 23:11:51 +02:00
end
add_warning " cask token mentions launcher " if token . end_with? " launcher "
add_warning " cask token mentions desktop " if token . end_with? " desktop "
add_warning " cask token mentions platform " if token . end_with? " mac " , " osx " , " macos "
add_warning " cask token mentions architecture " if token . end_with? " x86 " , " 32_bit " , " x86_64 " , " 64_bit "
2021-01-07 13:49:05 -08:00
frameworks = %w[ cocoa qt gtk wx java ]
return if frameworks . include? ( token ) || ! token . end_with? ( * frameworks )
2020-06-04 23:11:51 +02:00
add_warning " cask token mentions framework "
end
2016-09-24 13:52:43 +02:00
def core_tap
@core_tap || = CoreTap . instance
end
2016-08-18 22:11:42 +03:00
2016-09-24 13:52:43 +02:00
def core_formula_names
core_tap . formula_names
end
2016-08-18 22:11:42 +03:00
2020-10-20 12:03:48 +02:00
sig { returns ( String ) }
2016-09-24 13:52:43 +02:00
def core_formula_url
2020-06-25 11:20:57 +01:00
" #{ core_tap . default_remote } /blob/HEAD/Formula/ #{ cask . token } .rb "
2016-09-24 13:52:43 +02:00
end
2016-08-18 22:11:42 +03:00
2016-09-24 13:52:43 +02:00
def check_download
2021-01-07 13:49:05 -08:00
return if download . blank? || cask . url . blank?
2018-09-17 02:45:00 +02:00
2016-09-24 13:52:43 +02:00
odebug " Auditing download "
2020-11-19 18:12:16 +01:00
download . fetch
2016-09-24 13:52:43 +02:00
rescue = > e
2020-09-14 01:26:06 +02:00
add_error " download not possible: #{ e } "
2016-09-24 13:52:43 +02:00
end
2018-10-10 21:36:02 +00:00
2020-12-14 14:30:36 +01:00
def check_livecheck_version
return unless appcast?
return unless cask . livecheckable?
return if cask . livecheck . skip?
return if cask . version . latest?
latest_version = Homebrew :: Livecheck . latest_version ( cask ) & . fetch ( :latest )
return if cask . version . to_s == latest_version . to_s
add_error " Version ' #{ cask . version } ' differs from ' #{ latest_version } ' retrieved by livecheck. "
end
2019-05-07 17:06:54 +02:00
def check_appcast_contains_version
2020-04-23 21:16:17 +02:00
return unless appcast?
2019-05-07 17:06:54 +02:00
return if cask . appcast . to_s . empty?
2020-05-19 15:47:56 +01:00
return if cask . appcast . must_contain == :no_check
2019-05-07 17:06:54 +02:00
2020-12-06 17:47:41 +01:00
appcast_url = cask . appcast . to_s
begin
details = curl_http_content_headers_and_checksum ( appcast_url , user_agent : HOMEBREW_USER_AGENT_FAKE_SAFARI )
appcast_contents = details [ :file ]
2020-06-10 16:39:03 +02:00
rescue
2020-12-06 17:47:41 +01:00
add_error " appcast at URL ' #{ appcast_url } ' offline or looping "
2020-06-10 16:39:03 +02:00
return
end
2019-05-07 17:06:54 +02:00
version_stanza = cask . version . to_s
2020-12-01 17:04:59 +00:00
adjusted_version_stanza = cask . appcast . must_contain . presence || version_stanza . match ( / ^[[:alnum:].]+ / ) [ 0 ]
2019-05-07 17:06:54 +02:00
return if appcast_contents . include? adjusted_version_stanza
2020-12-06 17:47:41 +01:00
add_error " appcast at URL ' #{ appcast_url } ' does not contain " \
2019-11-23 17:25:10 +01:00
" the version number ' #{ adjusted_version_stanza } ': \n #{ appcast_contents } "
2019-05-07 17:06:54 +02:00
end
2020-08-13 16:17:47 +02:00
def check_github_prerelease_version
2020-09-03 17:35:00 +02:00
return if cask . tap == " homebrew/cask-versions "
2020-08-13 16:17:47 +02:00
odebug " Auditing GitHub prerelease "
user , repo = get_repo_data ( %r{ https?://github \ .com/([^/]+)/([^/]+)/?.* } ) if @online
return if user . nil?
2020-09-09 08:57:56 -07:00
tag = SharedAudits . github_tag_from_url ( cask . url )
tag || = cask . version
error = SharedAudits . github_release ( user , repo , tag , cask : cask )
2020-09-05 05:41:58 +02:00
add_error error if error
2020-08-13 16:17:47 +02:00
end
def check_gitlab_prerelease_version
2020-09-03 17:35:00 +02:00
return if cask . tap == " homebrew/cask-versions "
2020-08-13 16:17:47 +02:00
user , repo = get_repo_data ( %r{ https?://gitlab \ .com/([^/]+)/([^/]+)/?.* } ) if @online
return if user . nil?
odebug " Auditing GitLab prerelease "
2020-09-09 08:57:56 -07:00
tag = SharedAudits . gitlab_tag_from_url ( cask . url )
tag || = cask . version
2020-12-21 12:53:12 -05:00
error = SharedAudits . gitlab_release ( user , repo , tag , cask : cask )
2020-09-05 06:07:55 +02:00
add_error error if error
2020-08-13 16:17:47 +02:00
end
def check_github_repository_archived
user , repo = get_repo_data ( %r{ https?://github \ .com/([^/]+)/([^/]+)/?.* } ) if @online
return if user . nil?
odebug " Auditing GitHub repo archived "
metadata = SharedAudits . github_repo_data ( user , repo )
return if metadata . nil?
2020-11-07 21:29:06 +01:00
return unless metadata [ " archived " ]
message = " GitHub repo is archived "
if cask . discontinued?
add_warning message
else
add_error message
end
2020-08-13 16:17:47 +02:00
end
def check_gitlab_repository_archived
user , repo = get_repo_data ( %r{ https?://gitlab \ .com/([^/]+)/([^/]+)/?.* } ) if @online
return if user . nil?
odebug " Auditing GitLab repo archived "
metadata = SharedAudits . gitlab_repo_data ( user , repo )
return if metadata . nil?
2020-11-07 21:29:06 +01:00
return unless metadata [ " archived " ]
message = " GitLab repo is archived "
if cask . discontinued?
add_warning message
else
add_error message
end
2020-08-13 16:17:47 +02:00
end
2020-04-23 21:16:17 +02:00
def check_github_repository
2020-09-21 04:40:32 -05:00
return unless new_cask?
2020-08-13 16:17:47 +02:00
2020-04-23 21:16:17 +02:00
user , repo = get_repo_data ( %r{ https?://github \ .com/([^/]+)/([^/]+)/?.* } )
return if user . nil?
odebug " Auditing GitHub repo "
error = SharedAudits . github ( user , repo )
add_error error if error
end
def check_gitlab_repository
2020-09-02 19:13:46 +02:00
return unless new_cask?
2020-08-13 16:17:47 +02:00
2020-04-23 21:16:17 +02:00
user , repo = get_repo_data ( %r{ https?://gitlab \ .com/([^/]+)/([^/]+)/?.* } )
return if user . nil?
odebug " Auditing GitLab repo "
error = SharedAudits . gitlab ( user , repo )
add_error error if error
end
def check_bitbucket_repository
2020-09-21 04:40:32 -05:00
return unless new_cask?
2020-08-13 16:17:47 +02:00
2020-04-23 21:16:17 +02:00
user , repo = get_repo_data ( %r{ https?://bitbucket \ .org/([^/]+)/([^/]+)/?.* } )
return if user . nil?
odebug " Auditing Bitbucket repo "
error = SharedAudits . bitbucket ( user , repo )
add_error error if error
end
def get_repo_data ( regex )
2020-08-10 19:34:38 +02:00
return unless online?
2020-04-23 21:16:17 +02:00
_ , user , repo = * regex . match ( cask . url . to_s )
_ , user , repo = * regex . match ( cask . homepage ) unless user
_ , user , repo = * regex . match ( cask . appcast . to_s ) unless user
return if ! user || ! repo
repo . gsub! ( / .git$ / , " " )
[ user , repo ]
end
2020-06-06 21:10:16 +01:00
def check_denylist
2021-01-26 01:16:00 -08:00
return unless cask . tap
return unless cask . tap . official?
2020-06-06 21:10:16 +01:00
return unless reason = Denylist . reason ( cask . token )
2019-09-08 09:09:37 -04:00
2020-06-06 21:10:16 +01:00
add_error " #{ cask . token } is not allowed: #{ reason } "
2019-09-08 09:09:37 -04:00
end
2021-01-26 01:16:00 -08:00
def check_reverse_migration
return unless new_cask?
return unless cask . tap
return unless cask . tap . official?
return unless cask . tap . tap_migrations . key? ( cask . token )
add_error " #{ cask . token } is listed in tap_migrations.json "
end
2018-10-10 21:36:02 +00:00
def check_https_availability
2018-10-10 21:36:06 +00:00
return unless download
2019-02-19 13:12:52 +00:00
2020-09-05 04:08:34 +02:00
check_url_for_https_availability ( cask . url , user_agents : [ cask . url . user_agent ] ) if cask . url && ! cask . url . using
check_url_for_https_availability ( cask . appcast , check_content : true ) if cask . appcast && appcast?
check_url_for_https_availability ( cask . homepage , check_content : true , user_agents : [ :browser ] ) if cask . homepage
2018-10-10 21:36:02 +00:00
end
2020-09-05 04:08:34 +02:00
def check_url_for_https_availability ( url_to_check , ** options )
problem = curl_check_http_content ( url_to_check . to_s , ** options )
2018-11-24 11:21:52 +00:00
add_error problem if problem
2018-10-10 21:36:02 +00:00
end
2016-08-18 22:11:42 +03:00
end
end