Skip to content

Commit

Permalink
Restrict direct url installs to the file:// scheme
Browse files Browse the repository at this point in the history
  • Loading branch information
Rylan12 committed Jul 13, 2024
1 parent ab7e49c commit 8b0a4a9
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 11 deletions.
15 changes: 13 additions & 2 deletions Library/Homebrew/cask/cask_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ module Cask
module CaskLoader
extend Context

ALLOWED_URL_SCHEMES = %w[file].freeze
private_constant :ALLOWED_URL_SCHEMES

module ILoader
extend T::Helpers
interface!
Expand Down Expand Up @@ -171,17 +174,25 @@ def self.try_new(ref, warn: false)
new(uri)
end

attr_reader :url
attr_reader :url, :name

sig { params(url: T.any(URI::Generic, String)).void }
def initialize(url)
@url = URI(url)
super Cache.path/File.basename(T.must(@url.path))
@name = File.basename(T.must(@url.path))
super Cache.path/name
end

def load(config:)
path.dirname.mkpath

if ALLOWED_URL_SCHEMES.exclude?(url.scheme)
raise UnsupportedInstallationMethod,
"Non-checksummed download of #{name} formula file from an arbitrary URL is unsupported! " \
"`brew extract` or `brew create` and `brew tap-new` to create a formula file in a tap " \
"on GitHub instead."
end

begin
ohai "Downloading #{url}"
::Utils::Curl.curl_download url, to: path
Expand Down
14 changes: 5 additions & 9 deletions Library/Homebrew/formulary.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ module Formulary
extend Context
extend Cachable

URL_START_REGEX = %r{(https?|ftp|file)://}
private_constant :URL_START_REGEX
ALLOWED_URL_SCHEMES = %w[file].freeze
private_constant :ALLOWED_URL_SCHEMES

# `:codesign` and custom requirement classes are not supported.
API_SUPPORTED_REQUIREMENTS = [:arch, :linux, :macos, :maximum_macos, :xcode].freeze
Expand Down Expand Up @@ -696,7 +696,7 @@ class FromURILoader < FormulaLoader
def self.try_new(ref, from: T.unsafe(nil), warn: false)
ref = ref.to_s

new(ref, from:) if URL_START_REGEX.match?(ref)
new(ref, from:) if URI(ref).scheme.present?
end

attr_reader :url
Expand All @@ -713,12 +713,8 @@ def initialize(url, from: nil)
end

def load_file(flags:, ignore_errors:)
match = url.match(%r{githubusercontent.com/[\w-]+/[\w-]+/[a-f0-9]{40}(?:/Formula)?/(?<name>[\w+-.@]+).rb})
if match
raise UnsupportedInstallationMethod,
"Installation of #{match[:name]} from a GitHub commit URL is unsupported! " \
"`brew extract #{match[:name]}` to a stable tap on GitHub instead."
elsif url.match?(%r{^(https?|ftp)://})
url_scheme = URI(url).scheme
if ALLOWED_URL_SCHEMES.exclude?(url_scheme)
raise UnsupportedInstallationMethod,
"Non-checksummed download of #{name} formula file from an arbitrary URL is unsupported! " \
"`brew extract` or `brew create` and `brew tap-new` to create a formula file in a tap " \
Expand Down
30 changes: 30 additions & 0 deletions Library/Homebrew/test/cask/cask_loader/from_uri_loader_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,34 @@
RUBY
end
end

describe "::load" do
it "raises an error when given an https URL" do
loader = described_class.new("https://brew.sh/foo.rb")
expect do
loader.load(config: nil)
end.to raise_error(UnsupportedInstallationMethod)
end

it "raises an error when given an ftp URL" do
loader = described_class.new("ftp://brew.sh/foo.rb")
expect do
loader.load(config: nil)
end.to raise_error(UnsupportedInstallationMethod)
end

it "raises an error when given an sftp URL" do
loader = described_class.new("sftp://brew.sh/foo.rb")
expect do
loader.load(config: nil)
end.to raise_error(UnsupportedInstallationMethod)
end

it "does not raise an error when given a file URL" do
loader = described_class.new("file://#{TEST_FIXTURE_DIR}/cask/Casks/local-caffeine.rb")
expect do
loader.load(config: nil)
end.not_to raise_error(UnsupportedInstallationMethod)
end
end
end
32 changes: 32 additions & 0 deletions Library/Homebrew/test/formulary_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,38 @@ def formula_json_contents(extra_items = {})
end
end
end

context "when passed a URL" do
it "raises an error when given an https URL" do
expect do
described_class.factory("https://brew.sh/foo.rb")
end.to raise_error(UnsupportedInstallationMethod)
end

it "raises an error when given a bottle URL" do
expect do
described_class.factory("https://brew.sh/foo-1.0.arm64_catalina.bottle.tar.gz")
end.to raise_error(UnsupportedInstallationMethod)
end

it "raises an error when given an ftp URL" do
expect do
described_class.factory("ftp://brew.sh/foo.rb")
end.to raise_error(UnsupportedInstallationMethod)
end

it "raises an error when given an sftp URL" do
expect do
described_class.factory("sftp://brew.sh/foo.rb")
end.to raise_error(UnsupportedInstallationMethod)
end

it "does not raise an error when given a file URL" do
expect do
described_class.factory("file://#{TEST_FIXTURE_DIR}/testball.rb")
end.not_to raise_error(UnsupportedInstallationMethod)
end
end
end

specify "::from_contents" do
Expand Down

0 comments on commit 8b0a4a9

Please sign in to comment.