Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restrict direct url installs to the file:// scheme #17697

Merged
merged 1 commit into from
Jul 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading