package macoffice import ( "io" "net/http" "regexp" "strings" "time" "golang.org/x/net/html" ) var ( VersionPattern = regexp.MustCompile(`(?i)version (\d+\.\d+(\.\d+)?) \(?build \d+\)?`) BuildNumberPattern = regexp.MustCompile(`(?i)\(build (\d+)\)?`) cveLinkPattern = regexp.MustCompile(`CVE(-\d+)+$`) ) var IdToType = map[string]ProductType{ "office-suite": WholeSuite, "outlook": Outlook, "word": Word, "powerpoint": PowerPoint, "excel": Excel, "onenote": OneNote, } var nameToType = map[string]ProductType{ "office suite": WholeSuite, "outlook": Outlook, "word": Word, "powerpoint": PowerPoint, "excel": Excel, "onenote": OneNote, "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 { relDate, err := time.Parse(l, raw) if err == nil { return relDate, true } } return time.Time{}, false } func parseVulnLink(token html.Token) (string, bool) { for _, a := range token.Attr { // Check if the link points to a CVE if a.Key == "href" && cveLinkPattern.MatchString(a.Val) { parts := strings.Split(a.Val, "/") return parts[len(parts)-1], true } } return "", false } // returns the id attr value of an html token, an empty string otherwise func getId(token html.Token) string { for _, attr := range token.Attr { if attr.Key == "id" { return attr.Val } } return "" } // 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) { var result []ReleaseNote // We use these pieces of state to keep track of whether we are inside a 'Security Updates' // section of a release and also what product the security updates applies to. var insideSecUpts bool var currentProduct ProductType z := html.NewTokenizer(reader) for { switch z.Next() { case html.ErrorToken: // If EOF we are done... if z.Err() == io.EOF { return result, nil } return nil, z.Err() case html.StartTagToken: token := z.Token() // //-------------- // Release date // -------------- // // The release date could be in a
| Application | //Update | //Security updates | //Download link for update package | //
|---|---|---|---|
| Word |
// See your email attachments: Your email attachments are now available in the Shared tab. | //CVE-2019-0953: Microsoft Word Remote Code Execution Vulnerability |
// Word update package |
//