diff --git a/Library/Homebrew/cask/audit.rb b/Library/Homebrew/cask/audit.rb index e71c4d97a5..1840ed58fa 100644 --- a/Library/Homebrew/cask/audit.rb +++ b/Library/Homebrew/cask/audit.rb @@ -38,6 +38,8 @@ module Cask check_sha256 check_url check_generic_artifacts + check_token_valid + check_token_bad_words check_token_conflicts check_download check_https_availability @@ -282,6 +284,56 @@ module Cask add_warning "possible duplicate, cask token conflicts with Homebrew core formula: #{core_formula_url}" end + def check_token_valid + return unless @strict + + add_warning "cask token is not lowercase" if cask.token.downcase! + + add_warning "cask token contains non-ascii characters" unless cask.token.ascii_only? + + add_warning "cask token + should be replaced by -plus-" if cask.token.include? "+" + + add_warning "cask token @ should be replaced by -at-" if cask.token.include? "@" + + add_warning "cask token whitespace should be replaced by hyphens" if cask.token.include? " " + + add_warning "cask token underscores should be replaced by hyphens" if cask.token.include? "_" + + if cask.token.match?(/[^a-z0-9\-]/) + add_warning "cask token should only contain alphanumeric characters and hyphens" + end + + add_warning "cask token should not contain double hyphens" if cask.token.include? "--" + + return unless cask.token.end_with?("-") || cask.token.start_with?("-") + + add_warning "cask token should not have leading or trailing hyphens" + end + + def check_token_bad_words + return unless @strict + + token = cask.token + + add_warning "cask token contains .app" if token.end_with? ".app" + + if cask.token.end_with? "alpha", "beta", "release candidate" + add_warning "cask token contains version designation" + 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" + + return unless token.end_with?("cocoa", "qt", "gtk", "wx", "java") && !%w[cocoa qt gtk wx java].include?(token) + + add_warning "cask token mentions framework" + end + def core_tap @core_tap ||= CoreTap.instance end diff --git a/Library/Homebrew/test/cask/audit_spec.rb b/Library/Homebrew/test/cask/audit_spec.rb index 4533bf825c..4acb8b773a 100644 --- a/Library/Homebrew/test/cask/audit_spec.rb +++ b/Library/Homebrew/test/cask/audit_spec.rb @@ -40,11 +40,13 @@ describe Cask::Audit, :cask do let(:cask) { instance_double(Cask::Cask) } let(:download) { false } let(:token_conflicts) { false } + let(:strict) { false } let(:fake_system_command) { class_double(SystemCommand) } let(:audit) { described_class.new(cask, download: download, token_conflicts: token_conflicts, - command: fake_system_command) + command: fake_system_command, + strict: strict) } describe "#result" do @@ -83,6 +85,16 @@ describe Cask::Audit, :cask do describe "#run!" do subject { audit.run! } + def tmp_cask(name, text) + path = Pathname.new "#{dir}/#{name}.rb" + path.open("w") do |f| + f.write text + end + + Cask::CaskLoader.load(path) + end + + let(:dir) { mktmpdir } let(:cask) { Cask::CaskLoader.load(cask_token) } describe "required stanzas" do @@ -95,6 +107,174 @@ describe Cask::Audit, :cask do end end + describe "token validation" do + let(:strict) { true } + let(:cask) do + tmp_cask cask_token.to_s, <<~RUBY + cask '#{cask_token}' do + version '1.0' + sha256 '8dd95daa037ac02455435446ec7bc737b34567afe9156af7d20b2a83805c1d8a' + url "https://brew.sh/" + name 'Audit' + homepage 'https://brew.sh/' + app 'Audit.app' + end + RUBY + end + + context "when cask token is not lowercase" do + let(:cask_token) { "Upper-Case" } + + it "warns about lowercase" do + expect(subject).to warn_with(/token is not lowercase/) + end + end + + context "when cask token is not ascii" do + let(:cask_token) { "ascii⌘" } + + it "warns about ascii" do + expect(subject).to warn_with(/contains non-ascii characters/) + end + end + + context "when cask token has +" do + let(:cask_token) { "app++" } + + it "warns about +" do + expect(subject).to warn_with(/\+ should be replaced by -plus-/) + end + end + + context "when cask token has @" do + let(:cask_token) { "app@stuff" } + + it "warns about +" do + expect(subject).to warn_with(/@ should be replaced by -at-/) + end + end + + context "when cask token has whitespace" do + let(:cask_token) { "app stuff" } + + it "warns about whitespace" do + expect(subject).to warn_with(/whitespace should be replaced by hyphens/) + end + end + + context "when cask token has underscores" do + let(:cask_token) { "app_stuff" } + + it "warns about underscores" do + expect(subject).to warn_with(/underscores should be replaced by hyphens/) + end + end + + context "when cask token has non-alphanumeric characters" do + let(:cask_token) { "app(stuff)" } + + it "warns about non-alphanumeric characters" do + expect(subject).to warn_with(/should only contain alphanumeric characters and hyphens/) + end + end + + context "when cask token has double hyphens" do + let(:cask_token) { "app--stuff" } + + it "warns about double hyphens" do + expect(subject).to warn_with(/should not contain double hyphens/) + end + end + + context "when cask token has trailing hyphens" do + let(:cask_token) { "app-" } + + it "warns about trailing hyphens" do + expect(subject).to warn_with(/should not have leading or trailing hyphens/) + end + end + end + + describe "token bad words" do + let(:strict) { true } + let(:cask) do + tmp_cask cask_token.to_s, <<~RUBY + cask '#{cask_token}' do + version '1.0' + sha256 '8dd95daa037ac02455435446ec7bc737b34567afe9156af7d20b2a83805c1d8a' + url "https://brew.sh/" + name 'Audit' + homepage 'https://brew.sh/' + app 'Audit.app' + end + RUBY + end + + context "when cask token contains .app" do + let(:cask_token) { "token.app" } + + it "warns about .app" do + expect(subject).to warn_with(/token contains .app/) + end + end + + context "when cask token contains version" do + let(:cask_token) { "token-beta" } + + it "warns about version in token" do + expect(subject).to warn_with(/token contains version/) + end + end + + context "when cask token contains launcher" do + let(:cask_token) { "token-launcher" } + + it "warns about launcher in token" do + expect(subject).to warn_with(/token mentions launcher/) + end + end + + context "when cask token contains desktop" do + let(:cask_token) { "token-desktop" } + + it "warns about desktop in token" do + expect(subject).to warn_with(/token mentions desktop/) + end + end + + context "when cask token contains platform" do + let(:cask_token) { "token-osx" } + + it "warns about platform in token" do + expect(subject).to warn_with(/token mentions platform/) + end + end + + context "when cask token contains architecture" do + let(:cask_token) { "token-x86" } + + it "warns about architecture in token" do + expect(subject).to warn_with(/token mentions architecture/) + end + end + + context "when cask token contains framework" do + let(:cask_token) { "token-java" } + + it "warns about framework in token" do + expect(subject).to warn_with(/cask token mentions framework/) + end + end + + context "when cask token is framework" do + let(:cask_token) { "java" } + + it "does not warn about framework" do + expect(subject).not_to warn_with(/token contains version/) + end + end + end + describe "pkg allow_untrusted checks" do let(:warning_msg) { "allow_untrusted is not permitted in official Homebrew Cask taps" }