Add support for Chainguard distro (#190)

Signed-off-by: Dan Luhring <dluhring@chainguard.dev>
This commit is contained in:
Dan Luhring 2023-03-30 04:30:22 -04:00 committed by GitHub
parent 953c694412
commit 77ea72500e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 254 additions and 4 deletions

View File

@ -111,6 +111,10 @@ jobs:
name: Wolfi Secdb
run: ./vuln-list-update -target wolfi
- if: always()
name: Chainguard Secdb
run: ./vuln-list-update -target chainguard
- if: always()
name: Known Exploited Vulnerabilities Catalog
run: ./vuln-list-update -target kevc

View File

@ -20,9 +20,13 @@ https://github.com/aquasecurity/vuln-list/
$ vuln-list-update -h
Usage of vuln-list-update:
-target string
update target (nvd, alpine, alpine-unfixed, redhat, redhat-oval, debian, debian-oval, ubuntu, amazon, oracle-oval, suse-cvrf, photon, arch-linux, ghsa, glad, cwe, osv, go-vulndb, mariner, kevc)
update target (nvd, alpine, alpine-unfixed, redhat, redhat-oval, debian, debian-oval, ubuntu, amazon, oracle-oval, suse-cvrf, photon, arch-linux, ghsa, glad, cwe, osv, go-vulndb, mariner, kevc, wolfi, chainguard)
-target-branch string
alternative repository branch (only glad)
-target-uri string
alternative repository URI (only glad)
-years string
update years (only redhat)
update years (only redhat)
```
## Author

73
chainguard/chainguard.go Normal file
View File

@ -0,0 +1,73 @@
package chainguard
import (
"log"
"net/url"
"path/filepath"
"github.com/spf13/afero"
"golang.org/x/xerrors"
"github.com/aquasecurity/vuln-list-update/alpine"
"github.com/aquasecurity/vuln-list-update/utils"
)
const (
chainguardDir = "chainguard"
secdbURLBase = "https://packages.cgr.dev"
secdbURLPath = "chainguard/security.json"
)
type option func(c *Updater)
func WithVulnListDir(v string) option {
return func(c *Updater) { c.vulnListDir = v }
}
func WithAppFs(v afero.Fs) option {
return func(c *Updater) { c.appFs = v }
}
func WithBaseURL(v *url.URL) option {
return func(c *Updater) { c.baseURL = v }
}
type Updater struct {
vulnListDir string
appFs afero.Fs
baseURL *url.URL
}
func NewUpdater(options ...option) *Updater {
u, _ := url.Parse(secdbURLBase)
updater := &Updater{
vulnListDir: utils.VulnListDir(),
appFs: afero.NewOsFs(),
baseURL: u,
}
for _, option := range options {
option(updater)
}
return updater
}
func (u *Updater) Update() error {
dir := filepath.Join(u.vulnListDir, chainguardDir)
log.Printf("Remove Chainguard directory %s", dir)
if err := u.appFs.RemoveAll(dir); err != nil {
return xerrors.Errorf("failed to remove Chainguard directory: %w", err)
}
if err := u.appFs.MkdirAll(dir, 0755); err != nil {
return xerrors.Errorf("Chainguard mkdir error: %w", err)
}
log.Println("Fetching Chainguard data...")
alpineUpdater := alpine.NewUpdater(
alpine.WithBaseURL(u.baseURL),
alpine.WithVulnListDir(u.vulnListDir),
alpine.WithAdvisoryDir(chainguardDir),
alpine.WithAppFs(u.appFs))
return alpineUpdater.Save("", secdbURLPath)
}

View File

@ -0,0 +1,115 @@
package chainguard_test
import (
"flag"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"os"
"testing"
"github.com/aquasecurity/vuln-list-update/chainguard"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var update = flag.Bool("update", false, "update golden files")
func TestUpdater_Update(t *testing.T) {
type fields struct {
appFs afero.Fs
retry int
}
tests := []struct {
name string
fields fields
fileNames map[string]string
goldenFiles map[string]string
wantErr string
}{
{
name: "happy path",
fields: fields{
appFs: afero.NewMemMapFs(),
retry: 0,
},
fileNames: map[string]string{
"/chainguard/security.json": "testdata/security.json",
},
goldenFiles: map[string]string{
"/tmp/chainguard/chainguard/binutils.json": "testdata/golden/binutils.json",
},
},
{
name: "404",
fields: fields{
appFs: afero.NewMemMapFs(),
retry: 0,
},
fileNames: map[string]string{
"/": "testdata/index.html",
},
wantErr: "status code: 404",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fileName, ok := tt.fileNames[r.URL.Path]
if !ok {
http.NotFound(w, r)
return
}
http.ServeFile(w, r, fileName)
}))
defer ts.Close()
baseURL, err := url.Parse(ts.URL)
require.NoError(t, err)
u := chainguard.NewUpdater(
chainguard.WithVulnListDir("/tmp"),
chainguard.WithBaseURL(baseURL),
chainguard.WithAppFs(tt.fields.appFs))
err = u.Update()
if tt.wantErr != "" {
require.NotNil(t, err)
assert.Contains(t, err.Error(), tt.wantErr)
return
} else {
assert.NoError(t, err)
}
fileCount := 0
err = afero.Walk(tt.fields.appFs, "/", func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
fileCount++
actual, err := afero.ReadFile(tt.fields.appFs, path)
assert.NoError(t, err, path)
goldenPath, ok := tt.goldenFiles[path]
require.True(t, ok, path)
if *update {
err = ioutil.WriteFile(goldenPath, actual, 0666)
require.NoError(t, err, goldenPath)
}
expected, err := ioutil.ReadFile(goldenPath)
assert.NoError(t, err, goldenPath)
assert.JSONEq(t, string(expected), string(actual), path)
return nil
})
assert.Equal(t, len(tt.goldenFiles), fileCount)
assert.NoError(t, err)
})
}
}

View File

@ -0,0 +1,20 @@
{
"name": "binutils",
"secfixes": {
"2.39-r1": [
"CVE-2022-38126"
],
"2.39-r2": [
"CVE-2022-38533"
],
"2.39-r3": [
"CVE-2022-38128"
]
},
"apkurl": "{{urlprefix}}/{{reponame}}/{{arch}}/{{pkg.name}}-{{pkg.ver}}.apk",
"archs": [
"x86_64"
],
"urlprefix": "https://packages.cgr.dev",
"reponame": "chainguard"
}

26
chainguard/testdata/security.json vendored Normal file
View File

@ -0,0 +1,26 @@
{
"apkurl": "{{urlprefix}}/{{reponame}}/{{arch}}/{{pkg.name}}-{{pkg.ver}}.apk",
"archs": [
"x86_64"
],
"reponame": "chainguard",
"urlprefix": "https://packages.cgr.dev",
"packages": [
{
"pkg": {
"name": "binutils",
"secfixes": {
"2.39-r1": [
"CVE-2022-38126"
],
"2.39-r2": [
"CVE-2022-38533"
],
"2.39-r3": [
"CVE-2022-38128"
]
}
}
}
]
}

12
main.go
View File

@ -10,6 +10,7 @@ import (
"strings"
"time"
"github.com/aquasecurity/vuln-list-update/chainguard"
"github.com/aquasecurity/vuln-list-update/kevc"
"github.com/aquasecurity/vuln-list-update/wolfi"
@ -49,7 +50,7 @@ const (
var (
target = flag.String("target", "", "update target (nvd, alpine, alpine-unfixed, redhat, redhat-oval, "+
"debian, debian-oval, ubuntu, amazon, oracle-oval, suse-cvrf, photon, arch-linux, ghsa, glad, cwe, osv, go-vulndb, mariner, kevc, wolfi)")
"debian, debian-oval, ubuntu, amazon, oracle-oval, suse-cvrf, photon, arch-linux, ghsa, glad, cwe, osv, go-vulndb, mariner, kevc, wolfi, chainguard)")
years = flag.String("years", "", "update years (only redhat)")
targetUri = flag.String("target-uri", "", "alternative repository URI (only glad)")
targetBranch = flag.String("target-branch", "", "alternative repository branch (only glad)")
@ -75,6 +76,7 @@ func run() error {
url := fmt.Sprintf(repoURL, githubToken, repoOwner, repoName)
log.Printf("target repository is %s/%s\n", repoOwner, repoName)
log.Printf("cloning/pulling into %s", utils.VulnListDir())
if _, err := gc.CloneOrPull(url, utils.VulnListDir(), "main", debug); err != nil {
return xerrors.Errorf("clone or pull error: %w", err)
@ -234,7 +236,13 @@ func run() error {
if err := wu.Update(); err != nil {
return xerrors.Errorf("Wolfi update error: %w", err)
}
commitMsg = "Wolfi Issue Tracker"
commitMsg = "Wolfi Security Data"
case "chainguard":
cu := chainguard.NewUpdater()
if err := cu.Update(); err != nil {
return xerrors.Errorf("Chainguard update error: %w", err)
}
commitMsg = "Chainguard Security Data"
default:
return xerrors.New("unknown target")
}