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

Curl: Check all responses for protected cookies #13326

Merged
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
6 changes: 3 additions & 3 deletions Library/Homebrew/test/utils/curl_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
details[:normal][:no_cookie] = {
url: "https://www.example.com/",
final_url: nil,
status: "403",
status_code: "403",
headers: {
"age" => "123456",
"cache-control" => "max-age=604800",
Expand All @@ -35,7 +35,7 @@
}

details[:normal][:ok] = Marshal.load(Marshal.dump(details[:normal][:no_cookie]))
details[:normal][:ok][:status] = "200"
details[:normal][:ok][:status_code] = "200"

details[:normal][:single_cookie] = Marshal.load(Marshal.dump(details[:normal][:no_cookie]))
details[:normal][:single_cookie][:headers]["set-cookie"] = "a_cookie=for_testing"
Expand All @@ -52,7 +52,7 @@
details[:cloudflare][:single_cookie] = {
url: "https://www.example.com/",
final_url: nil,
status: "403",
status_code: "403",
headers: {
"date" => "Wed, 1 Jan 2020 01:23:45 GMT",
"content-type" => "text/plain; charset=UTF-8",
Expand Down
47 changes: 24 additions & 23 deletions Library/Homebrew/utils/curl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -198,21 +198,20 @@ def curl_output(*args, **options)
end

# Check if a URL is protected by CloudFlare (e.g. badlion.net and jaxx.io).
# @param details [Hash] Response information from
# `#curl_http_content_headers_and_checksum`.
# @param response [Hash] A response hash from `#parse_curl_response`.
# @return [true, false] Whether a response contains headers indicating that
# the URL is protected by Cloudflare.
sig { params(details: T::Hash[Symbol, T.untyped]).returns(T::Boolean) }
def url_protected_by_cloudflare?(details)
return false if details[:headers].blank?
return false unless [403, 503].include?(details[:status].to_i)
sig { params(response: T::Hash[Symbol, T.untyped]).returns(T::Boolean) }
def url_protected_by_cloudflare?(response)
return false if response[:headers].blank?
return false unless [403, 503].include?(response[:status_code].to_i)

set_cookie_header = Array(details[:headers]["set-cookie"])
set_cookie_header = Array(response[:headers]["set-cookie"])
has_cloudflare_cookie_header = set_cookie_header.compact.any? do |cookie|
cookie.match?(/^(__cfduid|__cf_bm)=/i)
end

server_header = Array(details[:headers]["server"])
server_header = Array(response[:headers]["server"])
has_cloudflare_server = server_header.compact.any? do |server|
server.match?(/^cloudflare/i)
end
Expand All @@ -221,16 +220,15 @@ def url_protected_by_cloudflare?(details)
end

# Check if a URL is protected by Incapsula (e.g. corsair.com).
# @param details [Hash] Response information from
# `#curl_http_content_headers_and_checksum`.
# @param response [Hash] A response hash from `#parse_curl_response`.
# @return [true, false] Whether a response contains headers indicating that
# the URL is protected by Incapsula.
sig { params(details: T::Hash[Symbol, T.untyped]).returns(T::Boolean) }
def url_protected_by_incapsula?(details)
return false if details[:headers].blank?
return false if details[:status].to_i != 403
sig { params(response: T::Hash[Symbol, T.untyped]).returns(T::Boolean) }
def url_protected_by_incapsula?(response)
return false if response[:headers].blank?
return false if response[:status_code].to_i != 403

set_cookie_header = Array(details[:headers]["set-cookie"])
set_cookie_header = Array(response[:headers]["set-cookie"])
set_cookie_header.compact.any? { |cookie| cookie.match?(/^(visid_incap|incap_ses)_/i) }
end

Expand All @@ -255,7 +253,7 @@ def curl_check_http_content(url, url_type, specs: {}, user_agents: [:default],
next
end

next unless http_status_ok?(secure_details[:status])
next unless http_status_ok?(secure_details[:status_code])

hash_needed = true
user_agents = [user_agent]
Expand All @@ -273,20 +271,22 @@ def curl_check_http_content(url, url_type, specs: {}, user_agents: [:default],
use_homebrew_curl: use_homebrew_curl,
user_agent: user_agent,
)
break if http_status_ok?(details[:status])
break if http_status_ok?(details[:status_code])
end

unless details[:status]
unless details[:status_code]
# Hack around https://github.com/Homebrew/brew/issues/3199
return if MacOS.version == :el_capitan

return "The #{url_type} #{url} is not reachable"
end

unless http_status_ok?(details[:status])
return if url_protected_by_cloudflare?(details) || url_protected_by_incapsula?(details)
unless http_status_ok?(details[:status_code])
return if details[:responses].any? do |response|
url_protected_by_cloudflare?(response) || url_protected_by_incapsula?(response)
end

return "The #{url_type} #{url} is not reachable (HTTP status code #{details[:status]})"
return "The #{url_type} #{url} is not reachable (HTTP status code #{details[:status_code]})"
end

if url.start_with?("https://") && Homebrew::EnvConfig.no_insecure_redirect? &&
Expand All @@ -296,7 +296,7 @@ def curl_check_http_content(url, url_type, specs: {}, user_agents: [:default],

return unless secure_details

return if !http_status_ok?(details[:status]) || !http_status_ok?(secure_details[:status])
return if !http_status_ok?(details[:status_code]) || !http_status_ok?(secure_details[:status_code])

etag_match = details[:etag] &&
details[:etag] == secure_details[:etag]
Expand Down Expand Up @@ -397,12 +397,13 @@ def curl_http_content_headers_and_checksum(
{
url: url,
final_url: final_url,
status: status_code,
status_code: status_code,
headers: headers,
etag: etag,
content_length: content_length,
file: file_contents,
file_hash: file_hash,
responses: responses,
}
ensure
file.unlink
Expand Down