Skip to content

Commit ef45fef

Browse files
committed
Preserve GitHub token during eval
- Keep `$HOMEBREW_GITHUB_API_TOKEN` available while formulae and casks are evaluated so private taps can keep resolving assets. - Share the eval scrubbing policy through `ENV` so formula and cask loaders do not drift. - Add temporary `HOMEBREW_NO_EVAL_ENV_SCRUBBING` for users who need a short-term escape hatch while private taps migrate.
1 parent 028c262 commit ef45fef

8 files changed

Lines changed: 120 additions & 7 deletions

File tree

Library/Homebrew/cask/cask_loader.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def initialize(content, tap: T.unsafe(nil))
103103
def load(config:)
104104
@config = config
105105

106-
ENV.clear_sensitive_environment! do
106+
ENV.clear_sensitive_environment_for_eval! do
107107
instance_eval(content, __FILE__, __LINE__)
108108
end
109109
end
@@ -190,7 +190,7 @@ def load(config:)
190190
end
191191

192192
begin
193-
ENV.clear_sensitive_environment! do
193+
ENV.clear_sensitive_environment_for_eval! do
194194
instance_eval(content, path.to_s).tap do |cask|
195195
raise CaskUnreadableError.new(token, "'#{path}' does not contain a cask.") unless cask.is_a?(Cask)
196196
end

