Skip to content

Commit

Permalink
feat!(detector): use vuls2 for redhat/centos/alma/rocky
Browse files Browse the repository at this point in the history
  • Loading branch information
shino committed Dec 1, 2024
1 parent 81a4921 commit f97a292
Show file tree
Hide file tree
Showing 9 changed files with 883 additions and 22 deletions.
1 change: 1 addition & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type Config struct {
Metasploit MetasploitConf `json:"metasploit,omitempty"`
KEVuln KEVulnConf `json:"kevuln,omitempty"`
Cti CtiConf `json:"cti,omitempty"`
Vuls2 Vuls2DictConf `json:"vuls2Dict,omitempty"`

Slack SlackConf `json:"-"`
EMail SMTPConf `json:"-"`
Expand Down
6 changes: 6 additions & 0 deletions config/vulnDictConf.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,9 @@ func (cnf *CtiConf) Init() {
cnf.setDefault("go-cti.sqlite3")
cnf.DebugSQL = Conf.DebugSQL
}

type Vuls2DictConf struct {
Repositories []string
Dir string
SkipUpdate bool
}
17 changes: 15 additions & 2 deletions detector/detector.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/future-architect/vuls/constant"
"github.com/future-architect/vuls/contrib/owasp-dependency-check/parser"
"github.com/future-architect/vuls/cwe"
"github.com/future-architect/vuls/detector/vuls2"
"github.com/future-architect/vuls/gost"
"github.com/future-architect/vuls/logging"
"github.com/future-architect/vuls/models"
Expand Down Expand Up @@ -50,7 +51,7 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
return nil, xerrors.Errorf("Failed to fill with Library dependency: %w", err)
}

if err := DetectPkgCves(&r, config.Conf.OvalDict, config.Conf.Gost, config.Conf.LogOpts); err != nil {
if err := DetectPkgCves(&r, config.Conf.OvalDict, config.Conf.Gost, config.Conf.Vuls2, config.Conf.LogOpts, config.Conf.NoProgress); err != nil {
return nil, xerrors.Errorf("Failed to detect Pkg CVE: %w", err)
}

Expand Down Expand Up @@ -318,14 +319,19 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {

// DetectPkgCves detects OS pkg cves
// pass 2 configs
func DetectPkgCves(r *models.ScanResult, ovalCnf config.GovalDictConf, gostCnf config.GostConf, logOpts logging.LogOpts) error {
func DetectPkgCves(r *models.ScanResult, ovalCnf config.GovalDictConf, gostCnf config.GostConf, vuls2Cnf config.Vuls2DictConf, logOpts logging.LogOpts, noProgress bool) error {
// Pkg Scan
if isPkgCvesDetactable(r) {
// OVAL, gost(Debian Security Tracker) does not support Package for Raspbian, so skip it.
if r.Family == constant.Raspbian {
r = r.RemoveRaspbianPackFromResult()
}

// Vuls2
if err := vuls2.Detect(r, vuls2Cnf, noProgress); err != nil {
return xerrors.Errorf("Failed to detect CVE with Vuls2: %w", err)
}

// OVAL
if err := detectPkgsCvesWithOval(ovalCnf, r, logOpts); err != nil {
return xerrors.Errorf("Failed to detect CVE with OVAL: %w", err)
Expand Down Expand Up @@ -538,6 +544,13 @@ func detectPkgsCvesWithOval(cnf config.GovalDictConf, r *models.ScanResult, logO
logging.Log.Infof("Skip OVAL and Scan with gost alone.")
logging.Log.Infof("%s: %d CVEs are detected with OVAL", r.FormatServerName(), 0)
return nil
case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky:
logging.Log.Debugf("Skip OVAL and Scan by Vuls2")
return nil
case constant.Oracle:
// FIXME: for testing, remove later
logging.Log.Debugf("Skip OVAL and Scan by Vuls2")
return nil
case constant.Windows, constant.MacOSX, constant.MacOSXServer, constant.MacOS, constant.MacOSServer, constant.FreeBSD, constant.ServerTypePseudo:
return nil
default:
Expand Down
211 changes: 211 additions & 0 deletions detector/vuls2/db.go
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
}
Loading

0 comments on commit f97a292

Please sign in to comment.