Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions .github/workflows/regenerate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ jobs:
HOMEBREW_NO_INSTALL_FROM_API: 1
run: brew generate-vulns-advisories advisories

- name: Concatenate advisories
run: bundle exec rake advisories:concat

- name: Configure git
uses: Homebrew/actions/git-user-config@8f3d1ec8a696b3b9d9a6c3696b6c73033cab69e4 # 2026.08.14.1
with:
Expand All @@ -69,7 +66,7 @@ jobs:
env:
BRANCH: regenerate-advisories
run: |
git add advisories/ data/
git add advisories/
Comment thread
p-linnane marked this conversation as resolved.
if git diff --cached --quiet; then
echo "No changes."
exit 0
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/.bundle/
/data/advisories.json
/vendor/bundle/
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

Advisories in this repository come from three places: records generated from `resolves` annotations on `homebrew/core` formula patches, candidate records matched against external OSV feeds by `brew advisory-match`, and records contributed directly by pull request. All live under `advisories/` as [OSV-schema](https://ossf.github.io/osv-schema/) JSON and are validated against that schema on every push.

The concatenated advisory index is built from the reviewed records on `main` and served at [formulae.brew.sh/api/advisories.json](https://formulae.brew.sh/api/advisories.json).

## Reporting a fixed vulnerability

If a `homebrew/core` formula already ships a patch that fixes a CVE, the simplest route is to annotate the patch. Open a pull request against [Homebrew/homebrew-core](https://github.com/Homebrew/homebrew-core) adding `resolves "CVE-YYYY-NNNNN"` to the relevant `patch do` block; see [`libquicktime.rb`](https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/lib/libquicktime.rb) for an example. CVE identifiers appearing in the patch URL or applied file paths are picked up automatically without an explicit `resolves`. The daily `Regenerate` workflow will pick it up and write a `BREW-<formula>-<CVE>` record here automatically. Generated records carry `"database_specific": {"source": "generated"}`. The workflow refreshes their upstream-derived fields (`summary`, `severity`, `references`, `ecosystem_specific.patches`) on each run but preserves `published` and `affected[].ranges` from whatever is on disk, so a hand-corrected `fixed` boundary will not be overwritten.
Expand Down
16 changes: 9 additions & 7 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,20 @@ namespace :advisories do
AdvisoryIndex.write("advisories", "data/advisories.json")
end

desc "Summarize data/advisories.json changes against HEAD as Markdown"
desc "Summarize advisories/ changes against HEAD as Markdown"
task :summary do
require "json"
require "open3"
require "tmpdir"
require_relative "lib/advisory_change_summary"
require_relative "lib/advisory_index"

before_json, status = Open3.capture2("git", "show", "HEAD:data/advisories.json")
abort "could not read data/advisories.json from HEAD" unless status.success?
before = Dir.mktmpdir do |dir|
statuses = Open3.pipeline(["git", "archive", "HEAD", "advisories/"], ["tar", "-x", "-C", dir])
abort "could not extract advisories/ from HEAD" unless statuses.all?(&:success?)

before = JSON.parse(before_json)
after = JSON.parse(File.read("data/advisories.json"))
puts AdvisoryChangeSummary.render(before, after)
AdvisoryIndex.build(File.join(dir, "advisories"))
end
puts AdvisoryChangeSummary.render(before, AdvisoryIndex.build("advisories"))
end

desc "Delete uncomparable and rejected matched candidates named as untracked paths on standard input"
Expand Down
1 change: 0 additions & 1 deletion data/advisories.json

This file was deleted.

5 changes: 2 additions & 3 deletions lib/advisory_change_summary.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

# Renders a concise pull request summary for changes to data/advisories.json.
# Renders a concise pull request summary for changes to the advisory records.
module AdvisoryChangeSummary
module_function

Expand All @@ -14,8 +14,7 @@ def render(before, after, formula_limit: 50)
.to_h { |id| [id, after_records.fetch(id)] }

lines = [
"Automated regeneration via `brew generate-vulns-advisories`, followed by rebuilding " \
"`data/advisories.json`.",
"Automated regeneration via `brew generate-vulns-advisories`.",
"",
]
lines << change_line("Added", added.values) unless added.empty?
Expand Down
6 changes: 3 additions & 3 deletions lib/advisory_index.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
require "json"

# Concatenates every record under `advisories/` into a single
# `data/advisories.json` indexed by formula name, so consumers
# (`brew generate-formula-api`, `brew vulns`, formulae.brew.sh) can fetch the
# whole corpus in one request instead of one per record.
# `data/advisories.json` indexed by formula name for local validation. The
# production index is built from the reviewed records on `main` and published
# by formulae.brew.sh.
#
# Output shape:
#
Expand Down
2 changes: 1 addition & 1 deletion spec/advisory_change_summary_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def index(*records, skipped: 0, schema: "1.7.3")
)

expect(described_class.render(before, after)).to eq <<~MARKDOWN.chomp
Automated regeneration via `brew generate-vulns-advisories`, followed by rebuilding `data/advisories.json`.
Automated regeneration via `brew generate-vulns-advisories`.

- Added 1 matched advisory.
- Updated 1 generated advisory.
Expand Down
14 changes: 10 additions & 4 deletions spec/workflows_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@
require "yaml"

RSpec.describe "workflow commands" do
it "runs regenerate advisory concatenation with Bundler" do
it "regenerates and commits only per-record advisories" do
path = File.expand_path("../.github/workflows/regenerate.yml", __dir__)
workflow = YAML.safe_load_file(path, aliases: true)
steps = workflow.fetch("jobs").values.flat_map { |job| job.fetch("steps", []) }
concat_step = steps.find { |step| step["name"] == "Concatenate advisories" }

expect(concat_step.fetch("run")).to eq "bundle exec rake advisories:concat"
commit_step = steps.find { |step| step["name"] == "Commit advisories" }

expect({
concat_step: steps.any? { |step| step["name"] == "Concatenate advisories" },
staged_paths: commit_step.fetch("run").lines.grep(/git add/).map(&:strip),
}).to eq({
concat_step: false,
staged_paths: ["git add advisories/"],
})
end

it "authors automation commits as BrewTestBot with signing and a machine token" do
Expand Down