aboutsummaryrefslogtreecommitdiffstats
path: root/Library
diff options
context:
space:
mode:
Diffstat (limited to 'Library')
-rw-r--r--Library/.rubocop.yml6
-rw-r--r--Library/Homebrew/dev-cmd/audit.rb23
-rw-r--r--Library/Homebrew/rubocops.rb1
-rw-r--r--Library/Homebrew/rubocops/checksum_cop.rb64
-rw-r--r--Library/Homebrew/rubocops/extend/formula_cop.rb20
-rw-r--r--Library/Homebrew/test/rubocops/checksum_cop_spec.rb222
6 files changed, 311 insertions, 25 deletions
diff --git a/Library/.rubocop.yml b/Library/.rubocop.yml
index 19d871414..21f123859 100644
--- a/Library/.rubocop.yml
+++ b/Library/.rubocop.yml
@@ -12,6 +12,12 @@ FormulaAudit/Text:
FormulaAudit/Caveats:
Enabled: true
+FormulaAudit/Checksum:
+ Enabled: true
+
+FormulaAudit/ChecksumCase:
+ Enabled: true
+
FormulaAuditStrict/BottleBlock:
Enabled: true
diff --git a/Library/Homebrew/dev-cmd/audit.rb b/Library/Homebrew/dev-cmd/audit.rb
index 0d9a630fd..a6c71f795 100644
--- a/Library/Homebrew/dev-cmd/audit.rb
+++ b/Library/Homebrew/dev-cmd/audit.rb
@@ -1246,7 +1246,6 @@ class ResourceAuditor
def audit
audit_version
- audit_checksum
audit_download_strategy
audit_urls
self
@@ -1273,28 +1272,6 @@ class ResourceAuditor
problem "version #{version} should not end with an underline and a number"
end
- def audit_checksum
- return unless checksum
-
- case checksum.hash_type
- when :md5
- problem "MD5 checksums are deprecated, please use SHA256"
- return
- when :sha1
- problem "SHA1 checksums are deprecated, please use SHA256"
- return
- when :sha256 then len = 64
- end
-
- if checksum.empty?
- problem "#{checksum.hash_type} is empty"
- else
- problem "#{checksum.hash_type} should be #{len} characters" unless checksum.hexdigest.length == len
- problem "#{checksum.hash_type} contains invalid characters" unless checksum.hexdigest =~ /^[a-fA-F0-9]+$/
- problem "#{checksum.hash_type} should be lowercase" unless checksum.hexdigest == checksum.hexdigest.downcase
- end
- end
-
def audit_download_strategy
if url =~ %r{^(cvs|bzr|hg|fossil)://} || url =~ %r{^(svn)\+http://}
problem "Use of the #{$&} scheme is deprecated, pass `:using => :#{Regexp.last_match(1)}` instead"
diff --git a/Library/Homebrew/rubocops.rb b/Library/Homebrew/rubocops.rb
index c4a38cdb7..4710654fa 100644
--- a/Library/Homebrew/rubocops.rb
+++ b/Library/Homebrew/rubocops.rb
@@ -5,3 +5,4 @@ require_relative "./rubocops/components_redundancy_cop"
require_relative "./rubocops/homepage_cop"
require_relative "./rubocops/text_cop"
require_relative "./rubocops/caveats_cop"
+require_relative "./rubocops/checksum_cop"
diff --git a/Library/Homebrew/rubocops/checksum_cop.rb b/Library/Homebrew/rubocops/checksum_cop.rb
new file mode 100644
index 000000000..dcaf60e7d
--- /dev/null
+++ b/Library/Homebrew/rubocops/checksum_cop.rb
@@ -0,0 +1,64 @@
+require_relative "./extend/formula_cop"
+
+module RuboCop
+ module Cop
+ module FormulaAudit
+ class Checksum < FormulaCop
+ def audit_formula(_node, _class_node, _parent_class_node, body_node)
+ return if body_node.nil?
+ if method_called_ever?(body_node, :md5)
+ problem "MD5 checksums are deprecated, please use SHA256"
+ end
+
+ if method_called_ever?(body_node, :sha1)
+ problem "SHA1 checksums are deprecated, please use SHA256"
+ end
+
+ sha256_calls = find_every_method_call_by_name(body_node, :sha256)
+ sha256_calls.each do |sha256_call|
+ sha256_node = get_checksum_node(sha256_call)
+ audit_sha256(sha256_node)
+ end
+ end
+
+ def audit_sha256(checksum)
+ return if checksum.nil?
+ if regex_match_group(checksum, /^$/)
+ problem "sha256 is empty"
+ return
+ end
+
+ if string_content(checksum).size != 64 && regex_match_group(checksum, /^\w*$/)
+ problem "sha256 should be 64 characters"
+ end
+
+ return unless regex_match_group(checksum, /[^a-f0-9]+/i)
+ problem "sha256 contains invalid characters"
+ end
+ end
+
+ class ChecksumCase < FormulaCop
+ def audit_formula(_node, _class_node, _parent_class_node, body_node)
+ return if body_node.nil?
+ sha256_calls = find_every_method_call_by_name(body_node, :sha256)
+ sha256_calls.each do |sha256_call|
+ checksum = get_checksum_node(sha256_call)
+ next if checksum.nil?
+ next unless regex_match_group(checksum, /[A-F]+/)
+ problem "sha256 should be lowercase"
+ end
+ end
+
+ private
+
+ def autocorrect(node)
+ lambda do |corrector|
+ correction = node.source.downcase
+ corrector.insert_before(node.source_range, correction)
+ corrector.remove(node.source_range)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/Library/Homebrew/rubocops/extend/formula_cop.rb b/Library/Homebrew/rubocops/extend/formula_cop.rb
index 75a3e72d5..439fde6a5 100644
--- a/Library/Homebrew/rubocops/extend/formula_cop.rb
+++ b/Library/Homebrew/rubocops/extend/formula_cop.rb
@@ -173,6 +173,12 @@ module RuboCop
node.each_child_node(:block).select { |block_node| block_name == block_node.method_name }
end
+ # Returns an array of block nodes of any depth below node in AST
+ def find_all_blocks(node, block_name)
+ return if node.nil?
+ node.each_descendant(:block).select { |block_node| block_name == block_node.method_name }
+ end
+
# Returns a method definition node with method_name
def find_method_def(node, method_name)
return if node.nil?
@@ -250,8 +256,7 @@ module RuboCop
# Returns the array of arguments of the method_node
def parameters(method_node)
- return unless method_node.send_type?
- method_node.method_args
+ method_node.method_args if method_node.send_type? || method_node.block_type?
end
# Returns true if the given parameters are present in method call
@@ -272,6 +277,17 @@ module RuboCop
end
end
+ # Returns the sha256 str node given a sha256 call node
+ def get_checksum_node(call)
+ return if parameters(call).empty? || parameters(call).nil?
+ if parameters(call).first.str_type?
+ parameters(call).first
+ # sha256 is passed as a key-value pair in bottle blocks
+ elsif parameters(call).first.hash_type?
+ parameters(call).first.keys.first
+ end
+ end
+
# Returns the begin position of the node's line in source code
def line_start_column(node)
node.source_range.source_buffer.line_range(node.loc.line).begin_pos
diff --git a/Library/Homebrew/test/rubocops/checksum_cop_spec.rb b/Library/Homebrew/test/rubocops/checksum_cop_spec.rb
new file mode 100644
index 000000000..644152c32
--- /dev/null
+++ b/Library/Homebrew/test/rubocops/checksum_cop_spec.rb
@@ -0,0 +1,222 @@
+require "rubocop"
+require "rubocop/rspec/support"
+require_relative "../../extend/string"
+require_relative "../../rubocops/checksum_cop"
+
+describe RuboCop::Cop::FormulaAudit::Checksum do
+ subject(:cop) { described_class.new }
+
+ context "When auditing spec checksums" do
+ it "When the checksum is empty" do
+ source = <<-EOS.undent
+ class Foo < Formula
+ url 'http://example.com/foo-1.0.tgz'
+ stable do
+ url "https://github.com/foo-lang/foo-compiler/archive/0.18.0.tar.gz"
+ sha256 ""
+
+ resource "foo-package" do
+ url "https://github.com/foo-lang/foo-package/archive/0.18.0.tar.gz"
+ sha256 ""
+ end
+ end
+ end
+ EOS
+
+ expected_offenses = [{ message: "sha256 is empty",
+ severity: :convention,
+ line: 5,
+ column: 12,
+ source: source },
+ { message: "sha256 is empty",
+ severity: :convention,
+ line: 9,
+ column: 14,
+ source: source }]
+
+ inspect_source(cop, source)
+
+ expected_offenses.zip(cop.offenses).each do |expected, actual|
+ expect_offense(expected, actual)
+ end
+ end
+
+ it "When the checksum is not 64 characters" do
+ source = <<-EOS.undent
+ class Foo < Formula
+ url 'http://example.com/foo-1.0.tgz'
+ stable do
+ url "https://github.com/foo-lang/foo-compiler/archive/0.18.0.tar.gz"
+ sha256 "5cf6e1ae0a645b426c0474cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea0e9ad"
+
+ resource "foo-package" do
+ url "https://github.com/foo-lang/foo-package/archive/0.18.0.tar.gz"
+ sha256 "5cf6e1ae0a645b426c047aaa4cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea0e9"
+ end
+ end
+ end
+ EOS
+
+ expected_offenses = [{ message: "sha256 should be 64 characters",
+ severity: :convention,
+ line: 5,
+ column: 12,
+ source: source },
+ { message: "sha256 should be 64 characters",
+ severity: :convention,
+ line: 9,
+ column: 14,
+ source: source }]
+
+ inspect_source(cop, source)
+
+ expected_offenses.zip(cop.offenses).each do |expected, actual|
+ expect_offense(expected, actual)
+ end
+ end
+
+ it "When the checksum has invalid chars" do
+ source = <<-EOS.undent
+ class Foo < Formula
+ url 'http://example.com/foo-1.0.tgz'
+ stable do
+ url "https://github.com/foo-lang/foo-compiler/archive/0.18.0.tar.gz"
+ sha256 "5cf6e1ae0a645b426c0k7cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea0e9a"
+
+ resource "foo-package" do
+ url "https://github.com/foo-lang/foo-package/archive/0.18.0.tar.gz"
+ sha256 "5cf6e1ae0a645b426x047aa4cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea9"
+ end
+ end
+ end
+ EOS
+
+ expected_offenses = [{ message: "sha256 contains invalid characters",
+ severity: :convention,
+ line: 5,
+ column: 31,
+ source: source },
+ { message: "sha256 contains invalid characters",
+ severity: :convention,
+ line: 9,
+ column: 31,
+ source: source }]
+
+ inspect_source(cop, source)
+
+ expected_offenses.zip(cop.offenses).each do |expected, actual|
+ expect_offense(expected, actual)
+ end
+ end
+ end
+end
+
+describe RuboCop::Cop::FormulaAudit::ChecksumCase do
+ subject(:cop) { described_class.new }
+
+ context "When auditing spec checksums" do
+ it "When the checksum has upper case characters" do
+ source = <<-EOS.undent
+ class Foo < Formula
+ url 'http://example.com/foo-1.0.tgz'
+ stable do
+ url "https://github.com/foo-lang/foo-compiler/archive/0.18.0.tar.gz"
+ sha256 "5cf6e1ae0A645b426c0a7cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea0e9a"
+
+ resource "foo-package" do
+ url "https://github.com/foo-lang/foo-package/archive/0.18.0.tar.gz"
+ sha256 "5cf6e1Ae0a645b426b047aa4cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea9"
+ end
+ end
+ end
+ EOS
+
+ expected_offenses = [{ message: "sha256 should be lowercase",
+ severity: :convention,
+ line: 5,
+ column: 21,
+ source: source },
+ { message: "sha256 should be lowercase",
+ severity: :convention,
+ line: 9,
+ column: 20,
+ source: source }]
+
+ inspect_source(cop, source)
+
+ expected_offenses.zip(cop.offenses).each do |expected, actual|
+ expect_offense(expected, actual)
+ end
+ end
+
+ it "When auditing stable blocks outside spec blocks" do
+ source = <<-EOS.undent
+ class Foo < Formula
+ url 'http://example.com/foo-1.0.tgz'
+ resource "foo-outside" do
+ url "https://github.com/foo-lang/foo-outside/archive/0.18.0.tar.gz"
+ sha256 "A4cc7cd3f7d1605ffa1ac5755cf6e1ae0a645b426b047a6a39a8b2268ddc7ea9"
+ end
+ stable do
+ url "https://github.com/foo-lang/foo-compiler/archive/0.18.0.tar.gz"
+ sha256 "5cf6e1ae0a645b426c0a7cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea0e9a"
+
+ resource "foo-package" do
+ url "https://github.com/foo-lang/foo-package/archive/0.18.0.tar.gz"
+ sha256 "5cf6e1ae0a645b426b047aa4cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea9"
+ end
+ end
+ end
+ EOS
+
+ expected_offenses = [{ message: "sha256 should be lowercase",
+ severity: :convention,
+ line: 5,
+ column: 12,
+ source: source }]
+
+ inspect_source(cop, source)
+
+ expected_offenses.zip(cop.offenses).each do |expected, actual|
+ expect_offense(expected, actual)
+ end
+ end
+ end
+
+ context "When auditing checksum with autocorrect" do
+ it "When there is uppercase sha256" do
+ source = <<-EOS.undent
+ class Foo < Formula
+ url 'http://example.com/foo-1.0.tgz'
+ stable do
+ url "https://github.com/foo-lang/foo-compiler/archive/0.18.0.tar.gz"
+ sha256 "5cf6e1ae0A645b426c0a7cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea0e9a"
+
+ resource "foo-package" do
+ url "https://github.com/foo-lang/foo-package/archive/0.18.0.tar.gz"
+ sha256 "5cf6e1Ae0a645b426b047aa4cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea9"
+ end
+ end
+ end
+ EOS
+
+ corrected_source = <<-EOS.undent
+ class Foo < Formula
+ url 'http://example.com/foo-1.0.tgz'
+ stable do
+ url "https://github.com/foo-lang/foo-compiler/archive/0.18.0.tar.gz"
+ sha256 "5cf6e1ae0a645b426c0a7cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea0e9a"
+
+ resource "foo-package" do
+ url "https://github.com/foo-lang/foo-package/archive/0.18.0.tar.gz"
+ sha256 "5cf6e1ae0a645b426b047aa4cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea9"
+ end
+ end
+ end
+ EOS
+
+ new_source = autocorrect_source(cop, source)
+ expect(new_source).to eq(corrected_source)
+ end
+ end
+end