Skip to content

Filter sensitive environment variables during Ruby evals#22384

Merged
MikeMcQuaid merged 1 commit into
mainfrom
filter-env-ruby-loading
May 22, 2026
Merged

Filter sensitive environment variables during Ruby evals#22384
MikeMcQuaid merged 1 commit into
mainfrom
filter-env-ruby-loading

Conversation

@MikeMcQuaid

Copy link
Copy Markdown
Member

This provides a potential security improvement for untrusted Ruby code but has minimal enough overhead that we can just do it always. We may add an opt-out for this behaviour if it breaks things for people but it was already applied during install/test/postinstall blocks.

  • Hide token-like environment variables while formulae and casks are evaluated so top-level DSL code cannot read them.
  • Restore the caller environment after metadata loading and command parser requires so command execution keeps its normal environment.
  • Cover formula, cask and external command require paths.

  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests for the same change?
  • Have you added an explanation of what your changes do and why you'd like us to include them? Performance claims (e.g. "this is faster") must include Hyperfine benchmarks.
  • Have you written new tests (excluding integration tests) for your changes? Here's an example.
  • Have you successfully run brew lgtm (style, typechecking and tests) with your changes locally?

  • AI was used to generate or assist with generating this PR. Please specify below how you used AI to help you, and what steps you have taken to manually verify the changes. Non-maintainers may only have one AI-assisted/generated PR open at a time.

OpenAI Codex 5.5 xhigh with local review and testing.


Copilot AI review requested due to automatic review settings May 22, 2026 11:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tightens security during evaluation of untrusted Ruby code by temporarily removing “token-like” environment variables while formulae, casks, and external Ruby commands are eval’d/require’d, then restoring the original environment afterward so normal command execution still sees the caller’s environment.

Changes:

  • Extend ENV.clear_sensitive_environment! to support a block form that restores the environment after yielding.
  • Wrap formula and cask evaluation (module_eval / instance_eval) in ENV.clear_sensitive_environment! to prevent top-level DSL code from reading sensitive variables.
  • Wrap external Ruby command loading (Homebrew.require?) in the same environment-scrubbing block and add test coverage for formula, cask, and brew help paths.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.

Show a summary per file
File Description
Library/Homebrew/extend/ENV.rb Adds block form to clear_sensitive_environment! with ensure-based restoration.
Library/Homebrew/formulary.rb Scrubs sensitive env vars during formula module_eval loading.
Library/Homebrew/cask/cask_loader.rb Scrubs sensitive env vars during cask instance_eval loading (content + path loaders).
Library/Homebrew/commands.rb Scrubs sensitive env vars while require-loading external Ruby v2 commands.
Library/Homebrew/cli/parser.rb Scrubs sensitive env vars while require-loading command parsers from Ruby command files.
Library/Homebrew/test/ENV_spec.rb Adds spec verifying env restoration and non-persistence of block mutations.
Library/Homebrew/test/formulary_spec.rb Adds spec ensuring formula top-level code can’t see sensitive env vars and env is restored.
Library/Homebrew/test/cask/cask_loader_spec.rb Adds spec ensuring cask DSL evaluation can’t see sensitive env vars and env is restored.
Library/Homebrew/test/cmd/help_spec.rb Adds integration coverage ensuring external command require doesn’t leak sensitive env vars.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@MikeMcQuaid MikeMcQuaid force-pushed the filter-env-ruby-loading branch from 50499ce to 5f59f92 Compare May 22, 2026 12:24
@MikeMcQuaid MikeMcQuaid enabled auto-merge May 22, 2026 13:27
@MikeMcQuaid MikeMcQuaid added this pull request to the merge queue May 22, 2026
@MikeMcQuaid MikeMcQuaid removed this pull request from the merge queue due to the queue being cleared May 22, 2026
@MikeMcQuaid MikeMcQuaid added this pull request to the merge queue May 22, 2026
@MikeMcQuaid MikeMcQuaid removed this pull request from the merge queue due to a manual request May 22, 2026
- Hide token-like environment variables while formulae and casks are
  evaluated so top-level DSL code cannot read them.
- Restore the caller environment after metadata loading and command parser
  requires so command execution keeps its normal environment.
- Keep the filtering extension lightweight so command parser loading does
  not require slow startup dependencies.
- Use `requires_ancestor` for the ENV module typing rather than
  `T.unsafe(self)`.
- Cover formula, cask and external command require paths.
@MikeMcQuaid MikeMcQuaid force-pushed the filter-env-ruby-loading branch from 5f59f92 to 0dc0566 Compare May 22, 2026 15:28
@MikeMcQuaid MikeMcQuaid enabled auto-merge May 22, 2026 15:31
@MikeMcQuaid MikeMcQuaid added this pull request to the merge queue May 22, 2026
Merged via the queue into main with commit abfb9d0 May 22, 2026
37 checks passed
@MikeMcQuaid MikeMcQuaid deleted the filter-env-ruby-loading branch May 22, 2026 16:16
caarlos0 added a commit to goreleaser/goreleaser that referenced this pull request May 29, 2026
Fixes #6638.

## Problem

Homebrew 5.1.14
([Homebrew/brew#22384](Homebrew/brew#22384))
scrubs sensitive environment variables (including
`HOMEBREW_GITHUB_API_TOKEN`) for the duration of cask `instance_eval`.
The previously documented `homebrew_casks` private-repo example
interpolates `#{GitHubHelper.token}` directly inside `url.template` and
`url.headers`, both of which are evaluated at cask-load time — exactly
when the token is hidden. Every `brew install`/`update`/`upgrade`
against an affected tap fails with:

> Error: Failed to retrieve github api token

## Fix

Docs-only — replace the snippet with a `CurlDownloadStrategy` subclass
referenced via `url.using:`. `using:` takes a **class reference**, so
cask-load only captures the constant; Homebrew restores the environment
before instantiating the strategy and calling `fetch`, so the token
lookup happens at download time and works again.

Also added a `[!WARNING]` callout explaining the 5.1.14 regression so
users hitting the error in the wild land on this section via search.

## Notes

- No Go changes — `url.template`, `url.using`, `url.verified`,
`url.headers` already exist on `HomebrewCaskURL` and render correctly
via `internal/pipe/cask/templates/additional_url_params.rb`.
- Existing Go tests that use the old Ruby snippet as a `custom_block`
fixture were left as-is — they exercise Go template rendering, not
actual brew behavior, so they're still valid.
- The "optional enhancement" of auto-generating the download strategy
from a flag is intentionally out of scope (per maintainer comment on the
issue: private repos use varied URL templates, so this shouldn't be
baked in.
EOF
)

Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants