Skip to content

Commit

Permalink
feat(commands): add search command
Browse files Browse the repository at this point in the history
  • Loading branch information
MaineK00n committed Apr 24, 2024
1 parent 7d9560e commit 304f4b0
Show file tree
Hide file tree
Showing 2 changed files with 336 additions and 0 deletions.
133 changes: 133 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ Available Commands:
completion generate the autocompletion script for the specified shell
fetch Fetch Vulnerability dictionary
help Help about any command
search Search for Vulnerability in the dictionary
server Start CVE dictionary HTTP Server
version Show version

Expand Down Expand Up @@ -321,6 +322,138 @@ Global Flags:
----
### Usage: Search Command
```bash
$ go-cve-dictionary search --help
Search for Vulnerability in the dictionary

Usage:
go-cve-dictionary search [command]

Available Commands:
cpe Search for Vulnerability in the dictionary by CPE
cve Search for Vulnerability in the dictionary by CVEID

Flags:
-h, --help help for search

Global Flags:
--config string config file (default is $HOME/.go-cve-dictionary.yaml)
--dbpath string /path/to/sqlite3 or SQL connection string (default "$PWD/cve.sqlite3")
--dbtype string Database type to store data in (sqlite3, mysql, postgres or redis supported) (default "sqlite3")
--debug debug mode (default: false)
--debug-sql SQL debug mode
--http-proxy string http://proxy-url:port (default: empty)
--log-dir string /path/to/log (default "/var/log/go-cve-dictionary")
--log-json output log as JSON
--log-to-file output log to file

Use "go-cve-dictionary search [command] --help" for more information about a command.
```
#### Search All CVE IDs
```bash
$ go-cve-dictionary search cve
[
"CVE-2023-38624",
"CVE-2024-20750",
"CVE-2024-21101",
"CVE-2023-27427",
"CVE-2023-30445",
...
```
#### Search by CVE ID(s)
```bash
$ go-cve-dictionary search cve CVE-2024-3400
{
"CveID": "CVE-2024-3400",
"Nvds": [
{
"CveID": "CVE-2024-3400",
"Descriptions": [
{
"Lang": "en",
"Value": "A command injection as a result of arbitrary file creation vulnerability in the GlobalProtect feature of Palo Alto Networks PAN-OS software for specific PAN-OS versions and distinct feature configurations may enable an unauthenticated attacker to execute arbitrary code with root privileges on the firewall.\n\nCloud NGFW, Panorama appliances, and Prisma Access are not impacted by this vulnerability."
},
...

$ go-cve-dictionary search cve CVE-2023-48783 CVE-2024-3400
{
"CVE-2023-48783": {
"CveID": "CVE-2023-48783",
"Nvds": [
{
"CveID": "CVE-2023-48783",
...
}
],
"Jvns": [],
"Fortinets": [
{
"AdvisoryID": "FG-IR-23-408",
"CveID": "CVE-2023-48783",
...
}
]
},
"CVE-2024-3400": {
"CveID": "CVE-2024-3400",
"Nvds": [
{
"CveID": "CVE-2024-3400",
...
```
#### Search by CPE
```bash
$ go-cve-dictionary search cpe "cpe:/a:fortinet:fortiportal"
[
{
"CveID": "CVE-2017-7337",
"Nvds": [],
"Jvns": [],
"Fortinets": [
{
"AdvisoryID": "FG-IR-17-114",
"CveID": "CVE-2017-7337",
"Title": "FortiPortal Multiple Vulnerabilities",
...
```
#### Search CVE IDs by CPE
```bash
$ go-cve-dictionary search cpe --cveid-only "cpe:/a:fortinet:fortiportal"
{
"Fortinet": [
"CVE-2023-46712",
"CVE-2023-48791",
"CVE-2017-7339",
"CVE-2017-7343",
"CVE-2022-27490",
"CVE-2017-7342",
"CVE-2017-7731",
"CVE-2023-41842",
"CVE-2023-48783",
"CVE-2024-21761",
"CVE-2017-7337",
"CVE-2017-7338",
"CVE-2017-7340"
],
"JVN": [],
"NVD": [
"CVE-2023-46712",
"CVE-2023-48791",
"CVE-2023-41842",
"CVE-2023-48783",
"CVE-2024-21761"
]
}
```
----
### Usage: Use MySQL as a DB storage back-end
- fetch nvd
Expand Down
203 changes: 203 additions & 0 deletions commands/search.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
package commands

