diff --git a/ee/maintained-apps/ingesters/homebrew/external_refs/main.go b/ee/maintained-apps/ingesters/homebrew/external_refs/main.go index 008b9cd0a5..2aefd90a8e 100644 --- a/ee/maintained-apps/ingesters/homebrew/external_refs/main.go +++ b/ee/maintained-apps/ingesters/homebrew/external_refs/main.go @@ -9,6 +9,9 @@ import ( var Funcs = map[string][]func(*maintained_apps.FMAManifestApp) (*maintained_apps.FMAManifestApp, error){ "microsoft-word/darwin": {MicrosoftVersionFromReleaseNotes}, "microsoft-excel/darwin": {MicrosoftVersionFromReleaseNotes}, + "microsoft-outlook/darwin": {MicrosoftVersionFromReleaseNotes}, + "microsoft-powerpoint/darwin": {MicrosoftVersionFromReleaseNotes}, + "microsoft-onenote/darwin": {MicrosoftVersionFromReleaseNotes}, "brave-browser/darwin": {BraveVersionTransformer}, "whatsapp/darwin": {WhatsAppVersionShortener, WhatsAppInstallerURL}, "google-chrome/darwin": {ChromePKGInstaller}, diff --git a/ee/maintained-apps/ingesters/homebrew/external_refs/microsoft_version_from_release_notes.go b/ee/maintained-apps/ingesters/homebrew/external_refs/microsoft_version_from_release_notes.go index c221a2971d..8a8747c51a 100644 --- a/ee/maintained-apps/ingesters/homebrew/external_refs/microsoft_version_from_release_notes.go +++ b/ee/maintained-apps/ingesters/homebrew/external_refs/microsoft_version_from_release_notes.go @@ -19,9 +19,24 @@ var ( // Homebrew version "16.95.25032931" // Release notes version "Version 16.95.3 (Build 25032931)" // For this example it would change the app version to "16.95.3" +// +// If the exact build number is not found in the release notes (e.g., because the +// release notes page hasn't been updated yet for a newly published build), the +// function falls back to the base version without the build number (e.g., "16.95" +// instead of "16.95.25032931"). This prevents a perpetual "update available" loop +// that would otherwise occur because osquery reports the short version +// (e.g., "16.95.3") while the manifest stores the raw build string, causing +// compareVersions to always flag the installed version as older. func MicrosoftVersionFromReleaseNotes(app *maintained_apps.FMAManifestApp) (*maintained_apps.FMAManifestApp, error) { homebrewVersion := app.Version - versionParts := strings.Split(homebrewVersion, ".") // homebrew version format is like "16.95.25032931" + versionParts := strings.Split(homebrewVersion, ".") // homebrew version format is like "16.95.25032931" + + // If the version doesn't have at least major.minor.build segments, there is no + // build number to extract and look up; return the version unchanged. + if len(versionParts) < 3 { + return app, nil + } + version := strings.Join(versionParts[:len(versionParts)-1], ".") // Extract version without the build number build := versionParts[len(versionParts)-1] // Extract the build number @@ -42,5 +57,9 @@ func MicrosoftVersionFromReleaseNotes(app *maintained_apps.FMAManifestApp) (*mai } } - return app, fmt.Errorf("no matching version found in release notes for %s", homebrewVersion) + // No exact match found in the release notes for this build number. Fall back to + // the base version (e.g., "16.95" from "16.95.25032931") so that the installed + // short version (e.g., "16.95.3") is not falsely flagged as older. + app.Version = version + return app, nil } diff --git a/ee/maintained-apps/ingesters/homebrew/external_refs/microsoft_version_from_release_notes_test.go b/ee/maintained-apps/ingesters/homebrew/external_refs/microsoft_version_from_release_notes_test.go index cdf69b0a9d..978fe2b097 100644 --- a/ee/maintained-apps/ingesters/homebrew/external_refs/microsoft_version_from_release_notes_test.go +++ b/ee/maintained-apps/ingesters/homebrew/external_refs/microsoft_version_from_release_notes_test.go @@ -43,13 +43,29 @@ func TestMicrosoftVersionFromReleaseNotes(t *testing.T) { assert.Equal(t, "16.98.3", result.Version) }) - t.Run("version not found", func(t *testing.T) { + t.Run("version not found falls back to base version", func(t *testing.T) { + // Build number "25999999" is not in the release notes cache above. + // The function should fall back to the base major.minor version ("16.50") + // rather than leaving the raw Homebrew build string, which would cause a + // perpetual "update available" loop against osquery's short version reporting. app := &maintained_apps.FMAManifestApp{ UniqueIdentifier: "microsoft-excel/darwin", Version: "16.50.25999999", } result, err := MicrosoftVersionFromReleaseNotes(app) - assert.Error(t, err) - assert.Equal(t, "16.50.25999999", result.Version) + assert.NoError(t, err) + assert.Equal(t, "16.50", result.Version) + }) + + t.Run("version with fewer than 3 segments is returned unchanged", func(t *testing.T) { + // Versions with fewer than 3 segments have no build number to look up; + // the function should return the version unchanged. + app := &maintained_apps.FMAManifestApp{ + UniqueIdentifier: "microsoft-onenote/darwin", + Version: "16.106", + } + result, err := MicrosoftVersionFromReleaseNotes(app) + assert.NoError(t, err) + assert.Equal(t, "16.106", result.Version) }) }