Skip to content

fix(brew): detect Homebrew installed outside PATH#172

Merged
ashishkurmi merged 3 commits into
step-security:mainfrom
swarit-stepsecurity:swarit/fix/homebrew-scan-missing
Jul 20, 2026
Merged

fix(brew): detect Homebrew installed outside PATH#172
ashishkurmi merged 3 commits into
step-security:mainfrom
swarit-stepsecurity:swarit/fix/homebrew-scan-missing

Conversation

@swarit-stepsecurity

Copy link
Copy Markdown
Member

What

Homebrew detection previously relied on brew being on the process PATH
(exec.LookPath("brew")) and shelled out to brew --version. Scans that run
without the user's interactive shell environment (e.g. scheduled/agent runs)
often have no brew on PATH, so Homebrew went undetected even when installed.

Changes

  • Resolve brew outside PATH. New resolveBrewExecutable falls back to the
    standard install locations when LookPath fails:
    /opt/homebrew/bin/brew, /usr/local/bin/brew,
    /home/linuxbrew/.linuxbrew/bin/brew. All invocations
    (DetectBrew, ListFormulae, ListCasks, ListFormulaeRich,
    ListCasksRich) use the resolved command.
  • Read version from git metadata, not brew --version. readBrewVersion
    derives the version from the Homebrew git repo (HEADdescribe-cache /
    packed-refs exact tag), avoiding a subprocess and working when brew isn't on
    PATH. Falls back to "unknown" when metadata is unavailable.

Tests

Added coverage for detection at a standard path outside PATH, the
<prefix>/Homebrew repository layout, version-from-packed-tag, and the
unknown-version fallback. Full detector suite passes.

Notes

Rebased onto latest upstream/main. The prior branch also touched
brewscan.go, but upstream has since refactored BrewScanner to synthesize
scan output from rich brew info --json=v2 data rather than shelling out to
brew list — so those scanner changes are obsolete and were dropped. The
outside-PATH fix now flows entirely through brew.go, which feeds the scanner.

Resolve the brew executable from standard install locations (/opt/homebrew,
/usr/local, /home/linuxbrew) when it is not on PATH, and read the version from
the Homebrew git repo metadata (describe-cache / packed-refs) instead of
shelling out to `brew --version`. Detection and formula/cask listing now work
for scans run without the user's interactive PATH.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Improves Homebrew (brew) detection so scans running in non-interactive environments can still find Homebrew even when brew is not on PATH, and derives the Homebrew version from local git metadata instead of invoking brew --version.

Changes:

  • Add resolveBrewExecutable to locate brew via LookPath or common install paths, and use the resolved executable for brew invocations.
  • Add git-metadata-based version resolution (HEAD + describe-cache / packed-refs) with an "unknown" fallback.
  • Add unit tests covering outside-PATH detection and git-based version resolution scenarios.

Reviewed changes

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

File Description
internal/detector/brew.go Implements outside-PATH brew executable resolution and git-based version derivation.
internal/detector/brew_test.go Adds tests for standard-path discovery and version-from-git/packed-refs fallbacks.

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

Comment thread internal/detector/brew.go
Comment on lines +235 to +238
func resolveBrewExecutable(exec executor.Executor) (brewExecutable, error) {
if path, err := exec.LookPath("brew"); err == nil {
return brewExecutable{path: path, command: "brew"}, nil
}
Comment thread internal/detector/brew.go
Comment on lines +306 to +311
if data, err := exec.ReadFile(filepath.Join(gitDir, "describe-cache", revision)); err == nil {
version := strings.TrimSpace(string(data))
if version != "" && !strings.Contains(version, "-dirty") {
return version
}
}
swarit-stepsecurity and others added 2 commits July 18, 2026 12:29
- resolveBrewExecutable now uses the resolved absolute path as the command
  even when found via PATH, so all invocations are PATH-independent.
- describe-cache version is reduced to its base tag and validated against
  brewVersionTagPattern, so strings like "4.3.5-17-g<sha>" no longer leak
  into PkgManager.Version.
@ashishkurmi
ashishkurmi merged commit e96ecd3 into step-security:main Jul 20, 2026
12 checks passed
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