Add brew vulns command#23080
Conversation
For installed formulae, read the source download URL from the keg's sbom.spdx.json (SPDXRef-Archive-*-src) so the OSV query and reported version reflect what is actually on disk. Patch suppression only applies when the scanned keg matches the current recipe's PkgVersion. When an outdated keg has no SBOM the formula is recorded in Results#outdated_without_sbom for the caller to surface.
There was a problem hiding this comment.
Pull request overview
This PR introduces a new brew vulns command to scan formulae for known vulnerabilities using OSV.dev, reusing SBOM metadata when available and integrating patch resolves suppression into the results.
Changes:
- Add the
brew vulnsCLI command with options for scope selection, severity filtering, and JSON/text output. - Implement the OSV client, scanning orchestration, SemVer range evaluation, CVSS v3.x severity scoring, and output formatting under
Library/Homebrew/vulns/. - Add RSpec coverage plus documentation and shell completions/manpage updates for the new command.
Reviewed changes
Copilot reviewed 22 out of 23 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| manpages/brew.1 | Adds brew vulns to the generated manpage. |
| Library/Homebrew/vulns/vulnerability.rb | OSV vulnerability record wrapper with severity and version-range evaluation. |
| Library/Homebrew/vulns/semver.rb | SemVer 2.0 comparator used for OSV SEMVER ranges. |
| Library/Homebrew/vulns/scanner.rb | Scanner that derives repo/tag targets (SBOM or formula URL), queries OSV, and applies patch suppression. |
| Library/Homebrew/vulns/output.rb | Text and JSON renderers, including sanitization of OSV-sourced strings. |
| Library/Homebrew/vulns/osv.rb | OSV API client (query batch + per-vuln fetch) using Utils::Curl. |
| Library/Homebrew/vulns/cvss.rb | CVSS v3.0/v3.1 base score + severity bucketing. |
| Library/Homebrew/vulns.rb | Aggregating require entrypoint for the vulns subsystem. |
| Library/Homebrew/test/vulns/vulnerability_spec.rb | Unit tests for OSV record parsing, severity extraction, and range matching. |
| Library/Homebrew/test/vulns/semver_spec.rb | Unit tests for SemVer parsing/comparison behavior. |
| Library/Homebrew/test/vulns/scanner_spec.rb | Scanner tests covering targeting, OSV interaction, filtering, and patch suppression. |
| Library/Homebrew/test/vulns/output_spec.rb | Output formatting tests for both text and JSON formats. |
| Library/Homebrew/test/vulns/osv_spec.rb | OSV client tests including pagination and error handling. |
| Library/Homebrew/test/vulns/cvss_spec.rb | CVSS scoring and severity bucketing tests. |
| Library/Homebrew/test/support/fixtures/vulns/sbom.spdx.json | SBOM fixture for extracting source URL/tag data. |
| Library/Homebrew/test/cmd/vulns_spec.rb | Command-level tests for arg validation, output mode, and failure behavior. |
| Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/vulns.rbi | Tapioca-generated RBI for Homebrew::Cmd::Vulns args accessors. |
| Library/Homebrew/cmd/vulns.rb | Implements the new brew vulns command and option handling. |
| docs/Manpage.md | Adds user-facing documentation for brew vulns. |
| completions/zsh/_brew | Adds zsh completion entries for brew vulns. |
| completions/internal_commands_list.txt | Registers vulns as an internal command for completion generation. |
| completions/fish/brew.fish | Adds fish completion entries for brew vulns. |
| completions/bash/brew | Adds bash completion entries for brew vulns. |
Files not reviewed (1)
- Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/vulns.rbi: File type not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
MikeMcQuaid
left a comment
There was a problem hiding this comment.
Looks good so far! Very close to mergeable as-is honestly.
Restores the optional-argument form from the tap so `brew vulns --brewfile` defaults to ./Brewfile (or HOMEBREW_BUNDLE_FILE). `--brewfile=<path>` still selects a specific file; the bare/empty value is normalised to nil for Bundle::Brewfile.read.
Widen repo_url to also consider the formula homepage, fall back to stable.specs[:tag] then the stable version string when no tag can be extracted from the source URL, and read the SBOM versionInfo alongside downloadLocation. This recovers coverage for formulae like curl, git, node and libssh2 whose stable URL is not a supported forge archive.
p-linnane
left a comment
There was a problem hiding this comment.
Thanks. The Brewfile, tag/version, and SBOM versionInfo fixes look good and cover the packages that matter (curl, node, rust, ruby, git, and libssh2 all scan now). One tap fallback is still missing: repo_url returns nil unless a URL is on GitHub, GitLab, or Codeberg, so the verbatim (source_url if @stable_tag) || head_url path is gone. That still skips 7 installed formulae here (bash, gmp, gnu-tar, go, make, openconnect, and zsh), each with a non-forge head. It's low priority, since OSV's GIT ecosystem returns nothing for those hosts today, so this is parity and future-proofing rather than a real detection loss. If you restore it, a non-forge-head fixture would cover the path. Not blocking though.
--eval-all was removed during review of Homebrew/brew#23080 in favour of the brew-wide tap trust mechanism.
repo_url only normalises GitHub, GitLab and Codeberg, so formulae whose stable, head and homepage URLs are all hosted elsewhere (bash, gmp, gnu-tar, go, make, openconnect, zsh) were skipped outright. Restore the tap's last-resort fallback: query OSV with the source URL verbatim when its path yielded a tag, otherwise the head URL verbatim. OSV's GIT ecosystem returns nothing for these hosts today, so this is parity with the tap and future-proofing rather than a detection change. Follow-up to Homebrew#23080.
Adds a
brew vulnscommand that checks installed formulae for known security vulnerabilities against the OSV.dev database. This upstreams the scanner from thehomebrew-brew-vulnstap so users get vulnerability reporting without installing a separate tap and gem, and so it can read the SBOM data brew already writes into each keg.Tracking issue with the full rationale and migration plan: Homebrew/homebrew-brew-vulns#111.
With no arguments it scans every installed formula; named formulae,
--deps,--brewfileand--eval-allnarrow or widen the set. Results are filtered by CVSS severity with--severityand rendered as text or--json. Patches that declareresolvessuppress the matching advisories unless--no-ignore-patchesis passed (building on #22466).The layout follows
livecheck: a thincmd/vulns.rbentry point delegating to modules underLibrary/Homebrew/vulns/(scanner orchestration, OSV client onUtils::Curl,VulnerabilityOSV record wrapper, a strict SemVer 2.0 comparator, a CVSS v3.x/v4.0 base-score calculator, and text/JSON output). No gems are added; the small pieces ofpurl,versandcvss-suitethat were needed have been reimplemented inline. SARIF and CycloneDX output from the tap version are dropped for now and can come back later as plain JSON serialisation.Opening as a draft for early feedback on the module layout and CLI surface before polishing docs further.
brewcommands to reproduce the bug?brew lgtm(style, typechecking and tests) locally?A combo of translation and review from https://github.com/Homebrew/homebrew-brew-vulns with bits of claude and codex