fleet/server/vulnerabilities/msrc/io/github.go
Juan Fernandez 98e4d6e0bc
Feature 7394: Use MSRC parser to generate security bulletin artifacts (#7491)
Generate security artifacts using the MSRC parser.
2022-09-12 15:17:13 -04:00

93 lines
2.2 KiB
Go

package io
import (
"context"
"fmt"
"net/http"
"net/url"
"path"
"path/filepath"
"strings"
"time"
"github.com/fleetdm/fleet/v4/pkg/download"
"github.com/google/go-github/v37/github"
)
// ReleaseLister interface around github.NewClient(...).Repositories.
type ReleaseLister interface {
ListReleases(
context.Context,
string,
string,
*github.ListOptions,
) ([]*github.RepositoryRelease, *github.Response, error)
}
// GitHubAPI allows users to interact with the MSRC artifacts published on Github.
type GitHubAPI interface {
Download(string) (string, error)
Bulletins() (map[SecurityBulletinName]string, error)
}
type GitHubClient struct {
httpClient *http.Client
releases ReleaseLister
workDir string
}
// NewGitHubClient returns a new GithubClient, 'workDir' will be used as the destination directory for
// downloading artifacts.
func NewGitHubClient(client *http.Client, releases ReleaseLister, workDir string) GitHubClient {
return GitHubClient{
httpClient: client,
releases: releases,
workDir: workDir,
}
}
// Download downloads the security bulletin located at 'URL' in 'workDir', returns the path of
// the downloaded bulletin.
func (gh GitHubClient) Download(URL string) (string, error) {
u, err := url.Parse(URL)
if err != nil {
return "", err
}
fPath := filepath.Join(gh.workDir, path.Base(u.Path))
if err := download.Download(gh.httpClient, u, fPath); err != nil {
return "", err
}
return fPath, nil
}
// Bulletins returns a map of 'bulletin name' => 'download URL' of the bulletins stored as assets on Github.
func (gh GitHubClient) Bulletins() (map[SecurityBulletinName]string, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
releases, r, err := gh.releases.ListReleases(
ctx,
"fleetdm",
"nvd",
&github.ListOptions{Page: 0, PerPage: 10},
)
if err != nil {
return nil, err
}
if r.StatusCode != http.StatusOK {
return nil, fmt.Errorf("github http status error: %d", r.StatusCode)
}
results := make(map[SecurityBulletinName]string)
for _, e := range releases[0].Assets {
name := e.GetName()
if strings.HasPrefix(name, mSRCFilePrefix) {
results[NewSecurityBulletinName(name)] = e.GetBrowserDownloadURL()
}
}
return results, nil
}