feat: init nightly vuls for blackhat

This commit is contained in:
MaineK00n
2022-11-15 11:26:26 +09:00
parent 1d97e91341
commit 3605645ff6
234 changed files with 6172 additions and 54872 deletions

413
pkg/cmd/db/create/create.go Normal file
View 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])
}
}

View 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"`
}

View 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
View 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
View 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
View 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
}

View 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
}

View 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
}