Library/Homebrew/env_config.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,13 @@ module EnvConfig
430430
description: "If set, do not print any hints about changing Homebrew's behaviour with environment variables.",
431431
boolean: true,
432432
},
433+
HOMEBREW_NO_EVAL_ENV_SCRUBBING: {
434+
# odeprecated: remove in a later release
435+
description: "If set, sensitive environment variables are available while evaluating formulae and casks. " \
436+
"`$HOMEBREW_GITHUB_API_TOKEN` is still available during evaluation when this is unset. " \
437+
"This setting will be removed in a later release.",
438+
boolean: true,
439+
},
433440
HOMEBREW_NO_FORCE_BREW_WRAPPER: {
434441
description: "`Deprecated:` If set, disables `$HOMEBREW_FORCE_BREW_WRAPPER` behaviour, even if set.",
435442
boolean: true,

Library/Homebrew/extend/ENV/sensitive.rb

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# typed: strict
22
# frozen_string_literal: true
33

4+
require "env_config"
5+
46
module EnvSensitive
57
extend T::Helpers
68

@@ -16,21 +18,28 @@ def sensitive_environment
1618
select { |key, _| sensitive?(key) }
1719
end
1820

19-
sig { params(block: T.nilable(T.proc.returns(T.untyped))).returns(T.untyped) }
20-
def clear_sensitive_environment!(&block)
21+
sig { params(except: T::Array[String], block: T.nilable(T.proc.returns(T.untyped))).returns(T.untyped) }
22+
def clear_sensitive_environment!(except: [], &block)
2123
unless block
22-
each_key { |key| delete key if sensitive?(key) }
24+
each_key { |key| delete key if sensitive?(key) && except.exclude?(key) }
2325
return
2426
end
2527

2628
old_env = to_hash.dup
2729
begin
28-
clear_sensitive_environment!
30+
clear_sensitive_environment!(except:)
2931
yield
3032
ensure
3133
replace(old_env)
3234
end
3335
end
36+
37+
sig { params(block: T.proc.returns(T.untyped)).returns(T.untyped) }
38+
def clear_sensitive_environment_for_eval!(&block)
39+
return yield if Homebrew::EnvConfig.no_eval_env_scrubbing?
40+
41+
clear_sensitive_environment!(except: ["HOMEBREW_GITHUB_API_TOKEN"], &block)
42+
end
3443
end
3544

3645
ENV.extend(EnvSensitive)

Library/Homebrew/formulary.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ def self.load_formula(name, path, contents, namespace, flags:, ignore_errors:)
152152
raise FormulaUnreadableError.new(name, e)
153153
end
154154
end
155-
ENV.clear_sensitive_environment! do
155+
ENV.clear_sensitive_environment_for_eval! do
156156
if ignore_errors
157157
Ignorable.hook_raise(&eval_formula)
158158
else

Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Library/Homebrew/test/ENV_spec.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,12 @@
157157
expect(subject).not_to include("SECRET_TOKEN")
158158
end
159159

160+
it "preserves excepted sensitive environment variables" do
161+
subject["SECRET_TOKEN"] = "password"
162+
subject.clear_sensitive_environment!(except: ["SECRET_TOKEN"])
163+
expect(subject["SECRET_TOKEN"]).to eq("password")
164+
end
165+
160166
it "leaves non-sensitive environment variables alone" do
161167
subject["FOO"] = "bar"
162168
subject.clear_sensitive_environment!

Library/Homebrew/test/cask/cask_loader_spec.rb

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,54 @@
299299
end
300300
end
301301

302+
it "allows the GitHub API token while evaluating casks" do
303+
cask_token = "github-token-env"
304+
cask_file = mktmpdir/"#{cask_token}.rb"
305+
cask_file.write <<~RUBY
306+
cask "#{cask_token}" do
307+
version "1.0.0"
308+
sha256 "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
309+
310+
url "https://example.com/app.dmg"
311+
name "GitHub Token Env"
312+
desc ENV.key?("HOMEBREW_GITHUB_API_TOKEN") ? "Token present" : "Token absent"
313+
homepage "https://example.com"
314+
315+
app "App.app"
316+
end
317+
RUBY
318+
319+
with_env(HOMEBREW_GITHUB_API_TOKEN: "github-token") do
320+
cask = Cask::CaskLoader::FromPathLoader.new(cask_file).load(config: nil)
321+
322+
expect(cask.desc).to eq("Token present")
323+
end
324+
end
325+
326+
it "supports temporarily opting out of scrubbing while evaluating casks" do
327+
cask_token = "unscrubbed-env"
328+
cask_file = mktmpdir/"#{cask_token}.rb"
329+
cask_file.write <<~RUBY
330+
cask "#{cask_token}" do
331+
version "1.0.0"
332+
sha256 "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
333+
334+
url "https://example.com/app.dmg"
335+
name "Unscrubbed Env"
336+
desc ENV.key?("SECRET_TOKEN") ? "Secret present" : "Secret absent"
337+
homepage "https://example.com"
338+
339+
app "App.app"
340+
end
341+
RUBY
342+
343+
with_env(HOMEBREW_NO_EVAL_ENV_SCRUBBING: "1", SECRET_TOKEN: "password") do
344+
cask = Cask::CaskLoader::FromPathLoader.new(cask_file).load(config: nil)
345+
346+
expect(cask.desc).to eq("Secret present")
347+
end
348+
end
349+
302350
describe "loading a cask with a removed DSL method" do
303351
let(:tmpdir) { mktmpdir }
304352
let(:cask_token) { "removed-method-cask" }

Library/Homebrew/test/formulary_spec.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,46 @@ class SensitiveEnv < Formula
7878
expect(ENV.fetch("SECRET_TOKEN", nil)).to eq("password")
7979
end
8080
end
81+
82+
it "allows the GitHub API token while evaluating formulae" do
83+
with_env(HOMEBREW_GITHUB_API_TOKEN: "github-token") do
84+
formula_class = Formulary.load_formula(
85+
"github-token-env",
86+
mktmpdir/"github-token-env.rb",
87+
<<~RUBY,
88+
class GithubTokenEnv < Formula
89+
GITHUB_TOKEN_PRESENT = ENV.key?("HOMEBREW_GITHUB_API_TOKEN")
90+
url "https://brew.sh/github-token-env-1.0.tar.gz"
91+
end
92+
RUBY
93+
"GithubTokenEnvNamespace",
94+
flags: [],
95+
ignore_errors: false,
96+
)
97+
98+
expect(formula_class::GITHUB_TOKEN_PRESENT).to be(true)
99+
end
100+
end
101+
102+
it "supports temporarily opting out of scrubbing while evaluating formulae" do
103+
with_env(HOMEBREW_NO_EVAL_ENV_SCRUBBING: "1", SECRET_TOKEN: "password") do
104+
formula_class = Formulary.load_formula(
105+
"unscrubbed-env",
106+
mktmpdir/"unscrubbed-env.rb",
107+
<<~RUBY,
108+
class UnscrubbedEnv < Formula
109+
SECRET_TOKEN_PRESENT = ENV.key?("SECRET_TOKEN")
110+
url "https://brew.sh/unscrubbed-env-1.0.tar.gz"
111+
end
112+
RUBY
113+
"UnscrubbedEnvNamespace",
114+
flags: [],
115+
ignore_errors: false,
116+
)
117+
118+
expect(formula_class::SECRET_TOKEN_PRESENT).to be(true)
119+
end
120+
end
81121
end
82122

83123
describe "::factory" do

0 commit comments

Comments
 (0)