-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat!(detector): use vuls2 for redhat/centos/alma/rocky
- Loading branch information
Showing
9 changed files
with
883 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
package vuls2 | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"time" | ||
|
||
"github.com/future-architect/vuls/logging" | ||
"github.com/klauspost/compress/zstd" | ||
ocispec "github.com/opencontainers/image-spec/specs-go/v1" | ||
"github.com/pkg/errors" | ||
progressbar "github.com/schollz/progressbar/v3" | ||
"golang.org/x/xerrors" | ||
oras "oras.land/oras-go/v2" | ||
"oras.land/oras-go/v2/content" | ||
"oras.land/oras-go/v2/content/memory" | ||
"oras.land/oras-go/v2/registry/remote" | ||
|
||
db "github.com/MaineK00n/vuls2/pkg/db/common" | ||
utilos "github.com/MaineK00n/vuls2/pkg/util/os" | ||
) | ||
|
||
const ( | ||
SchemaVersion = 0 | ||
dbMediaType = "application/vnd.mainek00n.vuls.db.layer.v1+zstd" | ||
) | ||
|
||
var ( | ||
// DefaultGHCRRepository is GitHub Container Registry for vuls2 db for vuls0 | ||
// FIXME: change to vuls2 db repo for vuls0 | ||
DefaultGHCRRepository = fmt.Sprintf("%s:%d", "ghcr.io/mainek00n/vuls2", SchemaVersion) | ||
|
||
// DefaultDBDir is the place for db dir | ||
DefaultDBDir = filepath.Join(utilos.UserCacheDir(), "vuls0-db") | ||
) | ||
|
||
type Config struct { | ||
Repositories []string | ||
DbDir string | ||
SkipUpdate bool | ||
Quiet bool | ||
} | ||
|
||
type metadata struct { | ||
DownloadedAt time.Time | ||
} | ||
|
||
func (c Config) Refresh() error { | ||
|
||
m, found, err := c.loadMetadata() | ||
if err != nil { | ||
return xerrors.Errorf("Failed to load vuls2 db metadata. err; %w", err) | ||
} | ||
|
||
if found && time.Now().Before(m.DownloadedAt.Add(6*time.Hour)) { | ||
return nil | ||
} | ||
|
||
if c.SkipUpdate { | ||
if !found { | ||
return xerrors.New("Vuls2 db not found, cannot skip update") | ||
} | ||
logging.Log.Infof("Skip Vuls2 db update. downloaded: %s", m.DownloadedAt.Format("2006-01-02T15:04:05-07:00")) | ||
return nil | ||
} | ||
|
||
if err := c.fetch(); err != nil { | ||
return xerrors.Errorf("Failed to fetch vuls2 db. err: %w", err) | ||
} | ||
|
||
m.DownloadedAt = time.Now() | ||
if err := c.storeMetadata(m); err != nil { | ||
return xerrors.Errorf("Failed to store vuls2 db metadata. err: %w", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (c Config) New() (db.DB, error) { | ||
vuls2Config := db.Config{ | ||
Type: "boltdb", | ||
Path: filepath.Join(c.DbDir, "vuls2.boltdb"), | ||
} | ||
|
||
dbc, err := vuls2Config.New() | ||
if err != nil { | ||
return nil, xerrors.Errorf("Failed to new vuls2 db. err; %w", err) | ||
} | ||
|
||
return dbc, nil | ||
} | ||
|
||
func (c Config) loadMetadata() (metadata, bool, error) { | ||
bs, err := os.ReadFile(filepath.Join(c.DbDir, "metadata.json")) | ||
if err != nil { | ||
if errors.Is(err, os.ErrNotExist) { | ||
return metadata{}, false, nil | ||
} | ||
return metadata{}, false, xerrors.Errorf("Failed to read db metadata. err: %w", err) | ||
} | ||
|
||
var m metadata | ||
if err := json.Unmarshal(bs, &m); err != nil { | ||
return metadata{}, false, xerrors.Errorf("Failed to load db metadata. err: %w", err) | ||
} | ||
|
||
return m, true, nil | ||
} | ||
|
||
func (c Config) storeMetadata(m metadata) error { | ||
bs, err := json.Marshal(m) | ||
if err != nil { | ||
return xerrors.Errorf("Failed to marshal db metadata. err: %w", err) | ||
} | ||
|
||
if err := os.WriteFile(filepath.Join(c.DbDir, "metadata.json"), bs, 0644); err != nil { | ||
return xerrors.Errorf("Failed to write db metadata. filepath: %s, err: %w", filepath.Join(c.DbDir, "metadata.json"), err) | ||
} | ||
return nil | ||
} | ||
|
||
func (c Config) fetch() error { | ||
for _, r := range c.Repositories { | ||
logging.Log.Infof("Downloading vuls2 db. repository: %s", r) | ||
if err := c.fetchFromRepository(r); err != nil { | ||
logging.Log.Warnf("Failed to fetch vuls2 db. repository: %s, err: %+v", r, err) | ||
continue | ||
} | ||
return nil | ||
} | ||
return xerrors.Errorf("Failed to fetch vuls2 db from any repositories. repositories: %+v", c.Repositories) | ||
} | ||
|
||
func (c Config) fetchFromRepository(repoPath string) error { | ||
logging.Log.Infof("Fetch vuls.db from %s", repoPath) | ||
|
||
ctx := context.TODO() | ||
|
||
ms := memory.New() | ||
|
||
repo, err := remote.NewRepository(repoPath) | ||
if err != nil { | ||
return xerrors.Errorf("Failed to create repository client. repository: %s, err: %w", repoPath, err) | ||
} | ||
|
||
manifestDescriptor, err := oras.Copy(ctx, repo, "latest", ms, "latest", oras.DefaultCopyOptions) | ||
if err != nil { | ||
return xerrors.Errorf("Failed to copy. repository: %s, err: %w", repoPath, err) | ||
} | ||
|
||
r, err := ms.Fetch(ctx, manifestDescriptor) | ||
if err != nil { | ||
return xerrors.Errorf("Failed to fetch manifest. err: %w", err) | ||
} | ||
defer r.Close() | ||
|
||
var manifest ocispec.Manifest | ||
if err := json.NewDecoder(content.NewVerifyReader(r, manifestDescriptor)).Decode(&manifest); err != nil { | ||
return xerrors.Errorf("Failed to decode manifest. err: %w", err) | ||
} | ||
|
||
l := func() *ocispec.Descriptor { | ||
for _, l := range manifest.Layers { | ||
if l.MediaType == dbMediaType { | ||
return &l | ||
} | ||
} | ||
return nil | ||
}() | ||
if l == nil { | ||
return xerrors.Errorf("Failed to find digest and filename from layers. actual layers: %#v", manifest.Layers) | ||
} | ||
|
||
r, err = repo.Fetch(ctx, *l) | ||
if err != nil { | ||
return xerrors.Errorf("Failed to fetch content. err: %w", err) | ||
} | ||
defer r.Close() | ||
|
||
d, err := zstd.NewReader(content.NewVerifyReader(r, *l)) | ||
if err != nil { | ||
return errors.Wrap(err, "new zstd reader") | ||
} | ||
defer d.Close() | ||
|
||
if err := os.MkdirAll(c.DbDir, 0755); err != nil { | ||
return errors.Wrapf(err, "mkdir %s", c.DbDir) | ||
} | ||
|
||
f, err := os.Create(filepath.Join(c.DbDir, "vuls2.boltdb")) | ||
if err != nil { | ||
return errors.Wrapf(err, "create %s", filepath.Join(c.DbDir, "vuls2.boltdb")) | ||
} | ||
defer f.Close() | ||
|
||
var pb *progressbar.ProgressBar | ||
pb = progressbar.DefaultBytesSilent(-1) | ||
if !c.Quiet { | ||
pb = progressbar.DefaultBytes(-1, "downloading") | ||
} | ||
if _, err := d.WriteTo(io.MultiWriter(f, pb)); err != nil { | ||
return xerrors.Errorf("Failed to write. filename: %s. err: %w", f.Name(), err) | ||
} | ||
_ = pb.Finish() | ||
|
||
return nil | ||
} |
Oops, something went wrong.