fix(rocky): use minor part in version number (#154)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
parent
e803f5cdec
commit
b19cb98d6a
@ -11,8 +11,10 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/PuerkitoBio/goquery"
|
||||||
"github.com/aquasecurity/vuln-list-update/utils"
|
"github.com/aquasecurity/vuln-list-update/utils"
|
||||||
"github.com/cheggaaa/pb/v3"
|
"github.com/cheggaaa/pb/v3"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
@ -24,10 +26,12 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
urlFormat = "https://download.rockylinux.org/pub/rocky/%s/%s/%s/os/"
|
baseUrl = "https://download.rockylinux.org/pub/rocky"
|
||||||
defaultReleases = []string{"8"}
|
urlFormat = "%s/%s/%s/%s/os/"
|
||||||
defaultRepos = []string{"BaseOS", "AppStream", "extras"}
|
defaultRepos = []string{"BaseOS", "AppStream", "extras"}
|
||||||
defaultArches = []string{"x86_64", "aarch64"}
|
defaultArches = []string{"x86_64", "aarch64"}
|
||||||
|
|
||||||
|
releaseRegex = regexp.MustCompile(`\d+.\d+`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// RepoMd has repomd data
|
// RepoMd has repomd data
|
||||||
@ -89,22 +93,22 @@ type Package struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type options struct {
|
type options struct {
|
||||||
url string
|
baseUrl string
|
||||||
dir string
|
urlFormat string
|
||||||
retry int
|
dir string
|
||||||
releases []string
|
retry int
|
||||||
repos []string
|
repos []string
|
||||||
arches []string
|
arches []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type option func(*options)
|
type option func(*options)
|
||||||
|
|
||||||
func With(url, dir string, retry int, releases, repos, arches []string) option {
|
func With(baseUrl, urlFormat, dir string, retry int, repos, arches []string) option {
|
||||||
return func(opts *options) {
|
return func(opts *options) {
|
||||||
opts.url = url
|
opts.baseUrl = baseUrl
|
||||||
|
opts.urlFormat = urlFormat
|
||||||
opts.dir = dir
|
opts.dir = dir
|
||||||
opts.retry = retry
|
opts.retry = retry
|
||||||
opts.releases = releases
|
|
||||||
opts.repos = repos
|
opts.repos = repos
|
||||||
opts.arches = arches
|
opts.arches = arches
|
||||||
}
|
}
|
||||||
@ -116,12 +120,12 @@ type Config struct {
|
|||||||
|
|
||||||
func NewConfig(opts ...option) Config {
|
func NewConfig(opts ...option) Config {
|
||||||
o := &options{
|
o := &options{
|
||||||
url: urlFormat,
|
baseUrl: baseUrl,
|
||||||
dir: filepath.Join(utils.VulnListDir(), rockyDir),
|
urlFormat: urlFormat,
|
||||||
retry: retry,
|
dir: filepath.Join(utils.VulnListDir(), rockyDir),
|
||||||
releases: defaultReleases,
|
retry: retry,
|
||||||
repos: defaultRepos,
|
repos: defaultRepos,
|
||||||
arches: defaultArches,
|
arches: defaultArches,
|
||||||
}
|
}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(o)
|
opt(o)
|
||||||
@ -133,11 +137,17 @@ func NewConfig(opts ...option) Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c Config) Update() error {
|
func (c Config) Update() error {
|
||||||
for _, release := range c.releases {
|
// "8" is an alias of the latest release that doesn't contain old security advisories,
|
||||||
|
// so we have to get all available minor releases like 8.5 and 8.6 so that we can have all the advisories.
|
||||||
|
releases, err := c.getReleasesList()
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("failed to get a list of Rocky Linux releases: %w", err)
|
||||||
|
}
|
||||||
|
for _, release := range releases {
|
||||||
for _, repo := range c.repos {
|
for _, repo := range c.repos {
|
||||||
for _, arch := range c.arches {
|
for _, arch := range c.arches {
|
||||||
log.Printf("Fetching Rocky Linux %s %s %s data...", release, repo, arch)
|
log.Printf("Fetching Rocky Linux %s %s %s data...", release, repo, arch)
|
||||||
if err := c.update(release, repo, arch); err != nil {
|
if err = c.update(release, repo, arch); err != nil {
|
||||||
return xerrors.Errorf("failed to update security advisories of Rocky Linux %s %s %s: %w", release, repo, arch, err)
|
return xerrors.Errorf("failed to update security advisories of Rocky Linux %s %s %s: %w", release, repo, arch, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,7 +166,7 @@ func (c Config) update(release, repo, arch string) error {
|
|||||||
return xerrors.Errorf("failed to mkdir: %w", err)
|
return xerrors.Errorf("failed to mkdir: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err := url.Parse(fmt.Sprintf(c.url, release, repo, arch))
|
u, err := url.Parse(fmt.Sprintf(c.urlFormat, c.baseUrl, release, repo, arch))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("failed to parse root url: %w", err)
|
return xerrors.Errorf("failed to parse root url: %w", err)
|
||||||
}
|
}
|
||||||
@ -188,14 +198,14 @@ func (c Config) update(release, repo, arch string) error {
|
|||||||
for year, errata := range secErrata {
|
for year, errata := range secErrata {
|
||||||
log.Printf("Write Errata for Rocky Linux %s %s %s %s", release, repo, arch, year)
|
log.Printf("Write Errata for Rocky Linux %s %s %s %s", release, repo, arch, year)
|
||||||
|
|
||||||
if err := os.MkdirAll(filepath.Join(dirPath, year), os.ModePerm); err != nil {
|
if err = os.MkdirAll(filepath.Join(dirPath, year), os.ModePerm); err != nil {
|
||||||
return xerrors.Errorf("failed to mkdir: %w", err)
|
return xerrors.Errorf("failed to mkdir: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bar := pb.StartNew(len(errata))
|
bar := pb.StartNew(len(errata))
|
||||||
for _, erratum := range errata {
|
for _, erratum := range errata {
|
||||||
jsonPath := filepath.Join(dirPath, year, fmt.Sprintf("%s.json", erratum.ID))
|
jsonPath := filepath.Join(dirPath, year, fmt.Sprintf("%s.json", erratum.ID))
|
||||||
if err := utils.Write(jsonPath, erratum); err != nil {
|
if err = utils.Write(jsonPath, erratum); err != nil {
|
||||||
return xerrors.Errorf("failed to write Rocky Linux CVE details: %w", err)
|
return xerrors.Errorf("failed to write Rocky Linux CVE details: %w", err)
|
||||||
}
|
}
|
||||||
bar.Increment()
|
bar.Increment()
|
||||||
@ -215,7 +225,7 @@ func (c Config) fetchUpdateInfoPath(repomdURL string) (updateInfoPath string, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
var repoMd RepoMd
|
var repoMd RepoMd
|
||||||
if err := xml.NewDecoder(bytes.NewBuffer(res)).Decode(&repoMd); err != nil {
|
if err = xml.NewDecoder(bytes.NewBuffer(res)).Decode(&repoMd); err != nil {
|
||||||
return "", xerrors.Errorf("failed to decode repomd.xml: %w", err)
|
return "", xerrors.Errorf("failed to decode repomd.xml: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +249,7 @@ func (c Config) fetchUpdateInfo(url string) (*UpdateInfo, error) {
|
|||||||
defer r.Close()
|
defer r.Close()
|
||||||
|
|
||||||
var updateInfo UpdateInfo
|
var updateInfo UpdateInfo
|
||||||
if err := xml.NewDecoder(r).Decode(&updateInfo); err != nil {
|
if err = xml.NewDecoder(r).Decode(&updateInfo); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for i, alas := range updateInfo.RLSAList {
|
for i, alas := range updateInfo.RLSAList {
|
||||||
@ -253,3 +263,27 @@ func (c Config) fetchUpdateInfo(url string) (*UpdateInfo, error) {
|
|||||||
}
|
}
|
||||||
return &updateInfo, nil
|
return &updateInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c Config) getReleasesList() ([]string, error) {
|
||||||
|
b, err := utils.FetchURL(c.baseUrl, "", c.retry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to get list of releases: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(b))
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to read list of releases: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var releases []string
|
||||||
|
doc.Find("a").Each(func(i int, s *goquery.Selection) {
|
||||||
|
if release := releaseRegex.FindString(s.Text()); release != "" {
|
||||||
|
releases = append(releases, release)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(releases) == 0 {
|
||||||
|
return nil, xerrors.Errorf("failed to get list of releases: list is empty")
|
||||||
|
}
|
||||||
|
return releases, nil
|
||||||
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package rocky_test
|
package rocky_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
@ -11,57 +10,72 @@ import (
|
|||||||
"github.com/aquasecurity/vuln-list-update/rocky"
|
"github.com/aquasecurity/vuln-list-update/rocky"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/xerrors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_Update(t *testing.T) {
|
func Test_Update(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
rootDir string
|
releasesFilePath string
|
||||||
repository []string
|
rootDir string
|
||||||
expectedError error
|
repository []string
|
||||||
|
wantErr string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "happy path",
|
name: "happy path",
|
||||||
rootDir: "testdata/fixtures/happy",
|
releasesFilePath: "testdata/fixtures/releases/happy.html",
|
||||||
repository: []string{"BaseOS"},
|
rootDir: "testdata/fixtures/happy",
|
||||||
expectedError: nil,
|
repository: []string{"BaseOS"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "bad repomd response",
|
name: "bad repomd response",
|
||||||
rootDir: "testdata/fixtures/repomd_invalid",
|
releasesFilePath: "testdata/fixtures/releases/happy.html",
|
||||||
repository: []string{"BaseOS"},
|
rootDir: "testdata/fixtures/repomd_invalid",
|
||||||
expectedError: xerrors.Errorf("failed to update security advisories of Rocky Linux 8 BaseOS x86_64: %w", errors.New("failed to fetch updateInfo path from repomd.xml")),
|
repository: []string{"BaseOS"},
|
||||||
|
wantErr: "failed to fetch updateInfo path from repomd.xml",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "bad updateInfo response",
|
name: "bad updateInfo response",
|
||||||
rootDir: "testdata/fixtures/updateinfo_invalid",
|
releasesFilePath: "testdata/fixtures/releases/happy.html",
|
||||||
repository: []string{"BaseOS"},
|
rootDir: "testdata/fixtures/updateinfo_invalid",
|
||||||
expectedError: xerrors.Errorf("failed to update security advisories of Rocky Linux 8 BaseOS x86_64: %w", errors.New("failed to fetch updateInfo")),
|
repository: []string{"BaseOS"},
|
||||||
|
wantErr: "failed to fetch updateInfo",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no updateInfo field(BaseOS)",
|
name: "no updateInfo field(BaseOS)",
|
||||||
rootDir: "testdata/fixtures/no_updateinfo_field",
|
releasesFilePath: "testdata/fixtures/releases/happy.html",
|
||||||
repository: []string{"BaseOS"},
|
rootDir: "testdata/fixtures/no_updateinfo_field",
|
||||||
expectedError: xerrors.Errorf("failed to update security advisories of Rocky Linux 8 BaseOS x86_64: %w", xerrors.Errorf("failed to fetch updateInfo path from repomd.xml: %w", rocky.ErrorNoUpdateInfoField)),
|
repository: []string{"BaseOS"},
|
||||||
|
wantErr: rocky.ErrorNoUpdateInfoField.Error(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no updateInfo field(extras)",
|
name: "no updateInfo field(extras)",
|
||||||
rootDir: "testdata/fixtures/no_updateinfo_field",
|
releasesFilePath: "testdata/fixtures/releases/happy.html",
|
||||||
repository: []string{"extras"},
|
rootDir: "testdata/fixtures/no_updateinfo_field",
|
||||||
expectedError: nil,
|
repository: []string{"extras"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty list of releases",
|
||||||
|
releasesFilePath: "testdata/fixtures/releases/empty.html",
|
||||||
|
repository: []string{"BaseOS"},
|
||||||
|
wantErr: "list is empty",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
tsUpdateInfoURL := httptest.NewServer(http.StripPrefix("/pub/rocky/8/BaseOS/x86_64/os/repodata/", http.FileServer(http.Dir(tt.rootDir))))
|
mux := http.NewServeMux()
|
||||||
|
mux.Handle("/pub/rocky/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
http.ServeFile(w, r, tt.releasesFilePath)
|
||||||
|
}))
|
||||||
|
mux.Handle("/pub/rocky/8.5/extras/x86_64/os/repodata/", http.StripPrefix("/pub/rocky/8.5/extras/x86_64/os/repodata/", http.FileServer(http.Dir(tt.rootDir))))
|
||||||
|
mux.Handle("/pub/rocky/8.5/BaseOS/x86_64/os/repodata/", http.StripPrefix("/pub/rocky/8.5/BaseOS/x86_64/os/repodata/", http.FileServer(http.Dir(tt.rootDir))))
|
||||||
|
tsUpdateInfoURL := httptest.NewServer(mux)
|
||||||
defer tsUpdateInfoURL.Close()
|
defer tsUpdateInfoURL.Close()
|
||||||
|
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
rc := rocky.NewConfig(rocky.With(tsUpdateInfoURL.URL+"/pub/rocky/%s/%s/%s/os/", dir, 0, []string{"8"}, tt.repository, []string{"x86_64"}))
|
rc := rocky.NewConfig(rocky.With(tsUpdateInfoURL.URL+"/pub/rocky", "%s/%s/%s/%s/os/", dir, 0, tt.repository, []string{"x86_64"}))
|
||||||
if err := rc.Update(); tt.expectedError != nil {
|
if err := rc.Update(); tt.wantErr != "" {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
assert.Contains(t, err.Error(), tt.expectedError.Error())
|
assert.Contains(t, err.Error(), tt.wantErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
13
rocky/testdata/fixtures/releases/empty.html
vendored
Normal file
13
rocky/testdata/fixtures/releases/empty.html
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Index of /pub/rocky/</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body bgcolor="white">
|
||||||
|
<h1>Index of /pub/rocky/</h1>
|
||||||
|
<hr>
|
||||||
|
<hr>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
17
rocky/testdata/fixtures/releases/happy.html
vendored
Normal file
17
rocky/testdata/fixtures/releases/happy.html
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Index of /pub/rocky/</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body bgcolor="white">
|
||||||
|
<h1>Index of /pub/rocky/</h1>
|
||||||
|
<hr>
|
||||||
|
<pre><a href="../">../</a>
|
||||||
|
<a href="8/">8/</a>
|
||||||
|
<a href="8.5/">8.5/</a>
|
||||||
|
</pre>
|
||||||
|
<hr>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user