Skip to content

Commit

Permalink
Fix to not panic if MSRC for current month is not available (#21749)
Browse files Browse the repository at this point in the history
See #21745.

---------

Co-authored-by: Ian Littman <[email protected]>
  • Loading branch information
lucasmrod and iansltx authored Sep 27, 2024
1 parent 5e66517 commit f52e0a0
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 14 deletions.
33 changes: 27 additions & 6 deletions cmd/msrc/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
Expand Down Expand Up @@ -64,6 +65,15 @@ func main() {
fmt.Println("Done.")
}

// windowsBulletinGracePeriod returns whether we are within the grace period for a MSRC monthly feed to exist.
//
// E.g. September 2024 bulletin was released on the 2nd, thus we add some grace period (5 days)
// for Microsoft to publish the current month bulletin.
func windowsBulletinGracePeriod(month time.Month, year int) bool {
now := time.Now()
return month == now.Month() && year == now.Year() && now.Day() <= 5
}

func update(
m time.Month,
y int,
Expand All @@ -72,15 +82,22 @@ func update(
ghClient io.GitHubAPI,
) ([]*parsed.SecurityBulletin, error) {
fmt.Println("Downloading current feed...")
f, err := msrcClient.GetFeed(m, y)
currentFeed, err := msrcClient.GetFeed(m, y)
if err != nil {
return nil, err
if errors.Is(err, msrc.FeedNotFound) && windowsBulletinGracePeriod(m, y) {
fmt.Printf("Current month feed %d-%d was not found, skipping...\n", y, m)
} else {
return nil, err
}
}

fmt.Println("Parsing current feed...")
nBulletins, err := msrc.ParseFeed(f)
if err != nil {
return nil, err
var nBulletins map[string]*parsed.SecurityBulletin
if currentFeed != "" {
fmt.Println("Parsing current feed...")
nBulletins, err = msrc.ParseFeed(currentFeed)
if err != nil {
return nil, err
}
}

var bulletins []*parsed.SecurityBulletin
Expand Down Expand Up @@ -118,6 +135,10 @@ func backfill(upToM time.Month, upToY int, client msrc.MSRCAPI) ([]*parsed.Secur
fmt.Printf("Downloading feed for %d-%d...\n", d.Year(), d.Month())
f, err := client.GetFeed(d.Month(), d.Year())
if err != nil {
if errors.Is(err, msrc.FeedNotFound) && windowsBulletinGracePeriod(d.Month(), d.Year()) {
fmt.Printf("Current month feed %d-%d was not found, skipping...\n", d.Year(), d.Month())
continue
}
return nil, err
}

Expand Down
16 changes: 15 additions & 1 deletion pkg/download/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package download
import (
"compress/bzip2"
"compress/gzip"
"errors"
"fmt"
"io"
"net/http"
Expand All @@ -20,16 +21,24 @@ import (
const backoffMaxElapsedTime = 5 * time.Minute

// Download downloads a file from a URL and writes it to path.
//
// It will retry requests until it succeeds. If the server returns a 404
// then it will not retry and return a NotFound error.
func Download(client *http.Client, u *url.URL, path string) error {
return download(client, u, path, false)
}

// DownloadAndExtract downloads and extracts a file from a URL and writes it to path.
// The compression method is determined using extension from the url path. Only .gz, .bz2, or .xz extensions are supported.
//
// It will retry requests until it succeeds. If the server returns a 404
// then it will not retry and return a NotFound error.
func DownloadAndExtract(client *http.Client, u *url.URL, path string) error {
return download(client, u, path, true)
}

var NotFound = errors.New("resource not found")

func download(client *http.Client, u *url.URL, path string, extract bool) error {
// atomically write to file
dir, file := filepath.Split(path)
Expand Down Expand Up @@ -79,7 +88,12 @@ func download(client *http.Client, u *url.URL, path string, extract bool) error
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
switch {
case resp.StatusCode == http.StatusOK:
// OK
case resp.StatusCode == http.StatusNotFound:
return &backoff.PermanentError{Err: NotFound}
default:
return fmt.Errorf("unexpected status code %d", resp.StatusCode)
}

Expand Down
24 changes: 24 additions & 0 deletions pkg/download/download_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package download

import (
"net/url"
"path/filepath"
"testing"
"time"

"github.com/fleetdm/fleet/v4/pkg/fleethttp"
"github.com/stretchr/testify/require"
)

func TestDownloadNotFoundNoRetries(t *testing.T) {
c := fleethttp.NewClient()
tmpDir := t.TempDir()
outputFile := filepath.Join(tmpDir)
url, err := url.Parse("https://github.com/fleetdm/non-existent")
require.NoError(t, err)
start := time.Now()
err = Download(c, url, outputFile)
require.Error(t, err)
require.ErrorIs(t, err, NotFound)
require.True(t, time.Since(start) < backoffMaxElapsedTime)
}
17 changes: 10 additions & 7 deletions server/vulnerabilities/io/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,17 @@ func (gh GitHubClient) list(ctx context.Context, prefix string, ctor func(fileNa
}

results := make(map[MetadataFileName]string)
for _, e := range releases[0].Assets {
name := e.GetName()
if strings.HasPrefix(name, prefix) {
metadataFileName, err := ctor(name)
if err != nil {
return nil, err

if len(releases) > 0 {
for _, e := range releases[0].Assets {
name := e.GetName()
if strings.HasPrefix(name, prefix) {
metadataFileName, err := ctor(name)
if err != nil {
return nil, err
}
results[metadataFileName] = e.GetBrowserDownloadURL()
}
results[metadataFileName] = e.GetBrowserDownloadURL()
}
}
return results, nil
Expand Down
8 changes: 8 additions & 0 deletions server/vulnerabilities/msrc/msrc_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ type MSRCAPI interface {
GetFeed(time.Month, int) (string, error)
}

// FeedNotFound is returned when a MSRC feed was not found.
//
// E.g. September 2024 bulleting was released on the 2nd.
var FeedNotFound = errors.New("feed not found")

type MSRCClient struct {
client *http.Client
workDir string
Expand Down Expand Up @@ -68,6 +73,9 @@ func (msrc MSRCClient) GetFeed(month time.Month, year int) (string, error) {
}

if err := download.Download(msrc.client, u, dst); err != nil {
if errors.Is(err, download.NotFound) {
return "", FeedNotFound
}
return "", err
}

Expand Down

0 comments on commit f52e0a0

Please sign in to comment.