diff --git a/changes/26977-macoffice-archives b/changes/26977-macoffice-archives new file mode 100644 index 0000000000..065ad4887c --- /dev/null +++ b/changes/26977-macoffice-archives @@ -0,0 +1 @@ +* Added new (as of 2025-03-07) archives page to data source for MS Mac Office vulnerability feed (applies to vulnerabilities feed rather than a specific Fleet release). diff --git a/cmd/macoffice/generate.go b/cmd/macoffice/generate.go index b419a117a2..b0308d7ffc 100644 --- a/cmd/macoffice/generate.go +++ b/cmd/macoffice/generate.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "net/http" "os" "path/filepath" "time" @@ -26,23 +25,10 @@ func main() { panicif(err) fmt.Println("Downloading and parsing Mac Office rel notes...") - res, err := http.Get(macoffice.RelNotesURL) - panicif(err) - defer res.Body.Close() - parsed, err := macoffice.ParseReleaseHTML(res.Body) + relNotes, err := macoffice.GetReleaseNotes(false) panicif(err) - var relNotes macoffice.ReleaseNotes - for _, rn := range parsed { - // We only care about release notes that have a version set (because we need that for - // matching software entries) and also that contain some - // security updates (because we only intented to use the release notes for vulnerability processing). - if rn.Valid() { - relNotes = append(relNotes, rn) - } - } - err = relNotes.Serialize(time.Now(), outPath) panicif(err) diff --git a/server/datastore/mysql/migrations/tables/20250127162751_AddUnifiedQueueTable_test.go b/server/datastore/mysql/migrations/tables/20250127162751_AddUnifiedQueueTable_test.go index 70d1cacf3d..63e49482c8 100644 --- a/server/datastore/mysql/migrations/tables/20250127162751_AddUnifiedQueueTable_test.go +++ b/server/datastore/mysql/migrations/tables/20250127162751_AddUnifiedQueueTable_test.go @@ -45,5 +45,5 @@ func TestUp_20250127162751(t *testing.T) { FROM information_schema.COLUMNS WHERE collation_name != "utf8mb4_unicode_ci" AND table_schema = (SELECT database())`) require.NoError(t, err) - require.Equal(t, []string{"secret", "node_key", "orbit_node_key", "name_bin"}, columns) + require.ElementsMatch(t, []string{"secret", "node_key", "orbit_node_key", "name_bin"}, columns) } diff --git a/server/vulnerabilities/macoffice/integration_parser_test.go b/server/vulnerabilities/macoffice/integration_parser_test.go index d8f218d326..0482b91ba8 100644 --- a/server/vulnerabilities/macoffice/integration_parser_test.go +++ b/server/vulnerabilities/macoffice/integration_parser_test.go @@ -1,7 +1,6 @@ package macoffice_test import ( - "net/http" "testing" "time" @@ -656,11 +655,7 @@ var expected = []macoffice.ReleaseNote{ func TestIntegrationsParseReleaseHTML(t *testing.T) { nettest.Run(t) - res, err := http.Get(macoffice.RelNotesURL) - require.NoError(t, err) - defer res.Body.Close() - - actual, err := macoffice.ParseReleaseHTML(res.Body) + actual, err := macoffice.GetReleaseNotes(true) require.NoError(t, err) require.NotEmpty(t, actual) diff --git a/server/vulnerabilities/macoffice/parser.go b/server/vulnerabilities/macoffice/parser.go index dcd620a71f..5b0c2b3b4a 100644 --- a/server/vulnerabilities/macoffice/parser.go +++ b/server/vulnerabilities/macoffice/parser.go @@ -2,6 +2,7 @@ package macoffice import ( "io" + "net/http" "regexp" "strings" "time" @@ -33,6 +34,52 @@ var nameToType = map[string]ProductType{ "microsoft autoupdate": WholeSuite, } +type releaseNotesURL string // type added to calm gosec variable URL warnings down +const ( + primaryURL = releaseNotesURL("https://learn.microsoft.com/en-us/officeupdates/release-notes-office-for-mac") + archiveURL = releaseNotesURL("https://learn.microsoft.com/en-us/officeupdates/release-notes-office-for-mac-archived") +) + +func GetReleaseNotes(includeInvalid bool) (ReleaseNotes, error) { + var relNotes ReleaseNotes + + relNotes, err := addReleaseNotes(relNotes, primaryURL, includeInvalid) + if err != nil { + return nil, err + } + + relNotes, err = addReleaseNotes(relNotes, archiveURL, includeInvalid) + if err != nil { + return nil, err + } + + return relNotes, nil +} + +func addReleaseNotes(relNotes ReleaseNotes, url releaseNotesURL, includeInvalid bool) (ReleaseNotes, error) { + res, err := http.Get(string(url)) + if err != nil { + return nil, err + } + defer res.Body.Close() + + parsed, err := parseReleaseHTML(res.Body) + if err != nil { + return nil, err + } + + for _, rn := range parsed { + // Under normal operation (outside parser tests), we only care about release notes that have a version set + // (because we need that for matching software entries) and also that contain some + // security updates (because we only intend to use the release notes for vulnerability processing). + if includeInvalid || rn.Valid() { + relNotes = append(relNotes, rn) + } + } + + return relNotes, nil +} + func parseRelDate(raw string) (time.Time, bool) { layouts := []string{"January-2-2006", "January-2-2006-release", "January 2, 2006"} for _, l := range layouts { @@ -70,7 +117,7 @@ func getId(token html.Token) string { // ParseReleaseHTML parses the release page using the provided reader. It is assumed that elements // in the page appear in order: first the release date then the version and finally any related // security updates. -func ParseReleaseHTML(reader io.Reader) ([]ReleaseNote, error) { +func parseReleaseHTML(reader io.Reader) ([]ReleaseNote, error) { var result []ReleaseNote // We use these pieces of state to keep track of whether we are inside a 'Security Updates' diff --git a/server/vulnerabilities/macoffice/release_note.go b/server/vulnerabilities/macoffice/release_note.go index 64c26351e8..074a461c7c 100644 --- a/server/vulnerabilities/macoffice/release_note.go +++ b/server/vulnerabilities/macoffice/release_note.go @@ -11,8 +11,6 @@ import ( "github.com/fleetdm/fleet/v4/server/vulnerabilities/utils" ) -const RelNotesURL = "https://learn.microsoft.com/en-us/officeupdates/release-notes-office-for-mac" - type ProductType int const (