import (
"encoding/json"
"os"

"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/vulsio/go-cve-dictionary/db"
log "github.com/vulsio/go-cve-dictionary/log"
"github.com/vulsio/go-cve-dictionary/models"
"golang.org/x/xerrors"
)

var searchCmd = &cobra.Command{
Use: "search",
Short: "Search for Vulnerability in the dictionary",
Long: "Search for Vulnerability in the dictionary",
}

var searchCVECmd = &cobra.Command{
Use: "cve",
Short: "Search for Vulnerability in the dictionary by CVEID",
Long: "Search for Vulnerability in the dictionary by CVEID",
RunE: searchCVE,
}

var searchCPECmd = &cobra.Command{
Use: "cpe",
Short: "Search for Vulnerability in the dictionary by CPE",
Long: "Search for Vulnerability in the dictionary by CPE",
Args: cobra.ExactArgs(1),
RunE: searchCPE,
}

func searchCVE(_ *cobra.Command, args []string) error {
if err := log.SetLogger(viper.GetBool("log-to-file"), viper.GetString("log-dir"), viper.GetBool("debug"), viper.GetBool("log-json")); err != nil {
return xerrors.Errorf("Failed to SetLogger. err: %w", err)
}

driver, err := db.NewDB(viper.GetString("dbtype"), viper.GetString("dbpath"), viper.GetBool("debug-sql"), db.Option{})
if err != nil {
if xerrors.Is(err, db.ErrDBLocked) {
return xerrors.Errorf("Failed to open DB. Close DB connection before fetching. err: %w", err)
}
return xerrors.Errorf("Failed to open DB. err: %w", err)
}

fetchMeta, err := driver.GetFetchMeta()
if err != nil {
return xerrors.Errorf("Failed to get FetchMeta from DB. err: %w", err)
}
if fetchMeta.OutDated() {
return xerrors.Errorf("Failed to start server. err: SchemaVersion is old. SchemaVersion: %+v", map[string]uint{"latest": models.LatestSchemaVersion, "DB": fetchMeta.SchemaVersion})
}

count := 0
nvdCount, err := driver.CountNvd()
if err != nil {
log.Errorf("Failed to count NVD table: %s", err)
return err
}
count += nvdCount

jvnCount, err := driver.CountJvn()
if err != nil {
log.Errorf("Failed to count JVN table: %s", err)
return err
}
count += jvnCount

fortinetCount, err := driver.CountFortinet()
if err != nil {
log.Errorf("Failed to count Fortinet table: %s", err)
return err
}
count += fortinetCount

if count == 0 {
log.Infof("No Vulnerability data found. Run the below command to fetch data from NVD, JVN, Fortinet")
log.Infof("")
log.Infof(" go-cve-dictionary fetch nvd")
log.Infof(" go-cve-dictionary fetch jvn")
log.Infof(" go-cve-dictionary fetch fortinet")
log.Infof("")
return nil
}

enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
switch len(args) {
case 0:
cveids, err := driver.GetCveIDs()
if err != nil {
return xerrors.Errorf("Failed to get All CVEIDs. err: %w", err)
}
if err := enc.Encode(cveids); err != nil {
return xerrors.Errorf("Failed to encode All CVEIDs. err: %w", err)
}
case 1:
d, err := driver.Get(args[0])
if err != nil {
return xerrors.Errorf("Failed to get CVEDetail by CVEID. err: %w", err)
}
if err := enc.Encode(d); err != nil {
return xerrors.Errorf("Failed to encode CVEDetail by CVEID. err: %w", err)
}
default:
ds, err := driver.GetMulti(args)
if err != nil {
return xerrors.Errorf("Failed to get CVEDetails by CVEIDs. err: %w", err)
}
if err := enc.Encode(ds); err != nil {
return xerrors.Errorf("Failed to encode CVEDetails by CVEIDs. err: %w", err)
}
}

return nil
}

