attestation: handle bad configurations better
This commit is contained in:
parent
4aae003a1a
commit
16d547b030
@ -44,6 +44,12 @@ module Homebrew
|
|||||||
# @api private
|
# @api private
|
||||||
class GhAuthNeeded < RuntimeError; end
|
class GhAuthNeeded < RuntimeError; end
|
||||||
|
|
||||||
|
# Raised if attestation verification cannot continue due to invalid
|
||||||
|
# credentials.
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
class GhAuthInvalid < RuntimeError; end
|
||||||
|
|
||||||
# Returns whether attestation verification is enabled.
|
# Returns whether attestation verification is enabled.
|
||||||
#
|
#
|
||||||
# @api private
|
# @api private
|
||||||
@ -53,6 +59,7 @@ module Homebrew
|
|||||||
return true if Homebrew::EnvConfig.verify_attestations?
|
return true if Homebrew::EnvConfig.verify_attestations?
|
||||||
return false if GitHub::API.credentials.blank?
|
return false if GitHub::API.credentials.blank?
|
||||||
return false if ENV.fetch("CI", false)
|
return false if ENV.fetch("CI", false)
|
||||||
|
return false if OS.unsupported_configuration?
|
||||||
|
|
||||||
Homebrew::EnvConfig.developer? || Homebrew::EnvConfig.devcmdrun?
|
Homebrew::EnvConfig.developer? || Homebrew::EnvConfig.devcmdrun?
|
||||||
end
|
end
|
||||||
@ -117,11 +124,14 @@ module Homebrew
|
|||||||
raise GhAuthNeeded, "missing credentials" if credentials.blank?
|
raise GhAuthNeeded, "missing credentials" if credentials.blank?
|
||||||
|
|
||||||
begin
|
begin
|
||||||
result = system_command!(gh_executable, args: cmd, env: { "GH_TOKEN" => credentials },
|
result = system_command!(gh_executable, args: cmd,
|
||||||
|
env: { "GH_TOKEN" => credentials, "GH_HOST" => "github.com" },
|
||||||
secrets: [credentials], print_stderr: false, chdir: HOMEBREW_TEMP)
|
secrets: [credentials], print_stderr: false, chdir: HOMEBREW_TEMP)
|
||||||
rescue ErrorDuringExecution => e
|
rescue ErrorDuringExecution => e
|
||||||
# Even if we have credentials, they may be invalid or malformed.
|
# Even if we have credentials, they may be invalid or malformed.
|
||||||
raise GhAuthNeeded, "invalid credentials" if e.status.exitstatus == 4
|
if e.status.exitstatus == 4 || e.stderr.include?("HTTP 401: Bad credentials")
|
||||||
|
raise GhAuthInvalid, "invalid credentials"
|
||||||
|
end
|
||||||
|
|
||||||
raise InvalidAttestationError, "attestation verification failed: #{e}"
|
raise InvalidAttestationError, "attestation verification failed: #{e}"
|
||||||
end
|
end
|
||||||
|
@ -1284,6 +1284,22 @@ on_request: installed_on_request?, options:)
|
|||||||
ohai "Verifying attestation for #{formula.name}"
|
ohai "Verifying attestation for #{formula.name}"
|
||||||
begin
|
begin
|
||||||
Homebrew::Attestation.check_core_attestation formula.bottle
|
Homebrew::Attestation.check_core_attestation formula.bottle
|
||||||
|
rescue Homebrew::Attestation::GhAuthInvalid
|
||||||
|
# Only raise an error if we explicitly opted-in to verification.
|
||||||
|
raise CannotInstallFormulaError, <<~EOS if Homebrew::EnvConfig.verify_attestations?
|
||||||
|
The bottle for #{formula.name} could not be verified.
|
||||||
|
|
||||||
|
This typically indicates an invalid GitHub API token.
|
||||||
|
|
||||||
|
If you have `HOMEBREW_GITHUB_API_TOKEN` set, check it is correct
|
||||||
|
or unset it and instead run:
|
||||||
|
|
||||||
|
gh auth login
|
||||||
|
EOS
|
||||||
|
|
||||||
|
# If we didn't explicitly opt-in, then quietly opt-out in the case of invalid credentials.
|
||||||
|
# Based on user reports, a significant number of users are running with stale tokens.
|
||||||
|
ENV["HOMEBREW_NO_VERIFY_ATTESTATIONS"] = "1"
|
||||||
rescue Homebrew::Attestation::GhAuthNeeded
|
rescue Homebrew::Attestation::GhAuthNeeded
|
||||||
raise CannotInstallFormulaError, <<~EOS
|
raise CannotInstallFormulaError, <<~EOS
|
||||||
The bottle for #{formula.name} could not be verified.
|
The bottle for #{formula.name} could not be verified.
|
||||||
|
@ -115,7 +115,7 @@ RSpec.describe Homebrew::Attestation do
|
|||||||
expect(described_class).to receive(:system_command!)
|
expect(described_class).to receive(:system_command!)
|
||||||
.with(fake_gh, args: ["attestation", "verify", cached_download, "--repo",
|
.with(fake_gh, args: ["attestation", "verify", cached_download, "--repo",
|
||||||
described_class::HOMEBREW_CORE_REPO, "--format", "json"],
|
described_class::HOMEBREW_CORE_REPO, "--format", "json"],
|
||||||
env: { "GH_TOKEN" => fake_gh_creds }, secrets: [fake_gh_creds],
|
env: { "GH_TOKEN" => fake_gh_creds, "GH_HOST" => "github.com" }, secrets: [fake_gh_creds],
|
||||||
print_stderr: false, chdir: HOMEBREW_TEMP)
|
print_stderr: false, chdir: HOMEBREW_TEMP)
|
||||||
.and_raise(ErrorDuringExecution.new(["foo"], status: fake_error_status))
|
.and_raise(ErrorDuringExecution.new(["foo"], status: fake_error_status))
|
||||||
|
|
||||||
@ -132,14 +132,14 @@ RSpec.describe Homebrew::Attestation do
|
|||||||
expect(described_class).to receive(:system_command!)
|
expect(described_class).to receive(:system_command!)
|
||||||
.with(fake_gh, args: ["attestation", "verify", cached_download, "--repo",
|
.with(fake_gh, args: ["attestation", "verify", cached_download, "--repo",
|
||||||
described_class::HOMEBREW_CORE_REPO, "--format", "json"],
|
described_class::HOMEBREW_CORE_REPO, "--format", "json"],
|
||||||
env: { "GH_TOKEN" => fake_gh_creds }, secrets: [fake_gh_creds],
|
env: { "GH_TOKEN" => fake_gh_creds, "GH_HOST" => "github.com" }, secrets: [fake_gh_creds],
|
||||||
print_stderr: false, chdir: HOMEBREW_TEMP)
|
print_stderr: false, chdir: HOMEBREW_TEMP)
|
||||||
.and_raise(ErrorDuringExecution.new(["foo"], status: fake_auth_status))
|
.and_raise(ErrorDuringExecution.new(["foo"], status: fake_auth_status))
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
described_class.check_attestation fake_bottle,
|
described_class.check_attestation fake_bottle,
|
||||||
described_class::HOMEBREW_CORE_REPO
|
described_class::HOMEBREW_CORE_REPO
|
||||||
end.to raise_error(described_class::GhAuthNeeded)
|
end.to raise_error(described_class::GhAuthInvalid)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "raises when gh returns invalid JSON" do
|
it "raises when gh returns invalid JSON" do
|
||||||
@ -149,7 +149,7 @@ RSpec.describe Homebrew::Attestation do
|
|||||||
expect(described_class).to receive(:system_command!)
|
expect(described_class).to receive(:system_command!)
|
||||||
.with(fake_gh, args: ["attestation", "verify", cached_download, "--repo",
|
.with(fake_gh, args: ["attestation", "verify", cached_download, "--repo",
|
||||||
described_class::HOMEBREW_CORE_REPO, "--format", "json"],
|
described_class::HOMEBREW_CORE_REPO, "--format", "json"],
|
||||||
env: { "GH_TOKEN" => fake_gh_creds }, secrets: [fake_gh_creds],
|
env: { "GH_TOKEN" => fake_gh_creds, "GH_HOST" => "github.com" }, secrets: [fake_gh_creds],
|
||||||
print_stderr: false, chdir: HOMEBREW_TEMP)
|
print_stderr: false, chdir: HOMEBREW_TEMP)
|
||||||
.and_return(fake_result_invalid_json)
|
.and_return(fake_result_invalid_json)
|
||||||
|
|
||||||
@ -166,7 +166,7 @@ RSpec.describe Homebrew::Attestation do
|
|||||||
expect(described_class).to receive(:system_command!)
|
expect(described_class).to receive(:system_command!)
|
||||||
.with(fake_gh, args: ["attestation", "verify", cached_download, "--repo",
|
.with(fake_gh, args: ["attestation", "verify", cached_download, "--repo",
|
||||||
described_class::HOMEBREW_CORE_REPO, "--format", "json"],
|
described_class::HOMEBREW_CORE_REPO, "--format", "json"],
|
||||||
env: { "GH_TOKEN" => fake_gh_creds }, secrets: [fake_gh_creds],
|
env: { "GH_TOKEN" => fake_gh_creds, "GH_HOST" => "github.com" }, secrets: [fake_gh_creds],
|
||||||
print_stderr: false, chdir: HOMEBREW_TEMP)
|
print_stderr: false, chdir: HOMEBREW_TEMP)
|
||||||
.and_return(fake_json_resp_wrong_sub)
|
.and_return(fake_json_resp_wrong_sub)
|
||||||
|
|
||||||
@ -183,7 +183,7 @@ RSpec.describe Homebrew::Attestation do
|
|||||||
expect(described_class).to receive(:system_command!)
|
expect(described_class).to receive(:system_command!)
|
||||||
.with(fake_gh, args: ["attestation", "verify", cached_download, "--repo",
|
.with(fake_gh, args: ["attestation", "verify", cached_download, "--repo",
|
||||||
described_class::HOMEBREW_CORE_REPO, "--format", "json"],
|
described_class::HOMEBREW_CORE_REPO, "--format", "json"],
|
||||||
env: { "GH_TOKEN" => fake_gh_creds }, secrets: [fake_gh_creds],
|
env: { "GH_TOKEN" => fake_gh_creds, "GH_HOST" => "github.com" }, secrets: [fake_gh_creds],
|
||||||
print_stderr: false, chdir: HOMEBREW_TEMP)
|
print_stderr: false, chdir: HOMEBREW_TEMP)
|
||||||
.and_return(fake_result_json_resp)
|
.and_return(fake_result_json_resp)
|
||||||
|
|
||||||
@ -204,7 +204,7 @@ RSpec.describe Homebrew::Attestation do
|
|||||||
expect(described_class).to receive(:system_command!)
|
expect(described_class).to receive(:system_command!)
|
||||||
.with(fake_gh, args: ["attestation", "verify", cached_download, "--repo",
|
.with(fake_gh, args: ["attestation", "verify", cached_download, "--repo",
|
||||||
described_class::HOMEBREW_CORE_REPO, "--format", "json"],
|
described_class::HOMEBREW_CORE_REPO, "--format", "json"],
|
||||||
env: { "GH_TOKEN" => fake_gh_creds }, secrets: [fake_gh_creds],
|
env: { "GH_TOKEN" => fake_gh_creds, "GH_HOST" => "github.com" }, secrets: [fake_gh_creds],
|
||||||
print_stderr: false, chdir: HOMEBREW_TEMP)
|
print_stderr: false, chdir: HOMEBREW_TEMP)
|
||||||
.and_return(fake_result_json_resp)
|
.and_return(fake_result_json_resp)
|
||||||
|
|
||||||
@ -215,7 +215,7 @@ RSpec.describe Homebrew::Attestation do
|
|||||||
expect(described_class).to receive(:system_command!)
|
expect(described_class).to receive(:system_command!)
|
||||||
.with(fake_gh, args: ["attestation", "verify", cached_download, "--repo",
|
.with(fake_gh, args: ["attestation", "verify", cached_download, "--repo",
|
||||||
described_class::HOMEBREW_CORE_REPO, "--format", "json"],
|
described_class::HOMEBREW_CORE_REPO, "--format", "json"],
|
||||||
env: { "GH_TOKEN" => fake_gh_creds }, secrets: [fake_gh_creds],
|
env: { "GH_TOKEN" => fake_gh_creds, "GH_HOST" => "github.com" }, secrets: [fake_gh_creds],
|
||||||
print_stderr: false, chdir: HOMEBREW_TEMP)
|
print_stderr: false, chdir: HOMEBREW_TEMP)
|
||||||
.once
|
.once
|
||||||
.and_raise(described_class::InvalidAttestationError)
|
.and_raise(described_class::InvalidAttestationError)
|
||||||
@ -223,7 +223,7 @@ RSpec.describe Homebrew::Attestation do
|
|||||||
expect(described_class).to receive(:system_command!)
|
expect(described_class).to receive(:system_command!)
|
||||||
.with(fake_gh, args: ["attestation", "verify", cached_download, "--repo",
|
.with(fake_gh, args: ["attestation", "verify", cached_download, "--repo",
|
||||||
described_class::BACKFILL_REPO, "--format", "json"],
|
described_class::BACKFILL_REPO, "--format", "json"],
|
||||||
env: { "GH_TOKEN" => fake_gh_creds }, secrets: [fake_gh_creds],
|
env: { "GH_TOKEN" => fake_gh_creds, "GH_HOST" => "github.com" }, secrets: [fake_gh_creds],
|
||||||
print_stderr: false, chdir: HOMEBREW_TEMP)
|
print_stderr: false, chdir: HOMEBREW_TEMP)
|
||||||
.and_return(fake_result_json_resp_backfill)
|
.and_return(fake_result_json_resp_backfill)
|
||||||
|
|
||||||
@ -234,7 +234,7 @@ RSpec.describe Homebrew::Attestation do
|
|||||||
expect(described_class).to receive(:system_command!)
|
expect(described_class).to receive(:system_command!)
|
||||||
.with(fake_gh, args: ["attestation", "verify", cached_download, "--repo",
|
.with(fake_gh, args: ["attestation", "verify", cached_download, "--repo",
|
||||||
described_class::HOMEBREW_CORE_REPO, "--format", "json"],
|
described_class::HOMEBREW_CORE_REPO, "--format", "json"],
|
||||||
env: { "GH_TOKEN" => fake_gh_creds }, secrets: [fake_gh_creds],
|
env: { "GH_TOKEN" => fake_gh_creds, "GH_HOST" => "github.com" }, secrets: [fake_gh_creds],
|
||||||
print_stderr: false, chdir: HOMEBREW_TEMP)
|
print_stderr: false, chdir: HOMEBREW_TEMP)
|
||||||
.once
|
.once
|
||||||
.and_raise(described_class::InvalidAttestationError)
|
.and_raise(described_class::InvalidAttestationError)
|
||||||
@ -242,7 +242,7 @@ RSpec.describe Homebrew::Attestation do
|
|||||||
expect(described_class).to receive(:system_command!)
|
expect(described_class).to receive(:system_command!)
|
||||||
.with(fake_gh, args: ["attestation", "verify", cached_download, "--repo",
|
.with(fake_gh, args: ["attestation", "verify", cached_download, "--repo",
|
||||||
described_class::BACKFILL_REPO, "--format", "json"],
|
described_class::BACKFILL_REPO, "--format", "json"],
|
||||||
env: { "GH_TOKEN" => fake_gh_creds }, secrets: [fake_gh_creds],
|
env: { "GH_TOKEN" => fake_gh_creds, "GH_HOST" => "github.com" }, secrets: [fake_gh_creds],
|
||||||
print_stderr: false, chdir: HOMEBREW_TEMP)
|
print_stderr: false, chdir: HOMEBREW_TEMP)
|
||||||
.and_return(fake_result_json_resp_too_new)
|
.and_return(fake_result_json_resp_too_new)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user