feat: init nightly vuls for blackhat
This commit is contained in:
413
pkg/cmd/db/create/create.go
Normal file
413
pkg/cmd/db/create/create.go
Normal file
@@ -0,0 +1,413 @@
|
||||
package create
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/future-architect/vuls/pkg/cmd/db/create/vulnsrc"
|
||||
"github.com/future-architect/vuls/pkg/db"
|
||||
"github.com/future-architect/vuls/pkg/util"
|
||||
)
|
||||
|
||||
type DBCreateOption struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
func NewCmdCreate() *cobra.Command {
|
||||
opts := &DBCreateOption{
|
||||
Path: "vuls.db",
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "create",
|
||||
Short: "Create Vuls DB",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
return create(args[0], opts.Path)
|
||||
},
|
||||
Example: heredoc.Doc(`
|
||||
$ vuls db create https://github.com/vulsio/vuls-data.git
|
||||
$ vuls db create /home/MaineK00n/.cache/vuls
|
||||
`),
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&opts.Path, "path", "p", "vuls.db", "path to create Vuls DB")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func create(src, dbpath string) error {
|
||||
datapath := src
|
||||
if u, err := url.Parse(src); err == nil && u.Scheme != "" {
|
||||
cloneDir := filepath.Join(util.CacheDir(), "clone")
|
||||
if err := exec.Command("git", "clone", "--depth", "1", src, cloneDir).Run(); err != nil {
|
||||
return errors.Wrapf(err, "git clone --depth 1 %s %s", src, cloneDir)
|
||||
}
|
||||
datapath = cloneDir
|
||||
}
|
||||
if _, err := os.Stat(datapath); err != nil {
|
||||
return errors.Wrapf(err, "%s not found", datapath)
|
||||
}
|
||||
|
||||
db, err := db.Open("boltdb", dbpath, false)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "open db")
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
if err := filepath.WalkDir(datapath, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
p := strings.TrimPrefix(strings.TrimPrefix(path, datapath), string(os.PathSeparator))
|
||||
srcType, p, found := strings.Cut(p, string(os.PathSeparator))
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
advType, p, found := strings.Cut(p, string(os.PathSeparator))
|
||||
if !found {
|
||||
return errors.Errorf(`unexpected filepath. expected: "%s/["official", ...]/["vulnerability", "os", "library", "cpe"]/...", actual: "%s"`, datapath, path)
|
||||
}
|
||||
|
||||
bs, err := util.Read(path)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "read %s", path)
|
||||
}
|
||||
if len(bs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch advType {
|
||||
case "vulnerability":
|
||||
var src vulnsrc.Vulnerability
|
||||
if err := json.Unmarshal(bs, &src); err != nil {
|
||||
return errors.Wrapf(err, "unmarshal json. path: %s", path)
|
||||
}
|
||||
if err := db.PutVulnerability(srcType, fmt.Sprintf("vulnerability:%s", src.ID), vulnsrc.ToVulsVulnerability(src)); err != nil {
|
||||
return errors.Wrap(err, "put vulnerability")
|
||||
}
|
||||
case "os":
|
||||
advType, err := toAdvType(p)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "path to adv type")
|
||||
}
|
||||
bucket, err := toAdvBucket(p)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "path to adv bucket")
|
||||
}
|
||||
|
||||
switch advType {
|
||||
case "redhat_oval":
|
||||
if strings.Contains(p, "repository_to_cpe.json") {
|
||||
var src vulnsrc.RepositoryToCPE
|
||||
if err := json.Unmarshal(bs, &src); err != nil {
|
||||
return errors.Wrapf(err, "unmarshal json. path: %s", path)
|
||||
}
|
||||
if err := db.PutRedHatRepoToCPE(srcType, bucket, vulnsrc.ToVulsRepositoryToCPE(src)); err != nil {
|
||||
return errors.Wrap(err, "put repository to cpe")
|
||||
}
|
||||
break
|
||||
}
|
||||
var src vulnsrc.DetectPackage
|
||||
if err := json.Unmarshal(bs, &src); err != nil {
|
||||
return errors.Wrapf(err, "unmarshal json. path: %s", path)
|
||||
}
|
||||
pkgs, err := vulnsrc.ToVulsPackage(src, advType)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "to vuls package")
|
||||
}
|
||||
if err := db.PutPackage(srcType, bucket, pkgs); err != nil {
|
||||
return errors.Wrap(err, "put package")
|
||||
}
|
||||
case "windows":
|
||||
if strings.Contains(p, "supercedence.json") {
|
||||
var supercedences []vulnsrc.Supercedence
|
||||
if err := json.Unmarshal(bs, &supercedences); err != nil {
|
||||
return errors.Wrapf(err, "unnmarshal json. path: %s", path)
|
||||
}
|
||||
if err := db.PutWindowsSupercedence(srcType, bucket, vulnsrc.ToVulsSupercedences(supercedences)); err != nil {
|
||||
return errors.Wrap(err, "put supercedence")
|
||||
}
|
||||
break
|
||||
}
|
||||
var src vulnsrc.DetectPackage
|
||||
if err := json.Unmarshal(bs, &src); err != nil {
|
||||
return errors.Wrapf(err, "unmarshal json. path: %s", path)
|
||||
}
|
||||
pkgs, err := vulnsrc.ToVulsPackage(src, advType)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "to vuls package")
|
||||
}
|
||||
if err := db.PutPackage(srcType, bucket, pkgs); err != nil {
|
||||
return errors.Wrap(err, "put package")
|
||||
}
|
||||
default:
|
||||
var src vulnsrc.DetectPackage
|
||||
if err := json.Unmarshal(bs, &src); err != nil {
|
||||
return errors.Wrapf(err, "unmarshal json. path: %s", path)
|
||||
}
|
||||
pkgs, err := vulnsrc.ToVulsPackage(src, advType)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "to vuls package")
|
||||
}
|
||||
if err := db.PutPackage(srcType, bucket, pkgs); err != nil {
|
||||
return errors.Wrap(err, "put package")
|
||||
}
|
||||
}
|
||||
case "library":
|
||||
case "cpe":
|
||||
var src vulnsrc.DetectCPE
|
||||
if err := json.Unmarshal(bs, &src); err != nil {
|
||||
return errors.Wrapf(err, "unmarshal json. path: %s", path)
|
||||
}
|
||||
|
||||
advType, err := toAdvType(p)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "path to adv type")
|
||||
}
|
||||
cs, err := vulnsrc.ToVulsCPEConfiguration(src, advType)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "to vuls cpe configuration")
|
||||
}
|
||||
bucket, err := toAdvBucket(p)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "path to adv bucket")
|
||||
}
|
||||
|
||||
if err := db.PutCPEConfiguration(srcType, bucket, cs); err != nil {
|
||||
return errors.Wrap(err, "put cpe configuration")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func toAdvType(path string) (string, error) {
|
||||
ss := strings.Split(path, string(os.PathSeparator))
|
||||
if len(ss) < 3 && ss[0] != "windows" {
|
||||
return "", errors.Errorf(`unexpected path. accepts: "[<os name>, <library name>, "nvd", "jvn"]/**/*.json*", received: "%s"`, path)
|
||||
}
|
||||
|
||||
switch ss[0] {
|
||||
case "alma", "alpine", "amazon", "epel", "fedora", "oracle", "rocky":
|
||||
return fmt.Sprintf("%s:%s", ss[0], ss[1]), nil
|
||||
case "arch", "freebsd", "gentoo", "windows", "conan", "erlang", "nvd", "jvn":
|
||||
return ss[0], nil
|
||||
case "debian":
|
||||
switch ss[1] {
|
||||
case "oval":
|
||||
return "debian_oval", nil
|
||||
case "tracker":
|
||||
return "debian_security_tracker", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected debian advisory type. accepts: ["oval", "tracker"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "redhat":
|
||||
switch ss[1] {
|
||||
case "api":
|
||||
return "redhat_security_api", nil
|
||||
case "oval":
|
||||
return "redhat_oval", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected redhat advisory type. accepts: ["api", "oval"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "suse":
|
||||
switch ss[1] {
|
||||
case "cvrf":
|
||||
return "suse_cvrf", nil
|
||||
case "oval":
|
||||
return "suse_oval", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected suse advisory type. accepts: ["cvrf", "oval"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "ubuntu":
|
||||
switch ss[1] {
|
||||
case "oval":
|
||||
return "ubuntu_oval", nil
|
||||
case "tracker":
|
||||
return "ubuntu_security_tracker", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected debian advisory type. accepts: ["oval", "tracker"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "cargo":
|
||||
switch ss[1] {
|
||||
case "db":
|
||||
return "cargo_db", nil
|
||||
case "ghsa":
|
||||
return "cargo_ghsa", nil
|
||||
case "osv":
|
||||
return "cargo_osv", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected cargo advisory type. accepts: ["db", "ghsa", "osv"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "composer":
|
||||
switch ss[1] {
|
||||
case "db":
|
||||
return "composer_db", nil
|
||||
case "ghsa":
|
||||
return "composer_ghsa", nil
|
||||
case "glsa":
|
||||
return "composer_glsa", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected composer advisory type. accepts: ["db", "ghsa", "glsa"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "golang":
|
||||
switch ss[1] {
|
||||
case "db":
|
||||
return "golang_db", nil
|
||||
case "ghsa":
|
||||
return "golang_ghsa", nil
|
||||
case "glsa":
|
||||
return "golang_glsa", nil
|
||||
case "govulndb":
|
||||
return "golang_govulndb", nil
|
||||
case "osv":
|
||||
return "golang_osv", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected golang advisory type. accepts: ["db", "ghsa", "glsa", "govulndb", "osv"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "maven":
|
||||
switch ss[1] {
|
||||
case "ghsa":
|
||||
return "maven_ghsa", nil
|
||||
case "glsa":
|
||||
return "maven_glsa", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected maven advisory type. accepts: ["ghsa", "glsa"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "npm":
|
||||
switch ss[1] {
|
||||
case "db":
|
||||
return "npm_db", nil
|
||||
case "ghsa":
|
||||
return "npm_ghsa", nil
|
||||
case "glsa":
|
||||
return "npm_glsa", nil
|
||||
case "osv":
|
||||
return "npm_osv", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected npm advisory type. accepts: ["db", "ghsa", "glsa", "osv"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "nuget":
|
||||
switch ss[1] {
|
||||
case "ghsa":
|
||||
return "nuget_ghsa", nil
|
||||
case "glsa":
|
||||
return "nuget_glsa", nil
|
||||
case "osv":
|
||||
return "nuget_osv", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected nuget advisory type. accepts: ["ghsa", "glsa", "osv"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "pip":
|
||||
switch ss[1] {
|
||||
case "db":
|
||||
return "pip_db", nil
|
||||
case "ghsa":
|
||||
return "pip_ghsa", nil
|
||||
case "glsa":
|
||||
return "pip_glsa", nil
|
||||
case "osv":
|
||||
return "pip_osv", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected pip advisory type. accepts: ["db", "ghsa", "glsa", "osv"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "rubygems":
|
||||
switch ss[1] {
|
||||
case "db":
|
||||
return "rubygems_db", nil
|
||||
case "ghsa":
|
||||
return "rubygems_ghsa", nil
|
||||
case "glsa":
|
||||
return "rubygems_glsa", nil
|
||||
case "osv":
|
||||
return "rubygems_osv", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected rubygems advisory type. accepts: ["db", "ghsa", "glsa", "osv"], received: "%s"`, ss[1])
|
||||
}
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected os or library or cpe. accepts: ["alma", "alpine", "amazon", "arch", "debian", "epel", "fedora", "freebsd", "gentoo", "oracle", "redhat", "rocky", "suse", "ubuntu", "windows", "cargo", "composer", "conan", "erlang", "golang", "maven", "npm", "nuget", "pip", "rubygems", "nvd", "jvn"], received: "%s"`, ss[0])
|
||||
}
|
||||
}
|
||||
|
||||
func toAdvBucket(path string) (string, error) {
|
||||
ss := strings.Split(path, string(os.PathSeparator))
|
||||
if len(ss) < 3 && ss[0] != "windows" {
|
||||
return "", errors.Errorf(`unexpected path. accepts: "[<os name>, <library name>, "nvd", "jvn"]/**/*.json*", received: "%s"`, path)
|
||||
}
|
||||
|
||||
switch ss[0] {
|
||||
case "alma", "alpine", "amazon", "epel", "fedora", "oracle", "rocky":
|
||||
return fmt.Sprintf("%s:%s", ss[0], ss[1]), nil
|
||||
case "arch", "freebsd", "gentoo", "cargo", "composer", "conan", "erlang", "golang", "maven", "npm", "nuget", "pip", "rubygems":
|
||||
return ss[0], nil
|
||||
case "debian":
|
||||
switch ss[1] {
|
||||
case "oval", "tracker":
|
||||
return fmt.Sprintf("%s:%s", ss[0], ss[2]), nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected debian advisory type. accepts: ["oval", "tracker"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "redhat":
|
||||
switch ss[1] {
|
||||
case "api":
|
||||
return fmt.Sprintf("%s:%s", ss[0], ss[2]), nil
|
||||
case "oval":
|
||||
if len(ss) < 4 {
|
||||
return "", errors.Errorf(`unexpected path. accepts: "redhat/oval/<os version>/<stream>/yyyy/*.json*", received: "%s"`, path)
|
||||
}
|
||||
if strings.Contains(path, "repository_to_cpe.json") {
|
||||
return fmt.Sprintf("%s_cpe:%s", ss[0], ss[3]), nil
|
||||
}
|
||||
return fmt.Sprintf("%s:%s", ss[0], ss[3]), nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected redhat advisory type. accepts: ["api", "oval"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "suse":
|
||||
switch ss[1] {
|
||||
case "cvrf", "oval":
|
||||
if len(ss) < 4 {
|
||||
return "", errors.Errorf(`unexpected path. accepts: "suse/[cvrf, oval]/<os>/<version>/yyyy/*.json*", received: "%s"`, path)
|
||||
}
|
||||
return fmt.Sprintf("%s:%s", ss[2], ss[3]), nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected suse advisory type. accepts: ["cvrf", "oval"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "ubuntu":
|
||||
switch ss[1] {
|
||||
case "oval", "tracker":
|
||||
return fmt.Sprintf("%s:%s", ss[0], ss[2]), nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected debian advisory type. accepts: ["oval", "tracker"], received: "%s"`, ss[1])
|
||||
}
|
||||
case "windows":
|
||||
if strings.Contains(path, "supercedence.json") {
|
||||
return "windows_supercedence", nil
|
||||
}
|
||||
return fmt.Sprintf("%s:%s", ss[0], ss[1]), nil
|
||||
case "nvd", "jvn":
|
||||
return "cpe", nil
|
||||
default:
|
||||
return "", errors.Errorf(`unexpected os or library or cpe. accepts: ["alma", "alpine", "amazon", "arch", "debian", "epel", "fedora", "freebsd", "gentoo", "oracle", "redhat", "rocky", "suse", "ubuntu", "windows", "cargo", "composer", "conan", "erlang", "golang", "maven", "npm", "nuget", "pip", "rubygems", "nvd", "jvn"], received: "%s"`, ss[0])
|
||||
}
|
||||
}
|
||||
269
pkg/cmd/db/create/vulnsrc/types.go
Normal file
269
pkg/cmd/db/create/vulnsrc/types.go
Normal file
@@ -0,0 +1,269 @@
|
||||
package vulnsrc
|
||||
|
||||
// https://github.com/MaineK00n/vuls-data-update/blob/main/pkg/build/types.go
|
||||
|
||||
import "time"
|
||||
|
||||
type Vulnerability struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Advisory *Advisories `json:"advisory,omitempty"`
|
||||
Title *Titles `json:"title,omitempty"`
|
||||
Description *Descriptions `json:"description,omitempty"`
|
||||
CVSS *CVSSes `json:"cvss,omitempty"`
|
||||
EPSS *EPSS `json:"epss,omitempty"`
|
||||
CWE *CWEs `json:"cwe,omitempty"`
|
||||
Metasploit []Metasploit `json:"metasploit,omitempty"`
|
||||
Exploit *Exploit `json:"exploit,omitempty"`
|
||||
KEV *KEV `json:"kev,omitempty"`
|
||||
Mitigation *Mitigation `json:"mitigation,omitempty"`
|
||||
Published *Publisheds `json:"published,omitempty"`
|
||||
Modified *Modifieds `json:"modified,omitempty"`
|
||||
References *References `json:"references,omitempty"`
|
||||
}
|
||||
|
||||
type Advisories struct {
|
||||
MITRE *Advisory `json:"mitre,omitempty"`
|
||||
NVD *Advisory `json:"nvd,omitempty"`
|
||||
JVN []Advisory `json:"jvn,omitempty"`
|
||||
Alma map[string][]Advisory `json:"alma,omitempty"`
|
||||
Alpine map[string]Advisory `json:"alpine,omitempty"`
|
||||
Amazon map[string][]Advisory `json:"amazon,omitempty"`
|
||||
Arch []Advisory `json:"arch,omitempty"`
|
||||
DebianOVAL map[string][]Advisory `json:"debian_oval,omitempty"`
|
||||
DebianSecurityTracker map[string]Advisory `json:"debian_security_tracker,omitempty"`
|
||||
FreeBSD []Advisory `json:"freebsd,omitempty"`
|
||||
Oracle map[string][]Advisory `json:"oracle,omitempty"`
|
||||
RedHatOVAL map[string][]Advisory `json:"redhat_oval,omitempty"`
|
||||
SUSEOVAL map[string][]Advisory `json:"suse_oval,omitempty"`
|
||||
SUSECVRF *Advisory `json:"suse_cvrf,omitempty"`
|
||||
UbuntuOVAL map[string][]Advisory `json:"ubuntu_oval,omitempty"`
|
||||
UbuntuSecurityTracker *Advisory `json:"ubuntu_security_tracker,omitempty"`
|
||||
}
|
||||
type Advisory struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
type Titles struct {
|
||||
MITRE string `json:"mitre,omitempty"`
|
||||
NVD string `json:"nvd,omitempty"`
|
||||
JVN map[string]string `json:"jvn,omitempty"`
|
||||
Alma map[string]map[string]string `json:"alma,omitempty"`
|
||||
Alpine map[string]string `json:"alpine,omitempty"`
|
||||
Amazon map[string]map[string]string `json:"amazon,omitempty"`
|
||||
Arch map[string]string `json:"arch,omitempty"`
|
||||
DebianOVAL map[string]map[string]string `json:"debian_oval,omitempty"`
|
||||
DebianSecurityTracker map[string]string `json:"debian_security_tracker,omitempty"`
|
||||
FreeBSD map[string]string `json:"freebsd,omitempty"`
|
||||
Oracle map[string]map[string]string `json:"oracle,omitempty"`
|
||||
RedHatOVAL map[string]map[string]string `json:"redhat_oval,omitempty"`
|
||||
SUSEOVAL map[string]map[string]string `json:"suse_oval,omitempty"`
|
||||
SUSECVRF string `json:"suse_cvrf,omitempty"`
|
||||
UbuntuOVAL map[string]map[string]string `json:"ubuntu_oval,omitempty"`
|
||||
UbuntuSecurityTracker string `json:"ubuntu_security_tracker,omitempty"`
|
||||
}
|
||||
|
||||
type Descriptions struct {
|
||||
MITRE string `json:"mitre,omitempty"`
|
||||
NVD string `json:"nvd,omitempty"`
|
||||
JVN map[string]string `json:"jvn,omitempty"`
|
||||
Alma map[string]map[string]string `json:"alma,omitempty"`
|
||||
Amazon map[string]map[string]string `json:"amazon,omitempty"`
|
||||
DebianOVAL map[string]map[string]string `json:"debian_oval,omitempty"`
|
||||
DebianSecurityTracker map[string]string `json:"debian_security_tracker,omitempty"`
|
||||
FreeBSD map[string]string `json:"freebsd,omitempty"`
|
||||
Oracle map[string]map[string]string `json:"oracle,omitempty"`
|
||||
RedHatOVAL map[string]map[string]string `json:"redhat_oval,omitempty"`
|
||||
SUSEOVAL map[string]map[string]string `json:"suse_oval,omitempty"`
|
||||
SUSECVRF string `json:"suse_cvrf,omitempty"`
|
||||
UbuntuOVAL map[string]map[string]string `json:"ubuntu_oval,omitempty"`
|
||||
UbuntuSecurityTracker string `json:"ubuntu_security_tracker,omitempty"`
|
||||
}
|
||||
|
||||
type CVSSes struct {
|
||||
NVD []CVSS `json:"nvd,omitempty"`
|
||||
JVN map[string][]CVSS `json:"jvn,omitempty"`
|
||||
Alma map[string]map[string][]CVSS `json:"alma,omitempty"`
|
||||
Amazon map[string]map[string][]CVSS `json:"amazon,omitempty"`
|
||||
Arch map[string][]CVSS `json:"arch,omitempty"`
|
||||
Oracle map[string]map[string][]CVSS `json:"oracle,omitempty"`
|
||||
RedHatOVAL map[string]map[string][]CVSS `json:"redhat_oval,omitempty"`
|
||||
SUSEOVAL map[string]map[string][]CVSS `json:"suse_oval,omitempty"`
|
||||
SUSECVRF []CVSS `json:"suse_cvrf,omitempty"`
|
||||
UbuntuOVAL map[string]map[string][]CVSS `json:"ubuntu_oval,omitempty"`
|
||||
UbuntuSecurityTracker []CVSS `json:"ubuntu_security_tracker,omitempty"`
|
||||
}
|
||||
|
||||
type CVSS struct {
|
||||
Version string `json:"version,omitempty"`
|
||||
Source string `json:"source,omitempty"`
|
||||
Vector string `json:"vector,omitempty"`
|
||||
Score *float64 `json:"score,omitempty"`
|
||||
Severity string `json:"severity,omitempty"`
|
||||
}
|
||||
|
||||
type EPSS struct {
|
||||
EPSS *float64 `json:"epss,omitempty"`
|
||||
Percentile *float64 `json:"percentile,omitempty"`
|
||||
}
|
||||
|
||||
type CWEs struct {
|
||||
NVD []string `json:"nvd,omitempty"`
|
||||
JVN map[string][]string `json:"jvn,omitempty"`
|
||||
RedHatOVAL map[string]map[string][]string `json:"redhat_oval,omitempty"`
|
||||
}
|
||||
|
||||
type Metasploit struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
URLs []string `json:"urls,omitempty"`
|
||||
}
|
||||
|
||||
type Exploit struct {
|
||||
NVD []string `json:"nvd,omitempty"`
|
||||
ExploitDB []ExploitDB `json:"exploit_db,omitempty"`
|
||||
GitHub []GitHub `json:"github,omitempty"`
|
||||
InTheWild []InTheWild `json:"inthewild,omitempty"`
|
||||
Trickest *Trickest `json:"trickest,omitempty"`
|
||||
}
|
||||
|
||||
type ExploitDB struct {
|
||||
ID string `json:"name,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
FileURL string `json:"file_url,omitempty"`
|
||||
}
|
||||
|
||||
type GitHub struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Stars int `json:"stars"`
|
||||
Forks int `json:"forks"`
|
||||
Watches int `json:"watches"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
type InTheWild struct {
|
||||
Source string `json:"source,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
type Trickest struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
PoC *TrickestPoc `json:"poc,omitempty"`
|
||||
}
|
||||
|
||||
type TrickestPoc struct {
|
||||
Reference []string `json:"reference,omitempty"`
|
||||
GitHub []string `json:"github,omitempty"`
|
||||
}
|
||||
|
||||
type KEV struct {
|
||||
Title string `json:"title,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
RequiredAction string `json:"required_action,omitempty"`
|
||||
DueDate *time.Time `json:"due_date,omitempty"`
|
||||
}
|
||||
|
||||
type Mitigation struct {
|
||||
NVD []string `json:"nvd,omitempty"`
|
||||
UbuntuSecurityTracker string `json:"ubuntu_security_tracker,omitempty"`
|
||||
}
|
||||
|
||||
type Publisheds struct {
|
||||
MITRE *time.Time `json:"mitre,omitempty"`
|
||||
NVD *time.Time `json:"nvd,omitempty"`
|
||||
JVN map[string]*time.Time `json:"jvn,omitempty"`
|
||||
Alma map[string]map[string]*time.Time `json:"alma,omitempty"`
|
||||
Amazon map[string]map[string]*time.Time `json:"amazon,omitempty"`
|
||||
FreeBSD map[string]*time.Time `json:"freebsd,omitempty"`
|
||||
Oracle map[string]map[string]*time.Time `json:"oracle,omitempty"`
|
||||
RedHatOVAL map[string]map[string]*time.Time `json:"redhat_oval,omitempty"`
|
||||
SUSEOVAL map[string]map[string]*time.Time `json:"suse_oval,omitempty"`
|
||||
SUSECVRF *time.Time `json:"suse_cvrf,omitempty"`
|
||||
UbuntuOVAL map[string]map[string]*time.Time `json:"ubuntu_oval,omitempty"`
|
||||
UbuntuSecurityTracker *time.Time `json:"ubuntu_security_tracker,omitempty"`
|
||||
}
|
||||
|
||||
type Modifieds struct {
|
||||
MITRE *time.Time `json:"mitre,omitempty"`
|
||||
NVD *time.Time `json:"nvd,omitempty"`
|
||||
JVN map[string]*time.Time `json:"jvn,omitempty"`
|
||||
Alma map[string]map[string]*time.Time `json:"alma,omitempty"`
|
||||
Amazon map[string]map[string]*time.Time `json:"amazon,omitempty"`
|
||||
FreeBSD map[string]*time.Time `json:"freebsd,omitempty"`
|
||||
RedHatOVAL map[string]map[string]*time.Time `json:"redhat_oval,omitempty"`
|
||||
SUSEOVAL map[string]map[string]*time.Time `json:"suse_oval,omitempty"`
|
||||
SUSECVRF *time.Time `json:"suse_cvrf,omitempty"`
|
||||
}
|
||||
|
||||
type References struct {
|
||||
MITRE []Reference `json:"mitre,omitempty"`
|
||||
NVD []Reference `json:"nvd,omitempty"`
|
||||
JVN map[string][]Reference `json:"jvn,omitempty"`
|
||||
Alma map[string]map[string][]Reference `json:"alma,omitempty"`
|
||||
Amazon map[string]map[string][]Reference `json:"amazon,omitempty"`
|
||||
Arch map[string][]Reference `json:"arch,omitempty"`
|
||||
DebianOVAL map[string]map[string][]Reference `json:"debian_oval,omitempty"`
|
||||
DebianSecurityTracker map[string][]Reference `json:"debian_security_tracker,omitempty"`
|
||||
FreeBSD map[string][]Reference `json:"freebsd,omitempty"`
|
||||
Oracle map[string]map[string][]Reference `json:"oracle,omitempty"`
|
||||
RedHatOVAL map[string]map[string][]Reference `json:"redhat_oval,omitempty"`
|
||||
SUSEOVAL map[string]map[string][]Reference `json:"suse_oval,omitempty"`
|
||||
SUSECVRF []Reference `json:"suse_cvrf,omitempty"`
|
||||
UbuntuOVAL map[string]map[string][]Reference `json:"ubuntu_oval,omitempty"`
|
||||
UbuntuSecurityTracker []Reference `json:"ubuntu_security_tracker,omitempty"`
|
||||
}
|
||||
|
||||
type Reference struct {
|
||||
Source string `json:"source,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
type DetectCPE struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Configurations map[string][]CPEConfiguration `json:"configurations,omitempty"`
|
||||
}
|
||||
|
||||
type CPEConfiguration struct {
|
||||
Vulnerable []CPE `json:"vulnerable,omitempty"`
|
||||
RunningOn []CPE `json:"running_on,omitempty"`
|
||||
}
|
||||
|
||||
type CPE struct {
|
||||
CPEVersion string `json:"cpe_version,omitempty"`
|
||||
CPE string `json:"cpe,omitempty"`
|
||||
Version []Version `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
type DetectPackage struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Packages map[string][]Package `json:"packages,omitempty"`
|
||||
}
|
||||
|
||||
type Package struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Version [][]Version `json:"version,omitempty"`
|
||||
ModularityLabel string `json:"modularity_label,omitempty"`
|
||||
Arch []string `json:"arch,omitempty"`
|
||||
Repository string `json:"repository,omitempty"`
|
||||
CPE []string `json:"cpe,omitempty"`
|
||||
}
|
||||
|
||||
type Version struct {
|
||||
Operator string `json:"operator,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
type RepositoryToCPE map[string][]string
|
||||
|
||||
type Supercedence struct {
|
||||
KBID string `json:"KBID,omitempty"`
|
||||
Supersededby struct {
|
||||
KBIDs []string `json:"KBIDs,omitempty"`
|
||||
} `json:"Supersededby,omitempty"`
|
||||
}
|
||||
619
pkg/cmd/db/create/vulnsrc/vulnsrc.go
Normal file
619
pkg/cmd/db/create/vulnsrc/vulnsrc.go
Normal file
@@ -0,0 +1,619 @@
|
||||
package vulnsrc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/knqyf263/go-cpe/common"
|
||||
"github.com/knqyf263/go-cpe/naming"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/future-architect/vuls/pkg/db/types"
|
||||
)
|
||||
|
||||
func ToVulsVulnerability(src Vulnerability) types.Vulnerability {
|
||||
return types.Vulnerability{
|
||||
ID: src.ID,
|
||||
Advisory: toVulsAdvisory(src.Advisory),
|
||||
Title: toVulsTitle(src.Title),
|
||||
Description: toVulsDescription(src.Description),
|
||||
CVSS: toVulsCVSS(src.CVSS),
|
||||
EPSS: toVulsEPSS(src.EPSS),
|
||||
CWE: toVulsCWE(src.CWE),
|
||||
Metasploit: toVulsMetasploit(src.Metasploit),
|
||||
Exploit: toVulsExploit(src.Exploit),
|
||||
KEV: src.KEV != nil,
|
||||
Published: toVulsPublished(src.Published),
|
||||
Modified: toVulsModified(src.Modified),
|
||||
Reference: toVulsReference(src.References),
|
||||
}
|
||||
}
|
||||
|
||||
func toVulsAdvisory(src *Advisories) []string {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var advs []string
|
||||
if src.MITRE != nil {
|
||||
advs = append(advs, "mitre")
|
||||
}
|
||||
if src.NVD != nil {
|
||||
advs = append(advs, "nvd")
|
||||
}
|
||||
for _, a := range src.JVN {
|
||||
advs = append(advs, fmt.Sprintf("jvn:%s", a.ID))
|
||||
}
|
||||
for v, as := range src.Alma {
|
||||
for _, a := range as {
|
||||
advs = append(advs, fmt.Sprintf("alma:%s:%s", v, a.ID))
|
||||
}
|
||||
}
|
||||
for v, a := range src.Alpine {
|
||||
advs = append(advs, fmt.Sprintf("alpine:%s:%s", v, a.ID))
|
||||
}
|
||||
for v, as := range src.Amazon {
|
||||
for _, a := range as {
|
||||
advs = append(advs, fmt.Sprintf("amazon:%s:%s", v, a.ID))
|
||||
}
|
||||
}
|
||||
for _, a := range src.Arch {
|
||||
advs = append(advs, fmt.Sprintf("arch:%s", a.ID))
|
||||
}
|
||||
for v, as := range src.DebianOVAL {
|
||||
for _, a := range as {
|
||||
advs = append(advs, fmt.Sprintf("debian_oval:%s:%s", v, a.ID))
|
||||
}
|
||||
}
|
||||
for v, a := range src.DebianSecurityTracker {
|
||||
advs = append(advs, fmt.Sprintf("debian_security_tracker:%s:%s", v, a.ID))
|
||||
}
|
||||
for _, a := range src.FreeBSD {
|
||||
advs = append(advs, fmt.Sprintf("freebsd:%s", a.ID))
|
||||
}
|
||||
for v, as := range src.Oracle {
|
||||
for _, a := range as {
|
||||
advs = append(advs, fmt.Sprintf("oracle:%s:%s", v, a.ID))
|
||||
}
|
||||
}
|
||||
for v, as := range src.RedHatOVAL {
|
||||
for _, a := range as {
|
||||
advs = append(advs, fmt.Sprintf("redhat_oval:%s:%s", v, a.ID))
|
||||
}
|
||||
}
|
||||
for v, as := range src.SUSEOVAL {
|
||||
for _, a := range as {
|
||||
advs = append(advs, fmt.Sprintf("suse_oval:%s:%s", v, a.ID))
|
||||
}
|
||||
}
|
||||
if src.SUSECVRF != nil {
|
||||
advs = append(advs, "suse_cvrf")
|
||||
}
|
||||
for v, as := range src.UbuntuOVAL {
|
||||
for _, a := range as {
|
||||
advs = append(advs, fmt.Sprintf("ubuntu_oval:%s:%s", v, a.ID))
|
||||
}
|
||||
}
|
||||
if src.UbuntuSecurityTracker != nil {
|
||||
advs = append(advs, "ubuntu_security_tracker")
|
||||
}
|
||||
return advs
|
||||
}
|
||||
|
||||
func toVulsTitle(src *Titles) string {
|
||||
if src == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if src.NVD != "" {
|
||||
return src.NVD
|
||||
}
|
||||
if src.MITRE != "" {
|
||||
return src.MITRE
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func toVulsDescription(src *Descriptions) string {
|
||||
if src == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if src.NVD != "" {
|
||||
return src.NVD
|
||||
}
|
||||
if src.MITRE != "" {
|
||||
return src.MITRE
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func toVulsCVSS(src *CVSSes) []types.CVSS {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var cvsses []types.CVSS
|
||||
for _, c := range src.NVD {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: "nvd",
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
for id, cs := range src.JVN {
|
||||
for _, c := range cs {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: fmt.Sprintf("jvn:%s", id),
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
}
|
||||
for v, idcs := range src.Alma {
|
||||
for id, cs := range idcs {
|
||||
for _, c := range cs {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: fmt.Sprintf("alma:%s:%s", v, id),
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for v, idcs := range src.Amazon {
|
||||
for id, cs := range idcs {
|
||||
for _, c := range cs {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: fmt.Sprintf("amazon:%s:%s", v, id),
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for id, cs := range src.Arch {
|
||||
for _, c := range cs {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: fmt.Sprintf("arch:%s", id),
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
}
|
||||
for v, idcs := range src.Oracle {
|
||||
for id, cs := range idcs {
|
||||
for _, c := range cs {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: fmt.Sprintf("oracle:%s:%s", v, id),
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for v, idcs := range src.RedHatOVAL {
|
||||
for id, cs := range idcs {
|
||||
for _, c := range cs {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: fmt.Sprintf("redhat_oval:%s:%s", v, id),
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for v, idcs := range src.SUSEOVAL {
|
||||
for id, cs := range idcs {
|
||||
for _, c := range cs {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: fmt.Sprintf("suse_oval:%s:%s", v, id),
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, c := range src.SUSECVRF {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: "suse_cvrf",
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
for v, idcs := range src.UbuntuOVAL {
|
||||
for id, cs := range idcs {
|
||||
for _, c := range cs {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: fmt.Sprintf("ubuntu_oval:%s:%s", v, id),
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, c := range src.UbuntuSecurityTracker {
|
||||
cvsses = append(cvsses, types.CVSS{
|
||||
Source: "ubuntu_security_tracker",
|
||||
Version: c.Version,
|
||||
Vector: c.Vector,
|
||||
Score: c.Score,
|
||||
Severity: c.Severity,
|
||||
})
|
||||
}
|
||||
|
||||
return cvsses
|
||||
}
|
||||
|
||||
func toVulsEPSS(src *EPSS) *types.EPSS {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
return &types.EPSS{EPSS: src.EPSS, Percentile: src.Percentile}
|
||||
}
|
||||
|
||||
func toVulsCWE(src *CWEs) []types.CWE {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
m := map[string][]string{}
|
||||
for _, c := range src.NVD {
|
||||
m[c] = append(m[c], "nvd")
|
||||
}
|
||||
for id, cs := range src.JVN {
|
||||
for _, c := range cs {
|
||||
m[c] = append(m[c], fmt.Sprintf("jvn:%s", id))
|
||||
}
|
||||
}
|
||||
for v, idcs := range src.RedHatOVAL {
|
||||
for id, cs := range idcs {
|
||||
for _, c := range cs {
|
||||
m[c] = append(m[c], fmt.Sprintf("redhat_oval:%s:%s", v, id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var cwes []types.CWE
|
||||
for id, srcs := range m {
|
||||
cwes = append(cwes, types.CWE{
|
||||
Source: srcs,
|
||||
ID: id,
|
||||
})
|
||||
}
|
||||
return cwes
|
||||
}
|
||||
|
||||
func toVulsMetasploit(src []Metasploit) []types.Metasploit {
|
||||
ms := make([]types.Metasploit, 0, len(src))
|
||||
for _, m := range src {
|
||||
ms = append(ms, types.Metasploit{
|
||||
Title: m.Title,
|
||||
URL: m.URLs[0],
|
||||
})
|
||||
}
|
||||
return ms
|
||||
}
|
||||
|
||||
func toVulsExploit(src *Exploit) []types.Exploit {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
m := map[string][]string{}
|
||||
for _, e := range src.NVD {
|
||||
m[e] = append(m[e], "nvd")
|
||||
}
|
||||
for _, e := range src.ExploitDB {
|
||||
m[e.URL] = append(m[e.URL], "exploit-db")
|
||||
}
|
||||
for _, e := range src.GitHub {
|
||||
m[e.URL] = append(m[e.URL], "github")
|
||||
}
|
||||
for _, e := range src.InTheWild {
|
||||
m[e.URL] = append(m[e.URL], "inthewild")
|
||||
}
|
||||
if src.Trickest != nil {
|
||||
if src.Trickest.PoC != nil {
|
||||
for _, e := range src.Trickest.PoC.Reference {
|
||||
m[e] = append(m[e], "trickest")
|
||||
}
|
||||
for _, e := range src.Trickest.PoC.GitHub {
|
||||
m[e] = append(m[e], "trickest")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var es []types.Exploit
|
||||
for u, srcs := range m {
|
||||
es = append(es, types.Exploit{
|
||||
Source: srcs,
|
||||
URL: u,
|
||||
})
|
||||
}
|
||||
return es
|
||||
}
|
||||
|
||||
func toVulsPublished(src *Publisheds) *time.Time {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if src.NVD != nil {
|
||||
return src.NVD
|
||||
}
|
||||
if src.MITRE != nil {
|
||||
return src.MITRE
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func toVulsModified(src *Modifieds) *time.Time {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if src.NVD != nil {
|
||||
return src.NVD
|
||||
}
|
||||
if src.MITRE != nil {
|
||||
return src.MITRE
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func toVulsReference(src *References) []string {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
m := map[string]struct{}{}
|
||||
for _, r := range src.MITRE {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
for _, r := range src.NVD {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
for _, rs := range src.JVN {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
for _, idrs := range src.Alma {
|
||||
for _, rs := range idrs {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, idrs := range src.Amazon {
|
||||
for _, rs := range idrs {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, rs := range src.Arch {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
for _, idrs := range src.DebianOVAL {
|
||||
for _, rs := range idrs {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, rs := range src.DebianSecurityTracker {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
for _, rs := range src.FreeBSD {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
for _, idrs := range src.Oracle {
|
||||
for _, rs := range idrs {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, idrs := range src.RedHatOVAL {
|
||||
for _, rs := range idrs {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, idrs := range src.SUSEOVAL {
|
||||
for _, rs := range idrs {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, r := range src.SUSECVRF {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
for _, idrs := range src.UbuntuOVAL {
|
||||
for _, rs := range idrs {
|
||||
for _, r := range rs {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, r := range src.UbuntuSecurityTracker {
|
||||
m[r.URL] = struct{}{}
|
||||
}
|
||||
|
||||
return maps.Keys(m)
|
||||
}
|
||||
|
||||
func ToVulsPackage(src DetectPackage, advType string) (map[string]types.Packages, error) {
|
||||
m := map[string]types.Packages{}
|
||||
for id, ps := range src.Packages {
|
||||
id = fmt.Sprintf("%s:%s", advType, id)
|
||||
for _, p := range ps {
|
||||
name, err := toVulsPackageName(p.Name, p.ModularityLabel)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "to vuls package name")
|
||||
}
|
||||
|
||||
base, ok := m[name]
|
||||
if !ok {
|
||||
base = types.Packages{
|
||||
ID: src.ID,
|
||||
Package: map[string]types.Package{},
|
||||
}
|
||||
}
|
||||
|
||||
vers := make([][]types.Version, 0, len(p.Version))
|
||||
for _, vs := range p.Version {
|
||||
vss := make([]types.Version, 0, len(vs))
|
||||
for _, v := range vs {
|
||||
vss = append(vss, types.Version{
|
||||
Operator: v.Operator,
|
||||
Version: v.Version,
|
||||
})
|
||||
}
|
||||
vers = append(vers, vss)
|
||||
}
|
||||
|
||||
base.Package[id] = types.Package{
|
||||
Status: p.Status,
|
||||
Version: vers,
|
||||
Arch: p.Arch,
|
||||
Repository: p.Repository,
|
||||
CPE: p.CPE,
|
||||
}
|
||||
|
||||
m[name] = base
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func toVulsPackageName(name, modularitylabel string) (string, error) {
|
||||
if modularitylabel == "" {
|
||||
return name, nil
|
||||
}
|
||||
|
||||
ss := strings.Split(modularitylabel, ":")
|
||||
if len(ss) < 2 {
|
||||
return name, errors.Errorf(`[WARN] unexpected modularitylabel. accepts: "<module name>:<stream>(:<version>:<context>:<arch>)", received: "%s"`, modularitylabel)
|
||||
}
|
||||
return fmt.Sprintf("%s:%s::%s", ss[0], ss[1], name), nil
|
||||
}
|
||||
|
||||
func ToVulsCPEConfiguration(src DetectCPE, advType string) (map[string]types.CPEConfigurations, error) {
|
||||
m := map[string][]types.CPEConfiguration{}
|
||||
for id, cs := range src.Configurations {
|
||||
id = fmt.Sprintf("%s:%s", advType, id)
|
||||
for _, c := range cs {
|
||||
rs := make([]types.CPE, 0, len(c.RunningOn))
|
||||
for _, r := range c.RunningOn {
|
||||
rs = append(rs, toVulnsrcCPEtoVulsCPE(r))
|
||||
}
|
||||
|
||||
for _, v := range c.Vulnerable {
|
||||
m[id] = append(m[id], types.CPEConfiguration{
|
||||
Vulnerable: toVulnsrcCPEtoVulsCPE(v),
|
||||
RunningOn: rs,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m2 := map[string]types.CPEConfigurations{}
|
||||
for id, cs := range m {
|
||||
for _, c := range cs {
|
||||
pvp, err := toVulsCPEConfigurationName(c.Vulnerable.CPEVersion, c.Vulnerable.CPE)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "to vuls cpe configuration name")
|
||||
}
|
||||
|
||||
base, ok := m2[pvp]
|
||||
if !ok {
|
||||
base = types.CPEConfigurations{
|
||||
ID: src.ID,
|
||||
Configuration: map[string][]types.CPEConfiguration{},
|
||||
}
|
||||
}
|
||||
base.Configuration[id] = append(base.Configuration[id], c)
|
||||
|
||||
m2[pvp] = base
|
||||
}
|
||||
}
|
||||
|
||||
return m2, nil
|
||||
}
|
||||
|
||||
func toVulsCPEConfigurationName(version string, cpe string) (string, error) {
|
||||
var (
|
||||
wfn common.WellFormedName
|
||||
err error
|
||||
)
|
||||
switch version {
|
||||
case "2.3":
|
||||
wfn, err = naming.UnbindFS(cpe)
|
||||
default:
|
||||
wfn, err = naming.UnbindURI(cpe)
|
||||
}
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "unbind %s", cpe)
|
||||
}
|
||||
return fmt.Sprintf("%s:%s:%s", wfn.GetString(common.AttributePart), wfn.GetString(common.AttributeVendor), wfn.GetString(common.AttributeProduct)), nil
|
||||
}
|
||||
|
||||
func toVulnsrcCPEtoVulsCPE(s CPE) types.CPE {
|
||||
d := types.CPE{
|
||||
CPEVersion: s.CPEVersion,
|
||||
CPE: s.CPE,
|
||||
}
|
||||
for _, v := range s.Version {
|
||||
d.Version = append(d.Version, types.Version{
|
||||
Operator: v.Operator,
|
||||
Version: v.Version,
|
||||
})
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func ToVulsRepositoryToCPE(src RepositoryToCPE) types.RepositoryToCPE {
|
||||
return types.RepositoryToCPE(src)
|
||||
}
|
||||
|
||||
func ToVulsSupercedences(src []Supercedence) types.Supercedence {
|
||||
ss := types.Supercedence{}
|
||||
for _, s := range src {
|
||||
ss[s.KBID] = s.Supersededby.KBIDs
|
||||
}
|
||||
return ss
|
||||
}
|
||||
37
pkg/cmd/db/db.go
Normal file
37
pkg/cmd/db/db.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
dbCreateCmd "github.com/future-architect/vuls/pkg/cmd/db/create"
|
||||
dbEditCmd "github.com/future-architect/vuls/pkg/cmd/db/edit"
|
||||
dbFetchCmd "github.com/future-architect/vuls/pkg/cmd/db/fetch"
|
||||
dbSearchCmd "github.com/future-architect/vuls/pkg/cmd/db/search"
|
||||
dbUploadCmd "github.com/future-architect/vuls/pkg/cmd/db/upload"
|
||||
)
|
||||
|
||||
func NewCmdDB() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "db <subcommand>",
|
||||
Short: "Vuls DB Operation",
|
||||
Example: heredoc.Doc(`
|
||||
$ vuls db create https://github.com/vulsio/vuls-data.git
|
||||
$ vuls db create /home/MaineK00n/.cache/vuls
|
||||
$ vuls db edit ubuntu 22.04 openssl
|
||||
$ vuls db edit vulnerability CVE-2022-3602
|
||||
$ vuls db fetch
|
||||
$ vuls db fetch ghcr.io/vuls/db
|
||||
$ vuls db search ubuntu 22.04 openssl
|
||||
$ vuls db search vulnerability CVE-2022-3602
|
||||
`),
|
||||
}
|
||||
|
||||
cmd.AddCommand(dbCreateCmd.NewCmdCreate())
|
||||
cmd.AddCommand(dbEditCmd.NewCmdEdit())
|
||||
cmd.AddCommand(dbFetchCmd.NewCmdFetch())
|
||||
cmd.AddCommand(dbSearchCmd.NewCmdSearch())
|
||||
cmd.AddCommand(dbUploadCmd.NewCmdUpload())
|
||||
|
||||
return cmd
|
||||
}
|
||||
23
pkg/cmd/db/edit/edit.go
Normal file
23
pkg/cmd/db/edit/edit.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package edit
|
||||
|
||||
import (
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewCmdEdit() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "edit",
|
||||
Short: "Edit Data in Vuls DB",
|
||||
Args: cobra.RangeArgs(2, 3),
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return nil
|
||||
},
|
||||
Example: heredoc.Doc(`
|
||||
$ vuls db edit ubuntu 22.04 openssl
|
||||
$ vuls db edit vulnerability CVE-2022-3602
|
||||
`),
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
152
pkg/cmd/db/fetch/fetch.go
Normal file
152
pkg/cmd/db/fetch/fetch.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package fetch
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"oras.land/oras-go/v2/content"
|
||||
"oras.land/oras-go/v2/registry/remote"
|
||||
)
|
||||
|
||||
type DBFetchOption struct {
|
||||
Path string
|
||||
PlainHTTP bool
|
||||
}
|
||||
|
||||
const (
|
||||
defaultVulsDBRepository = "ghcr.io/mainek00n/vuls-data/vuls-db"
|
||||
defaultTag = "latest"
|
||||
vulsDBConfigMediaType = "application/vnd.vuls.vuls.db"
|
||||
vulsDBLayerMediaType = "application/vnd.vuls.vuls.db.layer.v1.tar+gzip"
|
||||
)
|
||||
|
||||
func NewCmdFetch() *cobra.Command {
|
||||
opts := &DBFetchOption{
|
||||
Path: "vuls.db",
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "fetch",
|
||||
Short: "Fetch Vuls DB",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
p := defaultVulsDBRepository
|
||||
if len(args) > 0 {
|
||||
p = args[0]
|
||||
}
|
||||
return fetch(context.Background(), p, opts.Path, opts.PlainHTTP)
|
||||
},
|
||||
Example: heredoc.Doc(`
|
||||
$ vuls db fetch
|
||||
$ vuls db fetch ghcr.io/vuls/db
|
||||
`),
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&opts.Path, "path", "p", "vuls.db", "path to fetch Vuls DB")
|
||||
cmd.Flags().BoolVarP(&opts.PlainHTTP, "plain-http", "", false, "container registry is provided with plain http")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func fetch(ctx context.Context, ref, dbpath string, plainHTTP bool) error {
|
||||
repo, err := remote.NewRepository(ref)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
if plainHTTP {
|
||||
repo.PlainHTTP = true
|
||||
}
|
||||
|
||||
desc, err := repo.Resolve(ctx, defaultTag)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
pulledBlob, err := content.FetchAll(ctx, repo, desc)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
var manifest ocispec.Manifest
|
||||
if err := json.Unmarshal(pulledBlob, &manifest); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
if manifest.Config.MediaType != vulsDBConfigMediaType {
|
||||
return errors.New("not vuls repository")
|
||||
}
|
||||
|
||||
for _, l := range manifest.Layers {
|
||||
if l.MediaType != vulsDBLayerMediaType {
|
||||
continue
|
||||
}
|
||||
|
||||
desc, err := repo.Blobs().Resolve(ctx, l.Digest.String())
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
rc, err := repo.Fetch(ctx, desc)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
bs, err := content.ReadAll(rc, desc)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
gr, err := gzip.NewReader(bytes.NewReader(bs))
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
defer gr.Close()
|
||||
|
||||
tr := tar.NewReader(gr)
|
||||
for {
|
||||
header, err := tr.Next()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return errors.Wrap(err, "Next()")
|
||||
}
|
||||
|
||||
switch header.Typeflag {
|
||||
case tar.TypeDir:
|
||||
if err := os.MkdirAll(filepath.Join(dbpath, header.Name), header.FileInfo().Mode()); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
case tar.TypeReg:
|
||||
if err := func() error {
|
||||
f, err := os.OpenFile(dbpath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, os.FileMode(header.Mode))
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := io.Copy(f, tr); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}(); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.Errorf("unknown type: %s in %s", header.Typeflag, header.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
23
pkg/cmd/db/search/search.go
Normal file
23
pkg/cmd/db/search/search.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewCmdSearch() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "search",
|
||||
Short: "Search Vulnerabilty/Package in Vuls DB",
|
||||
Args: cobra.RangeArgs(2, 3),
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return nil
|
||||
},
|
||||
Example: heredoc.Doc(`
|
||||
$ vuls db search ubuntu 22.04 openssl
|
||||
$ vuls db search vulnerability CVE-2022-3602
|
||||
`),
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
23
pkg/cmd/db/upload/upload.go
Normal file
23
pkg/cmd/db/upload/upload.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package upload
|
||||
|
||||
import (
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewCmdUpload() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "upload",
|
||||
Short: "Upload Vuls DB",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return nil
|
||||
},
|
||||
Example: heredoc.Doc(`
|
||||
$ vuls db upload
|
||||
$ vuls db upload ghcr.io/vuls/db
|
||||
`),
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
Reference in New Issue
Block a user