func searchCPE(_ *cobra.Command, args []string) error {
if err := log.SetLogger(viper.GetBool("log-to-file"), viper.GetString("log-dir"), viper.GetBool("debug"), viper.GetBool("log-json")); err != nil {
return xerrors.Errorf("Failed to SetLogger. err: %w", err)
}

driver, err := db.NewDB(viper.GetString("dbtype"), viper.GetString("dbpath"), viper.GetBool("debug-sql"), db.Option{})
if err != nil {
if xerrors.Is(err, db.ErrDBLocked) {
return xerrors.Errorf("Failed to open DB. Close DB connection before fetching. err: %w", err)
}
return xerrors.Errorf("Failed to open DB. err: %w", err)
}

fetchMeta, err := driver.GetFetchMeta()
if err != nil {
return xerrors.Errorf("Failed to get FetchMeta from DB. err: %w", err)
}
if fetchMeta.OutDated() {
return xerrors.Errorf("Failed to start server. err: SchemaVersion is old. SchemaVersion: %+v", map[string]uint{"latest": models.LatestSchemaVersion, "DB": fetchMeta.SchemaVersion})
}

count := 0
nvdCount, err := driver.CountNvd()
if err != nil {
log.Errorf("Failed to count NVD table: %s", err)
return err
}
count += nvdCount

jvnCount, err := driver.CountJvn()
if err != nil {
log.Errorf("Failed to count JVN table: %s", err)
return err
}
count += jvnCount

fortinetCount, err := driver.CountFortinet()
if err != nil {
log.Errorf("Failed to count Fortinet table: %s", err)
return err
}
count += fortinetCount

if count == 0 {
log.Infof("No Vulnerability data found. Run the below command to fetch data from NVD, JVN, Fortinet")
log.Infof("")
log.Infof(" go-cve-dictionary fetch nvd")
log.Infof(" go-cve-dictionary fetch jvn")
log.Infof(" go-cve-dictionary fetch fortinet")
log.Infof("")
return nil
}

enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
if viper.GetBool("cveid-only") {
nvds, jvns, fortinets, err := driver.GetCveIDsByCpeURI(args[0])
if err != nil {
return xerrors.Errorf("Failed to Get CVEIDs by CPE URI. err: %w", err)
}
if err := enc.Encode(map[string][]string{"NVD": nvds, "JVN": jvns, "Fortinet": fortinets}); err != nil {
return xerrors.Errorf("Failed to encode CVEIDs by CPE URI. err: %w", err)
}
return nil
}
cveDetails, err := driver.GetByCpeURI(args[0])
if err != nil {
return xerrors.Errorf("Failed to Get CVEDetails by CPE URI. err: %w", err)
}
if err := enc.Encode(cveDetails); err != nil {
return xerrors.Errorf("Failed to encode CVEDetails by CPE URI. err: %w", err)
}

return nil
}

func init() {
RootCmd.AddCommand(searchCmd)
searchCmd.AddCommand(searchCVECmd, searchCPECmd)

searchCPECmd.PersistentFlags().Bool("cveid-only", false, "show only CVEID in search results")
_ = viper.BindPFlag("cveid-only", searchCPECmd.PersistentFlags().Lookup("cveid-only"))
}

0 comments on commit 304f4b0

Please sign in to comment.