refactor(nvd): use API instead of JSON feeds (#258)
Signed-off-by: knqyf263 <knqyf263@gmail.com> Co-authored-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
parent
4e31879ddb
commit
f1f4c3e8e6
@ -1,5 +1,5 @@
|
||||
run:
|
||||
go: 1.20
|
||||
go: 1.21
|
||||
timeout: 5m
|
||||
linters:
|
||||
enable:
|
||||
|
@ -2,7 +2,6 @@ package alpine_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
@ -118,10 +117,10 @@ func TestUpdater_Update(t *testing.T) {
|
||||
goldenPath, ok := tt.goldenFiles[path]
|
||||
require.True(t, ok, path)
|
||||
if *update {
|
||||
err = ioutil.WriteFile(goldenPath, actual, 0666)
|
||||
err = os.WriteFile(goldenPath, actual, 0666)
|
||||
require.NoError(t, err, goldenPath)
|
||||
}
|
||||
expected, err := ioutil.ReadFile(goldenPath)
|
||||
expected, err := os.ReadFile(goldenPath)
|
||||
assert.NoError(t, err, goldenPath)
|
||||
|
||||
assert.JSONEq(t, string(expected), string(actual), path)
|
||||
|
@ -2,7 +2,6 @@ package chainguard_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
@ -99,10 +98,10 @@ func TestUpdater_Update(t *testing.T) {
|
||||
goldenPath, ok := tt.goldenFiles[path]
|
||||
require.True(t, ok, path)
|
||||
if *update {
|
||||
err = ioutil.WriteFile(goldenPath, actual, 0666)
|
||||
err = os.WriteFile(goldenPath, actual, 0666)
|
||||
require.NoError(t, err, goldenPath)
|
||||
}
|
||||
expected, err := ioutil.ReadFile(goldenPath)
|
||||
expected, err := os.ReadFile(goldenPath)
|
||||
assert.NoError(t, err, goldenPath)
|
||||
|
||||
assert.JSONEq(t, string(expected), string(actual), path)
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -74,7 +74,7 @@ func (c CWEConfig) Update() error {
|
||||
}
|
||||
|
||||
func (c CWEConfig) saveFile(b []byte, fileType string) error {
|
||||
if err := ioutil.WriteFile(filepath.Join(c.cweDir, fileType), b, 0600); err != nil {
|
||||
if err := os.WriteFile(filepath.Join(c.cweDir, fileType), b, 0600); err != nil {
|
||||
return xerrors.Errorf("failed to write %s file: %w", fileType, err)
|
||||
}
|
||||
return nil
|
||||
@ -104,7 +104,7 @@ func readZipFile(zf *zip.File) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return ioutil.ReadAll(f)
|
||||
return io.ReadAll(f)
|
||||
}
|
||||
|
||||
func xmlToJSON(b []byte) (WeaknessCatalog, error) {
|
||||
|
@ -2,7 +2,6 @@ package cwe
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@ -57,7 +56,7 @@ func TestUpdate(t *testing.T) {
|
||||
cweURL = tc.cweServerUrl
|
||||
} else {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
b, _ := ioutil.ReadFile(tc.inputZipFile)
|
||||
b, _ := os.ReadFile(tc.inputZipFile)
|
||||
_, _ = io.WriteString(w, string(b))
|
||||
}))
|
||||
cweURL = ts.URL
|
||||
@ -66,11 +65,7 @@ func TestUpdate(t *testing.T) {
|
||||
}()
|
||||
}
|
||||
|
||||
dir, _ := ioutil.TempDir("", "TestUpdate-*")
|
||||
defer func() {
|
||||
_ = os.RemoveAll(dir)
|
||||
}()
|
||||
|
||||
dir := t.TempDir()
|
||||
c := NewCWEWithConfig(cweURL, filepath.Join(dir), 0)
|
||||
err := c.Update()
|
||||
switch {
|
||||
@ -78,10 +73,10 @@ func TestUpdate(t *testing.T) {
|
||||
require.Error(t, err, tc.name)
|
||||
default:
|
||||
// CWE-209.json is one file within good-small-cwe.xml.zip
|
||||
gotJSON, err := ioutil.ReadFile(filepath.Join(dir, "CWE-209.json"))
|
||||
gotJSON, err := os.ReadFile(filepath.Join(dir, "CWE-209.json"))
|
||||
require.NoError(t, err, tc.name)
|
||||
|
||||
wantJSON, _ := ioutil.ReadFile(tc.expectedOutputJSONFile)
|
||||
wantJSON, _ := os.ReadFile(tc.expectedOutputJSONFile)
|
||||
assert.JSONEq(t, string(wantJSON), string(gotJSON), tc.name)
|
||||
}
|
||||
})
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
@ -560,11 +559,11 @@ func TestConfig_Update(t *testing.T) {
|
||||
assert.True(t, ok, tc.name)
|
||||
|
||||
if *update {
|
||||
err = ioutil.WriteFile(goldenPath, actual, 0666)
|
||||
err = os.WriteFile(goldenPath, actual, 0666)
|
||||
assert.NoError(t, err, tc.name)
|
||||
}
|
||||
|
||||
expected, err := ioutil.ReadFile(goldenPath)
|
||||
expected, err := os.ReadFile(goldenPath)
|
||||
assert.NoError(t, err, tc.name)
|
||||
|
||||
assert.Equal(t, string(expected), string(actual), tc.name)
|
||||
|
@ -2,8 +2,6 @@ package glad
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@ -80,12 +78,11 @@ func TestUpdater_WalkDir(t *testing.T) {
|
||||
|
||||
goldenPath := filepath.Join(tc.goldenDir, relPath)
|
||||
if *update {
|
||||
fmt.Println(goldenPath)
|
||||
err = ioutil.WriteFile(goldenPath, got, 0666)
|
||||
err = os.WriteFile(goldenPath, got, 0666)
|
||||
assert.NoError(t, err, tc.name)
|
||||
}
|
||||
|
||||
want, err := ioutil.ReadFile(goldenPath)
|
||||
want, err := os.ReadFile(goldenPath)
|
||||
assert.NoError(t, err, goldenPath)
|
||||
|
||||
assert.JSONEq(t, string(want), string(got), tc.name)
|
||||
|
3
go.mod
3
go.mod
@ -1,6 +1,6 @@
|
||||
module github.com/aquasecurity/vuln-list-update
|
||||
|
||||
go 1.20
|
||||
go 1.21.5
|
||||
|
||||
require (
|
||||
github.com/PuerkitoBio/goquery v1.8.0
|
||||
@ -10,7 +10,6 @@ require (
|
||||
github.com/cheggaaa/pb/v3 v3.1.4
|
||||
github.com/hashicorp/go-getter v1.7.3
|
||||
github.com/kylelemons/godebug v1.1.0
|
||||
github.com/mattn/go-jsonpointer v0.0.1
|
||||
github.com/parnurzeal/gorequest v0.2.16
|
||||
github.com/shurcooL/githubv4 v0.0.0-20191127044304-8f68eb5628d0
|
||||
github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f
|
||||
|
2
go.sum
2
go.sum
@ -384,8 +384,6 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-jsonpointer v0.0.1 h1:j5m5P9BdP4B/zn6J7oH3KIQSOa2OHmcNAKEozUW7wuE=
|
||||
github.com/mattn/go-jsonpointer v0.0.1/go.mod h1:1s8vx7JSjlgVRF+LW16MPpWSRZAxyrc1/FYzOonxeao=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
|
5
main.go
5
main.go
@ -5,7 +5,6 @@ import (
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
githubql "github.com/shurcooL/githubv4"
|
||||
"golang.org/x/oauth2"
|
||||
@ -53,7 +52,6 @@ func main() {
|
||||
|
||||
func run() error {
|
||||
flag.Parse()
|
||||
now := time.Now().UTC()
|
||||
|
||||
if *vulnListDir != "" {
|
||||
utils.SetVulnListDir(*vulnListDir)
|
||||
@ -61,7 +59,8 @@ func run() error {
|
||||
|
||||
switch *target {
|
||||
case "nvd":
|
||||
if err := nvd.Update(now.Year()); err != nil {
|
||||
u := nvd.NewUpdater()
|
||||
if err := u.Update(); err != nil {
|
||||
return xerrors.Errorf("NVD update error: %w", err)
|
||||
}
|
||||
case "redhat":
|
||||
|
322
nvd/nvd.go
322
nvd/nvd.go
@ -1,162 +1,232 @@
|
||||
package nvd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
pb "github.com/cheggaaa/pb/v3"
|
||||
jsonpointer "github.com/mattn/go-jsonpointer"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/vuln-list-update/utils"
|
||||
)
|
||||
|
||||
type NVD struct {
|
||||
CVEItems []interface{} `json:"CVE_Items"`
|
||||
}
|
||||
|
||||
const (
|
||||
baseURL = "https://nvd.nist.gov/feeds/json/cve/1.1"
|
||||
feedDir = "feed"
|
||||
concurrency = 5
|
||||
wait = 0
|
||||
retry = 5
|
||||
retry = 50
|
||||
url20 = "https://services.nvd.nist.gov/rest/json/cves/2.0/"
|
||||
apiDir = "api"
|
||||
nvdTimeFormat = "2006-01-02T15:04:05"
|
||||
maxResultsPerPage = 2000
|
||||
retryAfter = 30 * time.Second
|
||||
apiKeyEnvName = "NVD_API_KEY"
|
||||
)
|
||||
|
||||
func Update(thisYear int) error {
|
||||
lastUpdatedDate, err := utils.GetLastUpdatedDate("nvd")
|
||||
if err != nil {
|
||||
return err
|
||||
type Option func(*Updater)
|
||||
|
||||
func WithLastModEndDate(lastModEndDate time.Time) Option {
|
||||
return func(u *Updater) {
|
||||
u.lastModEndDate = lastModEndDate
|
||||
}
|
||||
|
||||
var old bool
|
||||
var feeds []string
|
||||
for _, feed := range []string{"modified", "recent"} {
|
||||
lastModifiedDate, err := fetchLastModifiedDate(feed)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if lastUpdatedDate.After(lastModifiedDate) {
|
||||
continue
|
||||
}
|
||||
feeds = append(feeds, feed)
|
||||
|
||||
duration := lastModifiedDate.Sub(lastUpdatedDate)
|
||||
if duration > 24*time.Hour*7 {
|
||||
old = true
|
||||
}
|
||||
}
|
||||
|
||||
if old {
|
||||
// Fetch all years
|
||||
feeds = []string{}
|
||||
for year := 2002; year <= thisYear; year++ {
|
||||
feeds = append(feeds, fmt.Sprint(year))
|
||||
}
|
||||
}
|
||||
|
||||
feedCount := len(feeds)
|
||||
if feedCount == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
urls := make([]string, feedCount)
|
||||
for i, feed := range feeds {
|
||||
url := fmt.Sprintf("%s/nvdcve-1.1-%s.json.gz", baseURL, feed)
|
||||
urls[i] = url
|
||||
}
|
||||
|
||||
log.Println("Fetching NVD data...")
|
||||
responses, err := utils.FetchConcurrently(urls, concurrency, wait, retry)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to fetch concurrently: %w", err)
|
||||
}
|
||||
|
||||
log.Println("Saving NVD data...")
|
||||
bar := pb.StartNew(len(responses))
|
||||
for _, res := range responses {
|
||||
nvd, err := decode(res)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to decode NVD response: %w", err)
|
||||
}
|
||||
|
||||
if err := save(nvd); err != nil {
|
||||
return err
|
||||
}
|
||||
bar.Increment()
|
||||
}
|
||||
bar.Finish()
|
||||
return nil
|
||||
}
|
||||
|
||||
func save(nvd *NVD) error {
|
||||
for _, item := range nvd.CVEItems {
|
||||
v, err := jsonpointer.Get(item, "/cve/CVE_data_meta/ID")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
cveID, ok := v.(string)
|
||||
if !ok {
|
||||
log.Println("failed to type assertion")
|
||||
continue
|
||||
}
|
||||
|
||||
if err = utils.SaveCVEPerYear(filepath.Join(utils.VulnListDir(), feedDir), cveID, item); err != nil {
|
||||
return xerrors.Errorf("failed to save NVD CVE detail: %w", err)
|
||||
}
|
||||
func WithMaxResultsPerPage(maxResultsPerPage int) Option {
|
||||
return func(u *Updater) {
|
||||
u.maxResultsPerPage = maxResultsPerPage
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func fetchLastModifiedDate(feed string) (time.Time, error) {
|
||||
log.Printf("Fetching NVD metadata(%s)...\n", feed)
|
||||
func WithBaseURL(url string) Option {
|
||||
return func(u *Updater) {
|
||||
u.baseURL = url
|
||||
}
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/nvdcve-1.1-%s.meta", baseURL, feed)
|
||||
res, err := utils.FetchURL(url, "", 5)
|
||||
if err != nil {
|
||||
return time.Time{}, xerrors.Errorf("fetch error: %w", err)
|
||||
func WithRetry(retry int) Option {
|
||||
return func(u *Updater) {
|
||||
u.retry = retry
|
||||
}
|
||||
}
|
||||
|
||||
func WithRetryAfter(retryAfter time.Duration) Option {
|
||||
return func(u *Updater) {
|
||||
u.retryAfter = retryAfter
|
||||
}
|
||||
}
|
||||
|
||||
type Updater struct {
|
||||
baseURL string
|
||||
apiKey string
|
||||
maxResultsPerPage int
|
||||
retry int
|
||||
retryAfter time.Duration
|
||||
lastModEndDate time.Time // time.Now() by default
|
||||
}
|
||||
|
||||
func NewUpdater(opts ...Option) *Updater {
|
||||
u := &Updater{
|
||||
baseURL: url20,
|
||||
apiKey: os.Getenv(apiKeyEnvName),
|
||||
maxResultsPerPage: maxResultsPerPage,
|
||||
retry: retry,
|
||||
retryAfter: retryAfter,
|
||||
lastModEndDate: time.Now().UTC(),
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(res))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
s := strings.SplitN(line, ":", 2)
|
||||
if len(s) != 2 {
|
||||
continue
|
||||
}
|
||||
if s[0] == "lastModifiedDate" {
|
||||
t, err := time.Parse(time.RFC3339, s[1])
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
for _, opt := range opts {
|
||||
opt(u)
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
func (u Updater) Update() error {
|
||||
intervals, err := TimeIntervals(u.lastModEndDate)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("unable to build time intervals: %w", err)
|
||||
}
|
||||
|
||||
for _, interval := range intervals {
|
||||
slog.Info("Fetching NVD entries...", slog.String("start", interval.LastModStartDate),
|
||||
slog.String("end", interval.LastModEndDate))
|
||||
totalResults := 1 // Set a dummy value to start the loop
|
||||
for startIndex := 0; startIndex < totalResults; startIndex += u.maxResultsPerPage {
|
||||
if totalResults, err = u.saveEntry(interval, startIndex); err != nil {
|
||||
return xerrors.Errorf("unable to save entry CVEs for %q: %w", interval, err)
|
||||
}
|
||||
return t, nil
|
||||
slog.Info("Fetched NVD entries", slog.Int("total", totalResults), slog.Int("start_index", startIndex))
|
||||
}
|
||||
}
|
||||
return time.Unix(0, 0), nil
|
||||
|
||||
// Update last_updated.json at the end.
|
||||
if err = utils.SetLastUpdatedDate(apiDir, u.lastModEndDate); err != nil {
|
||||
return xerrors.Errorf("unable to update last_updated.json file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decode(b []byte) (*NVD, error) {
|
||||
zr, err := gzip.NewReader(bytes.NewBuffer(b))
|
||||
func (u Updater) saveEntry(interval TimeInterval, startIndex int) (int, error) {
|
||||
entryURL, err := urlWithParams(u.baseURL, startIndex, u.maxResultsPerPage, interval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return 0, xerrors.Errorf("unable to get url with query parameters: %w", err)
|
||||
}
|
||||
defer zr.Close()
|
||||
|
||||
nvd := &NVD{}
|
||||
err = json.NewDecoder(zr).Decode(nvd)
|
||||
entry, err := u.fetchEntry(entryURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return 0, xerrors.Errorf("unable to get entry for %q: %w", entryURL, err)
|
||||
}
|
||||
return nvd, nil
|
||||
for _, vuln := range entry.Vulnerabilities {
|
||||
if err := utils.SaveCVEPerYear(filepath.Join(utils.VulnListDir(), apiDir), vuln.Cve.ID, vuln.Cve); err != nil {
|
||||
return 0, xerrors.Errorf("unable to write %s: %w", vuln.Cve.ID, err)
|
||||
}
|
||||
}
|
||||
return entry.TotalResults, nil
|
||||
}
|
||||
|
||||
func (u Updater) fetchEntry(url string) (Entry, error) {
|
||||
var entry Entry
|
||||
r, err := u.fetchURL(url)
|
||||
if err != nil {
|
||||
return Entry{}, xerrors.Errorf("unable to fetch: %w", err)
|
||||
} else if r == nil {
|
||||
return Entry{}, xerrors.Errorf("unable to get entry from %q", url)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
if err = json.NewDecoder(r).Decode(&entry); err != nil {
|
||||
return Entry{}, xerrors.Errorf("unable to decode response for %q: %w", url, err)
|
||||
}
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
func (u Updater) fetchURL(url string) (io.ReadCloser, error) {
|
||||
var c http.Client
|
||||
for i := 0; i <= u.retry; i++ {
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("unable to build request for %q: %w", url, err)
|
||||
}
|
||||
if u.apiKey != "" {
|
||||
req.Header.Set("apiKey", u.apiKey)
|
||||
}
|
||||
|
||||
resp, err := c.Do(req)
|
||||
if err != nil {
|
||||
slog.Error("Response error. Try to get the entry again.", slog.String("error", err.Error()))
|
||||
continue
|
||||
}
|
||||
switch resp.StatusCode {
|
||||
case http.StatusForbidden:
|
||||
slog.Error("NVD rate limit. Wait to gain access.")
|
||||
// NVD limits:
|
||||
// Without API key: 5 requests / 30 seconds window
|
||||
// With API key: 50 requests / 30 seconds window
|
||||
time.Sleep(u.retryAfter)
|
||||
continue
|
||||
case http.StatusServiceUnavailable, http.StatusRequestTimeout, http.StatusBadGateway, http.StatusGatewayTimeout:
|
||||
slog.Error("NVD API is unstable. Try to fetch URL again.", slog.String("status_code", resp.Status))
|
||||
// NVD API works unstable
|
||||
time.Sleep(time.Duration(i) * time.Second)
|
||||
continue
|
||||
case http.StatusOK:
|
||||
return resp.Body, nil
|
||||
default:
|
||||
return nil, xerrors.Errorf("unexpected status code: %s", resp.Status)
|
||||
}
|
||||
|
||||
}
|
||||
return nil, xerrors.Errorf("unable to fetch url. Retry limit exceeded.")
|
||||
}
|
||||
|
||||
// TimeIntervals returns time intervals for NVD API
|
||||
// NVD API doesn't allow to get more than 120 days per request.
|
||||
// So we need to split the time range into intervals.
|
||||
func TimeIntervals(endTime time.Time) ([]TimeInterval, error) {
|
||||
lastUpdatedDate, err := utils.GetLastUpdatedDate(apiDir)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("unable to get lastUpdatedDate: %w", err)
|
||||
}
|
||||
var intervals []TimeInterval
|
||||
for endTime.Sub(lastUpdatedDate).Hours()/24 > 120 {
|
||||
newLastUpdatedDate := lastUpdatedDate.Add(120 * 24 * time.Hour)
|
||||
intervals = append(intervals, TimeInterval{
|
||||
LastModStartDate: lastUpdatedDate.Format(nvdTimeFormat),
|
||||
LastModEndDate: newLastUpdatedDate.Format(nvdTimeFormat),
|
||||
})
|
||||
lastUpdatedDate = newLastUpdatedDate
|
||||
}
|
||||
|
||||
// fill latest interval
|
||||
intervals = append(intervals, TimeInterval{
|
||||
LastModStartDate: lastUpdatedDate.Format(nvdTimeFormat),
|
||||
LastModEndDate: endTime.Format(nvdTimeFormat),
|
||||
})
|
||||
|
||||
return intervals, nil
|
||||
}
|
||||
|
||||
func urlWithParams(baseUrl string, startIndex, resultsPerPage int, interval TimeInterval) (string, error) {
|
||||
u, err := url.Parse(baseUrl)
|
||||
if err != nil {
|
||||
return "", xerrors.Errorf("unable to parse %q base url: %w", baseUrl, err)
|
||||
}
|
||||
q := u.Query()
|
||||
q.Set("lastModStartDate", interval.LastModStartDate)
|
||||
q.Set("lastModEndDate", interval.LastModEndDate)
|
||||
q.Set("startIndex", strconv.Itoa(startIndex))
|
||||
q.Set("resultsPerPage", strconv.Itoa(resultsPerPage))
|
||||
// NVD API doesn't work with escaped `:`
|
||||
// So we only need to escape `+` for `Z`:
|
||||
// https://nvd.nist.gov/developers/vulnerabilities:
|
||||
// `Please note, if a positive Z value is used (such as +01:00 for Central European Time) then the "+" should be encoded in the request as "%2B".`
|
||||
decoded, err := url.QueryUnescape(q.Encode())
|
||||
if err != nil {
|
||||
return "", xerrors.Errorf("unable to decode query params: %w", err)
|
||||
}
|
||||
u.RawQuery = decoded
|
||||
return u.String(), nil
|
||||
}
|
||||
|
265
nvd/nvd_test.go
Normal file
265
nvd/nvd_test.go
Normal file
@ -0,0 +1,265 @@
|
||||
package nvd_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/vuln-list-update/nvd"
|
||||
"github.com/aquasecurity/vuln-list-update/utils"
|
||||
)
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
maxResultsPerPage int
|
||||
retry int
|
||||
wantApiKey string
|
||||
respFiles map[string]string
|
||||
respStatus int
|
||||
lastUpdatedTime time.Time
|
||||
fakeTimeNow time.Time
|
||||
wantFiles []string
|
||||
wantError string
|
||||
}{
|
||||
{
|
||||
name: "happy path 1 page",
|
||||
maxResultsPerPage: 10,
|
||||
wantApiKey: "test_api_key",
|
||||
lastUpdatedTime: time.Date(2023, 11, 26, 0, 0, 0, 0, time.UTC),
|
||||
fakeTimeNow: time.Date(2023, 11, 28, 0, 0, 0, 0, time.UTC),
|
||||
respFiles: map[string]string{
|
||||
"resultsPerPage=1&startIndex=0": "testdata/fixtures/rootResp.json",
|
||||
"resultsPerPage=10&startIndex=0": "testdata/fixtures/respPageFull.json",
|
||||
},
|
||||
respStatus: 200,
|
||||
wantFiles: []string{
|
||||
filepath.Join("api", "2020", "CVE-2020-8167.json"),
|
||||
filepath.Join("api", "2021", "CVE-2021-22903.json"),
|
||||
filepath.Join("api", "2021", "CVE-2021-3881.json"),
|
||||
"last_updated.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path 1 page after reconnect",
|
||||
maxResultsPerPage: 10,
|
||||
wantApiKey: "test_api_key",
|
||||
retry: 1,
|
||||
lastUpdatedTime: time.Date(2023, 11, 26, 0, 0, 0, 0, time.UTC),
|
||||
fakeTimeNow: time.Date(2023, 11, 28, 0, 0, 0, 0, time.UTC),
|
||||
respFiles: map[string]string{
|
||||
"resultsPerPage=1&startIndex=0": "testdata/fixtures/rootResp.json",
|
||||
"resultsPerPage=10&startIndex=0": "testdata/fixtures/respPageFull.json",
|
||||
},
|
||||
respStatus: 403,
|
||||
wantFiles: []string{
|
||||
filepath.Join("api", "2020", "CVE-2020-8167.json"),
|
||||
filepath.Join("api", "2021", "CVE-2021-22903.json"),
|
||||
filepath.Join("api", "2021", "CVE-2021-3881.json"),
|
||||
"last_updated.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path 2 pages",
|
||||
maxResultsPerPage: 2,
|
||||
lastUpdatedTime: time.Date(2023, 11, 26, 0, 0, 0, 0, time.UTC),
|
||||
fakeTimeNow: time.Date(2023, 11, 28, 0, 0, 0, 0, time.UTC),
|
||||
respFiles: map[string]string{
|
||||
"resultsPerPage=1&startIndex=0": "testdata/fixtures/rootResp.json",
|
||||
"resultsPerPage=2&startIndex=0": "testdata/fixtures/respPage1.json",
|
||||
"resultsPerPage=2&startIndex=2": "testdata/fixtures/respPage2.json",
|
||||
},
|
||||
respStatus: 200,
|
||||
wantFiles: []string{
|
||||
filepath.Join("api", "2020", "CVE-2020-8167.json"),
|
||||
filepath.Join("api", "2021", "CVE-2021-22903.json"),
|
||||
filepath.Join("api", "2021", "CVE-2021-3881.json"),
|
||||
"last_updated.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "503 response",
|
||||
maxResultsPerPage: 10,
|
||||
wantApiKey: "test_api_key",
|
||||
lastUpdatedTime: time.Date(2023, 11, 26, 0, 0, 0, 0, time.UTC),
|
||||
fakeTimeNow: time.Date(2023, 11, 28, 0, 0, 0, 0, time.UTC),
|
||||
respStatus: 503,
|
||||
wantError: "unable to fetch url",
|
||||
},
|
||||
{
|
||||
name: "408 response",
|
||||
maxResultsPerPage: 10,
|
||||
wantApiKey: "test_api_key",
|
||||
lastUpdatedTime: time.Date(2023, 11, 26, 0, 0, 0, 0, time.UTC),
|
||||
fakeTimeNow: time.Date(2023, 11, 28, 0, 0, 0, 0, time.UTC),
|
||||
respStatus: 408,
|
||||
wantError: "unable to fetch url",
|
||||
},
|
||||
{
|
||||
name: "502 response",
|
||||
maxResultsPerPage: 10,
|
||||
wantApiKey: "test_api_key",
|
||||
lastUpdatedTime: time.Date(2023, 11, 26, 0, 0, 0, 0, time.UTC),
|
||||
fakeTimeNow: time.Date(2023, 11, 28, 0, 0, 0, 0, time.UTC),
|
||||
respStatus: 502,
|
||||
wantError: "unable to fetch url",
|
||||
},
|
||||
{
|
||||
name: "504 response",
|
||||
maxResultsPerPage: 10,
|
||||
wantApiKey: "test_api_key",
|
||||
lastUpdatedTime: time.Date(2023, 11, 26, 0, 0, 0, 0, time.UTC),
|
||||
fakeTimeNow: time.Date(2023, 11, 28, 0, 0, 0, 0, time.UTC),
|
||||
respStatus: 504,
|
||||
wantError: "unable to fetch url",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.wantApiKey != "" {
|
||||
t.Setenv("NVD_API_KEY", tt.wantApiKey)
|
||||
}
|
||||
|
||||
// overwrite vuln-list dir
|
||||
tmpDir := t.TempDir()
|
||||
savedVulnListDir := utils.VulnListDir()
|
||||
utils.SetVulnListDir(tmpDir)
|
||||
defer utils.SetVulnListDir(savedVulnListDir)
|
||||
|
||||
// create last_updated.json file into temp dir
|
||||
err := utils.SetLastUpdatedDate("api", tt.lastUpdatedTime)
|
||||
require.NoError(t, err)
|
||||
|
||||
respStatus := tt.respStatus
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", func(resp http.ResponseWriter, req *http.Request) {
|
||||
if respStatus != 200 {
|
||||
resp.WriteHeader(respStatus)
|
||||
// update respStatus update status after reconnection (if retry > 0)
|
||||
respStatus = 200
|
||||
return
|
||||
}
|
||||
|
||||
if tt.wantApiKey != "" {
|
||||
gotApiKey := req.Header.Get("apiKey")
|
||||
require.Equal(t, tt.wantApiKey, gotApiKey)
|
||||
}
|
||||
|
||||
var filePath string
|
||||
for params, path := range tt.respFiles {
|
||||
if strings.Contains(req.URL.String(), params) {
|
||||
filePath = path
|
||||
break
|
||||
}
|
||||
}
|
||||
if filePath == "" {
|
||||
t.Errorf("response files doesn't exist for %q", req.URL.String())
|
||||
}
|
||||
|
||||
b, err := os.ReadFile(filePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = resp.Write(b)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
ts := httptest.NewServer(mux)
|
||||
defer ts.Close()
|
||||
|
||||
u := nvd.NewUpdater(nvd.WithBaseURL(ts.URL), nvd.WithMaxResultsPerPage(tt.maxResultsPerPage),
|
||||
nvd.WithRetry(tt.retry), nvd.WithLastModEndDate(tt.fakeTimeNow), nvd.WithRetryAfter(1*time.Second))
|
||||
err = u.Update()
|
||||
if tt.wantError != "" {
|
||||
require.ErrorContains(t, err, tt.wantError)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
for _, wantFile := range tt.wantFiles {
|
||||
got, err := os.ReadFile(filepath.Join(tmpDir, wantFile))
|
||||
require.NoError(t, err)
|
||||
|
||||
want, err := os.ReadFile(filepath.Join("testdata", "golden", wantFile))
|
||||
require.NoError(t, err)
|
||||
|
||||
require.JSONEq(t, string(want), string(got))
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeIntervals(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
lastUpdatedTime time.Time
|
||||
fakeTimeNow time.Time
|
||||
wantIntervals []nvd.TimeInterval
|
||||
}{
|
||||
{
|
||||
name: "one interval",
|
||||
lastUpdatedTime: time.Date(2023, 11, 26, 0, 0, 0, 0, time.UTC),
|
||||
fakeTimeNow: time.Date(2023, 11, 28, 0, 0, 0, 0, time.UTC),
|
||||
wantIntervals: []nvd.TimeInterval{
|
||||
{
|
||||
LastModStartDate: "2023-11-26T00:00:00",
|
||||
LastModEndDate: "2023-11-28T00:00:00",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "two intervals",
|
||||
lastUpdatedTime: time.Date(2023, 5, 28, 0, 0, 0, 0, time.UTC),
|
||||
fakeTimeNow: time.Date(2023, 11, 28, 0, 0, 0, 0, time.UTC),
|
||||
wantIntervals: []nvd.TimeInterval{
|
||||
{
|
||||
LastModStartDate: "2023-05-28T00:00:00",
|
||||
LastModEndDate: "2023-09-25T00:00:00",
|
||||
},
|
||||
{
|
||||
LastModStartDate: "2023-09-25T00:00:00",
|
||||
LastModEndDate: "2023-11-28T00:00:00",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "last_updated.json file doesn't exist",
|
||||
lastUpdatedTime: time.Unix(0, 0),
|
||||
fakeTimeNow: time.Date(1970, 03, 01, 0, 0, 0, 0, time.UTC),
|
||||
wantIntervals: []nvd.TimeInterval{
|
||||
{
|
||||
LastModStartDate: "1970-01-01T00:00:00",
|
||||
LastModEndDate: "1970-03-01T00:00:00",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// overwrite vuln-list dir
|
||||
tmpDir := t.TempDir()
|
||||
savedVulnListDir := utils.VulnListDir()
|
||||
utils.SetVulnListDir(tmpDir)
|
||||
defer utils.SetVulnListDir(savedVulnListDir)
|
||||
|
||||
if tt.lastUpdatedTime != time.Unix(0, 0) {
|
||||
// create last_updated.json file into temp dir
|
||||
err := utils.SetLastUpdatedDate("api", tt.lastUpdatedTime)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
gotIntervals, err := nvd.TimeIntervals(tt.fakeTimeNow)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tt.wantIntervals, gotIntervals)
|
||||
})
|
||||
}
|
||||
}
|
3
nvd/testdata/fixtures/last_updated.json
vendored
Normal file
3
nvd/testdata/fixtures/last_updated.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"api": "2023-05-10T18:06:42.577411575Z"
|
||||
}
|
299
nvd/testdata/fixtures/respPage1.json
vendored
Normal file
299
nvd/testdata/fixtures/respPage1.json
vendored
Normal file
@ -0,0 +1,299 @@
|
||||
{
|
||||
"resultsPerPage": 2,
|
||||
"startIndex": 0,
|
||||
"totalResults": 3,
|
||||
"format": "NVD_CVE",
|
||||
"version": "2.0",
|
||||
"timestamp": "2023-11-27T04:36:56.737",
|
||||
"vulnerabilities": [
|
||||
{
|
||||
"cve": {
|
||||
"id": "CVE-2020-8167",
|
||||
"sourceIdentifier": "support@hackerone.com",
|
||||
"published": "2020-06-19T18:15:11.163",
|
||||
"lastModified": "2021-10-21T14:35:21.047",
|
||||
"vulnStatus": "Analyzed",
|
||||
"descriptions": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "A CSRF vulnerability exists in rails <= 6.0.3 rails-ujs module that could allow attackers to send CSRF tokens to wrong domains."
|
||||
},
|
||||
{
|
||||
"lang": "es",
|
||||
"value": "Se presenta una vulnerabilidad de tipo CSRF en el módulo rails versiones anteriores a 6.0.3 incluyéndola, rails-ujs que podría permitir a atacantes enviar tokens CSRF a dominios incorrectos"
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"cvssMetricV31": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"cvssData": {
|
||||
"version": "3.1",
|
||||
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N",
|
||||
"attackVector": "NETWORK",
|
||||
"attackComplexity": "LOW",
|
||||
"privilegesRequired": "NONE",
|
||||
"userInteraction": "REQUIRED",
|
||||
"scope": "UNCHANGED",
|
||||
"confidentialityImpact": "NONE",
|
||||
"integrityImpact": "HIGH",
|
||||
"availabilityImpact": "NONE",
|
||||
"baseScore": 6.5,
|
||||
"baseSeverity": "MEDIUM"
|
||||
},
|
||||
"exploitabilityScore": 2.8,
|
||||
"impactScore": 3.6
|
||||
}
|
||||
],
|
||||
"cvssMetricV2": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"cvssData": {
|
||||
"version": "2.0",
|
||||
"vectorString": "AV:N/AC:M/Au:N/C:N/I:P/A:N",
|
||||
"accessVector": "NETWORK",
|
||||
"accessComplexity": "MEDIUM",
|
||||
"authentication": "NONE",
|
||||
"confidentialityImpact": "NONE",
|
||||
"integrityImpact": "PARTIAL",
|
||||
"availabilityImpact": "NONE",
|
||||
"baseScore": 4.3
|
||||
},
|
||||
"baseSeverity": "MEDIUM",
|
||||
"exploitabilityScore": 8.6,
|
||||
"impactScore": 2.9,
|
||||
"acInsufInfo": false,
|
||||
"obtainAllPrivilege": false,
|
||||
"obtainUserPrivilege": false,
|
||||
"obtainOtherPrivilege": false,
|
||||
"userInteractionRequired": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"weaknesses": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "CWE-352"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "support@hackerone.com",
|
||||
"type": "Secondary",
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "CWE-352"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"configurations": [
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"operator": "OR",
|
||||
"negate": false,
|
||||
"cpeMatch": [
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:a:rubyonrails:rails:*:*:*:*:*:*:*:*",
|
||||
"versionEndExcluding": "5.2.4.3",
|
||||
"matchCriteriaId": "4357891D-A07C-4E1B-B540-92D6C477E7BB"
|
||||
},
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:a:rubyonrails:rails:*:*:*:*:*:*:*:*",
|
||||
"versionStartIncluding": "6.0.0",
|
||||
"versionEndExcluding": "6.0.3.1",
|
||||
"matchCriteriaId": "12B5617A-91AC-4B94-BE1A-057DBF322808"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"operator": "OR",
|
||||
"negate": false,
|
||||
"cpeMatch": [
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:o:debian:debian_linux:10.0:*:*:*:*:*:*:*",
|
||||
"matchCriteriaId": "07B237A9-69A3-4A9C-9DA0-4E06BD37AE73"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"url": "https://groups.google.com/g/rubyonrails-security/c/x9DixQDG9a0",
|
||||
"source": "support@hackerone.com",
|
||||
"tags": [
|
||||
"Mailing List",
|
||||
"Patch",
|
||||
"Third Party Advisory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "https://hackerone.com/reports/189878",
|
||||
"source": "support@hackerone.com",
|
||||
"tags": [
|
||||
"Exploit",
|
||||
"Third Party Advisory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "https://www.debian.org/security/2020/dsa-4766",
|
||||
"source": "support@hackerone.com",
|
||||
"tags": [
|
||||
"Third Party Advisory"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"cve": {
|
||||
"id": "CVE-2021-22903",
|
||||
"sourceIdentifier": "support@hackerone.com",
|
||||
"published": "2021-06-11T16:15:11.437",
|
||||
"lastModified": "2021-10-21T14:32:48.653",
|
||||
"vulnStatus": "Analyzed",
|
||||
"descriptions": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "The actionpack ruby gem before 6.1.3.2 suffers from a possible open redirect vulnerability. Specially crafted Host headers in combination with certain \"allowed host\" formats can cause the Host Authorization middleware in Action Pack to redirect users to a malicious website. This is similar to CVE-2021-22881. Strings in config.hosts that do not have a leading dot are converted to regular expressions without proper escaping. This causes, for example, `config.hosts << \"sub.example.com\"` to permit a request with a Host header value of `sub-example.com`."
|
||||
},
|
||||
{
|
||||
"lang": "es",
|
||||
"value": "El actionpack ruby gem versiones anteriores a 6.1.3.2, sufre una posible vulnerabilidad de redireccionamiento abierto. Las cabeceras de Host especialmente diseñadas en combinación con determinados formatos \"allowed host\" pueden hacer que el middleware Host Authorization de Action Pack redirija a usuarios hacia un sitio web malicioso. Esto es similar a CVE-2021-22881. Las cadenas en config.hosts que no tienen un punto inicial se convierten en expresiones regulares sin un escape apropiado. Esto hace que, por ejemplo, \"config.hosts (( \"sub.example.com\"\" permita una petición con un valor de cabecera Host de \"sub-example.com\""
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"cvssMetricV31": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"cvssData": {
|
||||
"version": "3.1",
|
||||
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N",
|
||||
"attackVector": "NETWORK",
|
||||
"attackComplexity": "LOW",
|
||||
"privilegesRequired": "NONE",
|
||||
"userInteraction": "REQUIRED",
|
||||
"scope": "CHANGED",
|
||||
"confidentialityImpact": "LOW",
|
||||
"integrityImpact": "LOW",
|
||||
"availabilityImpact": "NONE",
|
||||
"baseScore": 6.1,
|
||||
"baseSeverity": "MEDIUM"
|
||||
},
|
||||
"exploitabilityScore": 2.8,
|
||||
"impactScore": 2.7
|
||||
}
|
||||
],
|
||||
"cvssMetricV2": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"cvssData": {
|
||||
"version": "2.0",
|
||||
"vectorString": "AV:N/AC:M/Au:N/C:P/I:P/A:N",
|
||||
"accessVector": "NETWORK",
|
||||
"accessComplexity": "MEDIUM",
|
||||
"authentication": "NONE",
|
||||
"confidentialityImpact": "PARTIAL",
|
||||
"integrityImpact": "PARTIAL",
|
||||
"availabilityImpact": "NONE",
|
||||
"baseScore": 5.8
|
||||
},
|
||||
"baseSeverity": "MEDIUM",
|
||||
"exploitabilityScore": 8.6,
|
||||
"impactScore": 4.9,
|
||||
"acInsufInfo": false,
|
||||
"obtainAllPrivilege": false,
|
||||
"obtainUserPrivilege": false,
|
||||
"obtainOtherPrivilege": false,
|
||||
"userInteractionRequired": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"weaknesses": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "CWE-601"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "support@hackerone.com",
|
||||
"type": "Secondary",
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "CWE-601"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"configurations": [
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"operator": "OR",
|
||||
"negate": false,
|
||||
"cpeMatch": [
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:a:rubyonrails:rails:*:*:*:*:*:*:*:*",
|
||||
"versionStartIncluding": "6.1.1",
|
||||
"versionEndExcluding": "6.1.3.2",
|
||||
"matchCriteriaId": "3CAFC5D0-4073-430A-B9A1-5CF37A75EC7F"
|
||||
},
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:a:rubyonrails:rails:6.1.0:rc2:*:*:*:*:*:*",
|
||||
"matchCriteriaId": "B4431B78-31D7-4845-920B-238B355BF890"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"url": "https://discuss.rubyonrails.org/t/cve-2021-22903-possible-open-redirect-vulnerability-in-action-pack/77867",
|
||||
"source": "support@hackerone.com",
|
||||
"tags": [
|
||||
"Mitigation",
|
||||
"Patch",
|
||||
"Vendor Advisory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "https://hackerone.com/reports/1148025",
|
||||
"source": "support@hackerone.com",
|
||||
"tags": [
|
||||
"Permissions Required",
|
||||
"Third Party Advisory"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
158
nvd/testdata/fixtures/respPage2.json
vendored
Normal file
158
nvd/testdata/fixtures/respPage2.json
vendored
Normal file
@ -0,0 +1,158 @@
|
||||
{
|
||||
"resultsPerPage": 1,
|
||||
"startIndex": 3,
|
||||
"totalResults": 3,
|
||||
"format": "NVD_CVE",
|
||||
"version": "2.0",
|
||||
"timestamp": "2023-11-27T04:36:56.737",
|
||||
"vulnerabilities": [
|
||||
{
|
||||
"cve": {
|
||||
"id": "CVE-2021-3881",
|
||||
"sourceIdentifier": "security@huntr.dev",
|
||||
"published": "2021-10-15T14:15:07.907",
|
||||
"lastModified": "2021-10-22T12:29:28.390",
|
||||
"vulnStatus": "Analyzed",
|
||||
"descriptions": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "libmobi is vulnerable to Out-of-bounds Read"
|
||||
},
|
||||
{
|
||||
"lang": "es",
|
||||
"value": "libmobi es vulnerable a una lectura fuera de límites"
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"cvssMetricV31": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"cvssData": {
|
||||
"version": "3.1",
|
||||
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
|
||||
"attackVector": "NETWORK",
|
||||
"attackComplexity": "LOW",
|
||||
"privilegesRequired": "NONE",
|
||||
"userInteraction": "NONE",
|
||||
"scope": "UNCHANGED",
|
||||
"confidentialityImpact": "HIGH",
|
||||
"integrityImpact": "HIGH",
|
||||
"availabilityImpact": "HIGH",
|
||||
"baseScore": 9.8,
|
||||
"baseSeverity": "CRITICAL"
|
||||
},
|
||||
"exploitabilityScore": 3.9,
|
||||
"impactScore": 5.9
|
||||
}
|
||||
],
|
||||
"cvssMetricV30": [
|
||||
{
|
||||
"source": "security@huntr.dev",
|
||||
"type": "Secondary",
|
||||
"cvssData": {
|
||||
"version": "3.0",
|
||||
"vectorString": "CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:L",
|
||||
"attackVector": "NETWORK",
|
||||
"attackComplexity": "LOW",
|
||||
"privilegesRequired": "NONE",
|
||||
"userInteraction": "REQUIRED",
|
||||
"scope": "CHANGED",
|
||||
"confidentialityImpact": "LOW",
|
||||
"integrityImpact": "LOW",
|
||||
"availabilityImpact": "LOW",
|
||||
"baseScore": 7.1,
|
||||
"baseSeverity": "HIGH"
|
||||
},
|
||||
"exploitabilityScore": 2.8,
|
||||
"impactScore": 3.7
|
||||
}
|
||||
],
|
||||
"cvssMetricV2": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"cvssData": {
|
||||
"version": "2.0",
|
||||
"vectorString": "AV:N/AC:L/Au:N/C:P/I:P/A:P",
|
||||
"accessVector": "NETWORK",
|
||||
"accessComplexity": "LOW",
|
||||
"authentication": "NONE",
|
||||
"confidentialityImpact": "PARTIAL",
|
||||
"integrityImpact": "PARTIAL",
|
||||
"availabilityImpact": "PARTIAL",
|
||||
"baseScore": 7.5
|
||||
},
|
||||
"baseSeverity": "HIGH",
|
||||
"exploitabilityScore": 10,
|
||||
"impactScore": 6.4,
|
||||
"acInsufInfo": false,
|
||||
"obtainAllPrivilege": false,
|
||||
"obtainUserPrivilege": false,
|
||||
"obtainOtherPrivilege": false,
|
||||
"userInteractionRequired": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"weaknesses": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "CWE-125"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "security@huntr.dev",
|
||||
"type": "Secondary",
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "CWE-125"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"configurations": [
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"operator": "OR",
|
||||
"negate": false,
|
||||
"cpeMatch": [
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:a:libmobi_project:libmobi:*:*:*:*:*:*:*:*",
|
||||
"versionEndIncluding": "0.7",
|
||||
"matchCriteriaId": "D2333D42-14FA-48A8-873C-573E718CBD86"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"url": "https://github.com/bfabiszewski/libmobi/commit/bec783e6212439a335ba6e8df7ab8ed610ca9a21",
|
||||
"source": "security@huntr.dev",
|
||||
"tags": [
|
||||
"Patch",
|
||||
"Third Party Advisory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "https://huntr.dev/bounties/540fd115-7de4-4e19-a918-5ee61f5157c1",
|
||||
"source": "security@huntr.dev",
|
||||
"tags": [
|
||||
"Exploit",
|
||||
"Third Party Advisory"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
447
nvd/testdata/fixtures/respPageFull.json
vendored
Normal file
447
nvd/testdata/fixtures/respPageFull.json
vendored
Normal file
@ -0,0 +1,447 @@
|
||||
{
|
||||
"resultsPerPage": 1,
|
||||
"startIndex": 3,
|
||||
"totalResults": 3,
|
||||
"format": "NVD_CVE",
|
||||
"version": "2.0",
|
||||
"timestamp": "2023-11-27T04:36:56.737",
|
||||
"vulnerabilities": [
|
||||
{
|
||||
"cve": {
|
||||
"id": "CVE-2020-8167",
|
||||
"sourceIdentifier": "support@hackerone.com",
|
||||
"published": "2020-06-19T18:15:11.163",
|
||||
"lastModified": "2021-10-21T14:35:21.047",
|
||||
"vulnStatus": "Analyzed",
|
||||
"descriptions": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "A CSRF vulnerability exists in rails <= 6.0.3 rails-ujs module that could allow attackers to send CSRF tokens to wrong domains."
|
||||
},
|
||||
{
|
||||
"lang": "es",
|
||||
"value": "Se presenta una vulnerabilidad de tipo CSRF en el módulo rails versiones anteriores a 6.0.3 incluyéndola, rails-ujs que podría permitir a atacantes enviar tokens CSRF a dominios incorrectos"
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"cvssMetricV31": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"cvssData": {
|
||||
"version": "3.1",
|
||||
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N",
|
||||
"attackVector": "NETWORK",
|
||||
"attackComplexity": "LOW",
|
||||
"privilegesRequired": "NONE",
|
||||
"userInteraction": "REQUIRED",
|
||||
"scope": "UNCHANGED",
|
||||
"confidentialityImpact": "NONE",
|
||||
"integrityImpact": "HIGH",
|
||||
"availabilityImpact": "NONE",
|
||||
"baseScore": 6.5,
|
||||
"baseSeverity": "MEDIUM"
|
||||
},
|
||||
"exploitabilityScore": 2.8,
|
||||
"impactScore": 3.6
|
||||
}
|
||||
],
|
||||
"cvssMetricV2": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"cvssData": {
|
||||
"version": "2.0",
|
||||
"vectorString": "AV:N/AC:M/Au:N/C:N/I:P/A:N",
|
||||
"accessVector": "NETWORK",
|
||||
"accessComplexity": "MEDIUM",
|
||||
"authentication": "NONE",
|
||||
"confidentialityImpact": "NONE",
|
||||
"integrityImpact": "PARTIAL",
|
||||
"availabilityImpact": "NONE",
|
||||
"baseScore": 4.3
|
||||
},
|
||||
"baseSeverity": "MEDIUM",
|
||||
"exploitabilityScore": 8.6,
|
||||
"impactScore": 2.9,
|
||||
"acInsufInfo": false,
|
||||
"obtainAllPrivilege": false,
|
||||
"obtainUserPrivilege": false,
|
||||
"obtainOtherPrivilege": false,
|
||||
"userInteractionRequired": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"weaknesses": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "CWE-352"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "support@hackerone.com",
|
||||
"type": "Secondary",
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "CWE-352"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"configurations": [
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"operator": "OR",
|
||||
"negate": false,
|
||||
"cpeMatch": [
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:a:rubyonrails:rails:*:*:*:*:*:*:*:*",
|
||||
"versionEndExcluding": "5.2.4.3",
|
||||
"matchCriteriaId": "4357891D-A07C-4E1B-B540-92D6C477E7BB"
|
||||
},
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:a:rubyonrails:rails:*:*:*:*:*:*:*:*",
|
||||
"versionStartIncluding": "6.0.0",
|
||||
"versionEndExcluding": "6.0.3.1",
|
||||
"matchCriteriaId": "12B5617A-91AC-4B94-BE1A-057DBF322808"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"operator": "OR",
|
||||
"negate": false,
|
||||
"cpeMatch": [
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:o:debian:debian_linux:10.0:*:*:*:*:*:*:*",
|
||||
"matchCriteriaId": "07B237A9-69A3-4A9C-9DA0-4E06BD37AE73"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"url": "https://groups.google.com/g/rubyonrails-security/c/x9DixQDG9a0",
|
||||
"source": "support@hackerone.com",
|
||||
"tags": [
|
||||
"Mailing List",
|
||||
"Patch",
|
||||
"Third Party Advisory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "https://hackerone.com/reports/189878",
|
||||
"source": "support@hackerone.com",
|
||||
"tags": [
|
||||
"Exploit",
|
||||
"Third Party Advisory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "https://www.debian.org/security/2020/dsa-4766",
|
||||
"source": "support@hackerone.com",
|
||||
"tags": [
|
||||
"Third Party Advisory"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"cve": {
|
||||
"id": "CVE-2021-22903",
|
||||
"sourceIdentifier": "support@hackerone.com",
|
||||
"published": "2021-06-11T16:15:11.437",
|
||||
"lastModified": "2021-10-21T14:32:48.653",
|
||||
"vulnStatus": "Analyzed",
|
||||
"descriptions": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "The actionpack ruby gem before 6.1.3.2 suffers from a possible open redirect vulnerability. Specially crafted Host headers in combination with certain \"allowed host\" formats can cause the Host Authorization middleware in Action Pack to redirect users to a malicious website. This is similar to CVE-2021-22881. Strings in config.hosts that do not have a leading dot are converted to regular expressions without proper escaping. This causes, for example, `config.hosts << \"sub.example.com\"` to permit a request with a Host header value of `sub-example.com`."
|
||||
},
|
||||
{
|
||||
"lang": "es",
|
||||
"value": "El actionpack ruby gem versiones anteriores a 6.1.3.2, sufre una posible vulnerabilidad de redireccionamiento abierto. Las cabeceras de Host especialmente diseñadas en combinación con determinados formatos \"allowed host\" pueden hacer que el middleware Host Authorization de Action Pack redirija a usuarios hacia un sitio web malicioso. Esto es similar a CVE-2021-22881. Las cadenas en config.hosts que no tienen un punto inicial se convierten en expresiones regulares sin un escape apropiado. Esto hace que, por ejemplo, \"config.hosts (( \"sub.example.com\"\" permita una petición con un valor de cabecera Host de \"sub-example.com\""
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"cvssMetricV31": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"cvssData": {
|
||||
"version": "3.1",
|
||||
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N",
|
||||
"attackVector": "NETWORK",
|
||||
"attackComplexity": "LOW",
|
||||
"privilegesRequired": "NONE",
|
||||
"userInteraction": "REQUIRED",
|
||||
"scope": "CHANGED",
|
||||
"confidentialityImpact": "LOW",
|
||||
"integrityImpact": "LOW",
|
||||
"availabilityImpact": "NONE",
|
||||
"baseScore": 6.1,
|
||||
"baseSeverity": "MEDIUM"
|
||||
},
|
||||
"exploitabilityScore": 2.8,
|
||||
"impactScore": 2.7
|
||||
}
|
||||
],
|
||||
"cvssMetricV2": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"cvssData": {
|
||||
"version": "2.0",
|
||||
"vectorString": "AV:N/AC:M/Au:N/C:P/I:P/A:N",
|
||||
"accessVector": "NETWORK",
|
||||
"accessComplexity": "MEDIUM",
|
||||
"authentication": "NONE",
|
||||
"confidentialityImpact": "PARTIAL",
|
||||
"integrityImpact": "PARTIAL",
|
||||
"availabilityImpact": "NONE",
|
||||
"baseScore": 5.8
|
||||
},
|
||||
"baseSeverity": "MEDIUM",
|
||||
"exploitabilityScore": 8.6,
|
||||
"impactScore": 4.9,
|
||||
"acInsufInfo": false,
|
||||
"obtainAllPrivilege": false,
|
||||
"obtainUserPrivilege": false,
|
||||
"obtainOtherPrivilege": false,
|
||||
"userInteractionRequired": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"weaknesses": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "CWE-601"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "support@hackerone.com",
|
||||
"type": "Secondary",
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "CWE-601"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"configurations": [
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"operator": "OR",
|
||||
"negate": false,
|
||||
"cpeMatch": [
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:a:rubyonrails:rails:*:*:*:*:*:*:*:*",
|
||||
"versionStartIncluding": "6.1.1",
|
||||
"versionEndExcluding": "6.1.3.2",
|
||||
"matchCriteriaId": "3CAFC5D0-4073-430A-B9A1-5CF37A75EC7F"
|
||||
},
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:a:rubyonrails:rails:6.1.0:rc2:*:*:*:*:*:*",
|
||||
"matchCriteriaId": "B4431B78-31D7-4845-920B-238B355BF890"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"url": "https://discuss.rubyonrails.org/t/cve-2021-22903-possible-open-redirect-vulnerability-in-action-pack/77867",
|
||||
"source": "support@hackerone.com",
|
||||
"tags": [
|
||||
"Mitigation",
|
||||
"Patch",
|
||||
"Vendor Advisory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "https://hackerone.com/reports/1148025",
|
||||
"source": "support@hackerone.com",
|
||||
"tags": [
|
||||
"Permissions Required",
|
||||
"Third Party Advisory"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"cve": {
|
||||
"id": "CVE-2021-3881",
|
||||
"sourceIdentifier": "security@huntr.dev",
|
||||
"published": "2021-10-15T14:15:07.907",
|
||||
"lastModified": "2021-10-22T12:29:28.390",
|
||||
"vulnStatus": "Analyzed",
|
||||
"descriptions": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "libmobi is vulnerable to Out-of-bounds Read"
|
||||
},
|
||||
{
|
||||
"lang": "es",
|
||||
"value": "libmobi es vulnerable a una lectura fuera de límites"
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"cvssMetricV31": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"cvssData": {
|
||||
"version": "3.1",
|
||||
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
|
||||
"attackVector": "NETWORK",
|
||||
"attackComplexity": "LOW",
|
||||
"privilegesRequired": "NONE",
|
||||
"userInteraction": "NONE",
|
||||
"scope": "UNCHANGED",
|
||||
"confidentialityImpact": "HIGH",
|
||||
"integrityImpact": "HIGH",
|
||||
"availabilityImpact": "HIGH",
|
||||
"baseScore": 9.8,
|
||||
"baseSeverity": "CRITICAL"
|
||||
},
|
||||
"exploitabilityScore": 3.9,
|
||||
"impactScore": 5.9
|
||||
}
|
||||
],
|
||||
"cvssMetricV30": [
|
||||
{
|
||||
"source": "security@huntr.dev",
|
||||
"type": "Secondary",
|
||||
"cvssData": {
|
||||
"version": "3.0",
|
||||
"vectorString": "CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:L",
|
||||
"attackVector": "NETWORK",
|
||||
"attackComplexity": "LOW",
|
||||
"privilegesRequired": "NONE",
|
||||
"userInteraction": "REQUIRED",
|
||||
"scope": "CHANGED",
|
||||
"confidentialityImpact": "LOW",
|
||||
"integrityImpact": "LOW",
|
||||
"availabilityImpact": "LOW",
|
||||
"baseScore": 7.1,
|
||||
"baseSeverity": "HIGH"
|
||||
},
|
||||
"exploitabilityScore": 2.8,
|
||||
"impactScore": 3.7
|
||||
}
|
||||
],
|
||||
"cvssMetricV2": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"cvssData": {
|
||||
"version": "2.0",
|
||||
"vectorString": "AV:N/AC:L/Au:N/C:P/I:P/A:P",
|
||||
"accessVector": "NETWORK",
|
||||
"accessComplexity": "LOW",
|
||||
"authentication": "NONE",
|
||||
"confidentialityImpact": "PARTIAL",
|
||||
"integrityImpact": "PARTIAL",
|
||||
"availabilityImpact": "PARTIAL",
|
||||
"baseScore": 7.5
|
||||
},
|
||||
"baseSeverity": "HIGH",
|
||||
"exploitabilityScore": 10,
|
||||
"impactScore": 6.4,
|
||||
"acInsufInfo": false,
|
||||
"obtainAllPrivilege": false,
|
||||
"obtainUserPrivilege": false,
|
||||
"obtainOtherPrivilege": false,
|
||||
"userInteractionRequired": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"weaknesses": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "CWE-125"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "security@huntr.dev",
|
||||
"type": "Secondary",
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "CWE-125"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"configurations": [
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"operator": "OR",
|
||||
"negate": false,
|
||||
"cpeMatch": [
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:a:libmobi_project:libmobi:*:*:*:*:*:*:*:*",
|
||||
"versionEndIncluding": "0.7",
|
||||
"matchCriteriaId": "D2333D42-14FA-48A8-873C-573E718CBD86"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"url": "https://github.com/bfabiszewski/libmobi/commit/bec783e6212439a335ba6e8df7ab8ed610ca9a21",
|
||||
"source": "security@huntr.dev",
|
||||
"tags": [
|
||||
"Patch",
|
||||
"Third Party Advisory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "https://huntr.dev/bounties/540fd115-7de4-4e19-a918-5ee61f5157c1",
|
||||
"source": "security@huntr.dev",
|
||||
"tags": [
|
||||
"Exploit",
|
||||
"Third Party Advisory"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
166
nvd/testdata/fixtures/rootResp.json
vendored
Normal file
166
nvd/testdata/fixtures/rootResp.json
vendored
Normal file
@ -0,0 +1,166 @@
|
||||
{
|
||||
"resultsPerPage": 1,
|
||||
"startIndex": 0,
|
||||
"totalResults": 3,
|
||||
"format": "NVD_CVE",
|
||||
"version": "2.0",
|
||||
"timestamp": "2023-11-27T04:36:56.737",
|
||||
"vulnerabilities": [
|
||||
{
|
||||
"cve": {
|
||||
"id": "CVE-2020-8167",
|
||||
"sourceIdentifier": "support@hackerone.com",
|
||||
"published": "2020-06-19T18:15:11.163",
|
||||
"lastModified": "2021-10-21T14:35:21.047",
|
||||
"vulnStatus": "Analyzed",
|
||||
"descriptions": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "A CSRF vulnerability exists in rails <= 6.0.3 rails-ujs module that could allow attackers to send CSRF tokens to wrong domains."
|
||||
},
|
||||
{
|
||||
"lang": "es",
|
||||
"value": "Se presenta una vulnerabilidad de tipo CSRF en el módulo rails versiones anteriores a 6.0.3 incluyéndola, rails-ujs que podría permitir a atacantes enviar tokens CSRF a dominios incorrectos"
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"cvssMetricV31": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"cvssData": {
|
||||
"version": "3.1",
|
||||
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N",
|
||||
"attackVector": "NETWORK",
|
||||
"attackComplexity": "LOW",
|
||||
"privilegesRequired": "NONE",
|
||||
"userInteraction": "REQUIRED",
|
||||
"scope": "UNCHANGED",
|
||||
"confidentialityImpact": "NONE",
|
||||
"integrityImpact": "HIGH",
|
||||
"availabilityImpact": "NONE",
|
||||
"baseScore": 6.5,
|
||||
"baseSeverity": "MEDIUM"
|
||||
},
|
||||
"exploitabilityScore": 2.8,
|
||||
"impactScore": 3.6
|
||||
}
|
||||
],
|
||||
"cvssMetricV2": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"cvssData": {
|
||||
"version": "2.0",
|
||||
"vectorString": "AV:N/AC:M/Au:N/C:N/I:P/A:N",
|
||||
"accessVector": "NETWORK",
|
||||
"accessComplexity": "MEDIUM",
|
||||
"authentication": "NONE",
|
||||
"confidentialityImpact": "NONE",
|
||||
"integrityImpact": "PARTIAL",
|
||||
"availabilityImpact": "NONE",
|
||||
"baseScore": 4.3
|
||||
},
|
||||
"baseSeverity": "MEDIUM",
|
||||
"exploitabilityScore": 8.6,
|
||||
"impactScore": 2.9,
|
||||
"acInsufInfo": false,
|
||||
"obtainAllPrivilege": false,
|
||||
"obtainUserPrivilege": false,
|
||||
"obtainOtherPrivilege": false,
|
||||
"userInteractionRequired": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"weaknesses": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "CWE-352"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "support@hackerone.com",
|
||||
"type": "Secondary",
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "CWE-352"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"configurations": [
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"operator": "OR",
|
||||
"negate": false,
|
||||
"cpeMatch": [
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:a:rubyonrails:rails:*:*:*:*:*:*:*:*",
|
||||
"versionEndExcluding": "5.2.4.3",
|
||||
"matchCriteriaId": "4357891D-A07C-4E1B-B540-92D6C477E7BB"
|
||||
},
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:a:rubyonrails:rails:*:*:*:*:*:*:*:*",
|
||||
"versionStartIncluding": "6.0.0",
|
||||
"versionEndExcluding": "6.0.3.1",
|
||||
"matchCriteriaId": "12B5617A-91AC-4B94-BE1A-057DBF322808"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"operator": "OR",
|
||||
"negate": false,
|
||||
"cpeMatch": [
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:o:debian:debian_linux:10.0:*:*:*:*:*:*:*",
|
||||
"matchCriteriaId": "07B237A9-69A3-4A9C-9DA0-4E06BD37AE73"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"url": "https://groups.google.com/g/rubyonrails-security/c/x9DixQDG9a0",
|
||||
"source": "support@hackerone.com",
|
||||
"tags": [
|
||||
"Mailing List",
|
||||
"Patch",
|
||||
"Third Party Advisory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "https://hackerone.com/reports/189878",
|
||||
"source": "support@hackerone.com",
|
||||
"tags": [
|
||||
"Exploit",
|
||||
"Third Party Advisory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "https://www.debian.org/security/2020/dsa-4766",
|
||||
"source": "support@hackerone.com",
|
||||
"tags": [
|
||||
"Third Party Advisory"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
154
nvd/testdata/golden/api/2020/CVE-2020-8167.json
vendored
Normal file
154
nvd/testdata/golden/api/2020/CVE-2020-8167.json
vendored
Normal file
@ -0,0 +1,154 @@
|
||||
{
|
||||
"id": "CVE-2020-8167",
|
||||
"sourceIdentifier": "support@hackerone.com",
|
||||
"published": "2020-06-19T18:15:11.163",
|
||||
"lastModified": "2021-10-21T14:35:21.047",
|
||||
"vulnStatus": "Analyzed",
|
||||
"descriptions": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "A CSRF vulnerability exists in rails \u003c= 6.0.3 rails-ujs module that could allow attackers to send CSRF tokens to wrong domains."
|
||||
},
|
||||
{
|
||||
"lang": "es",
|
||||
"value": "Se presenta una vulnerabilidad de tipo CSRF en el módulo rails versiones anteriores a 6.0.3 incluyéndola, rails-ujs que podría permitir a atacantes enviar tokens CSRF a dominios incorrectos"
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"cvssMetricV31": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"cvssData": {
|
||||
"version": "3.1",
|
||||
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N",
|
||||
"attackVector": "NETWORK",
|
||||
"attackComplexity": "LOW",
|
||||
"privilegesRequired": "NONE",
|
||||
"userInteraction": "REQUIRED",
|
||||
"scope": "UNCHANGED",
|
||||
"confidentialityImpact": "NONE",
|
||||
"integrityImpact": "HIGH",
|
||||
"availabilityImpact": "NONE",
|
||||
"baseScore": 6.5,
|
||||
"baseSeverity": "MEDIUM"
|
||||
},
|
||||
"exploitabilityScore": 2.8,
|
||||
"impactScore": 3.6
|
||||
}
|
||||
],
|
||||
"cvssMetricV2": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"cvssData": {
|
||||
"version": "2.0",
|
||||
"vectorString": "AV:N/AC:M/Au:N/C:N/I:P/A:N",
|
||||
"accessVector": "NETWORK",
|
||||
"accessComplexity": "MEDIUM",
|
||||
"authentication": "NONE",
|
||||
"confidentialityImpact": "NONE",
|
||||
"integrityImpact": "PARTIAL",
|
||||
"availabilityImpact": "NONE",
|
||||
"baseScore": 4.3
|
||||
},
|
||||
"baseSeverity": "MEDIUM",
|
||||
"exploitabilityScore": 8.6,
|
||||
"impactScore": 2.9,
|
||||
"acInsufInfo": false,
|
||||
"obtainAllPrivilege": false,
|
||||
"obtainUserPrivilege": false,
|
||||
"obtainOtherPrivilege": false,
|
||||
"userInteractionRequired": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"weaknesses": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "CWE-352"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "support@hackerone.com",
|
||||
"type": "Secondary",
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "CWE-352"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"configurations": [
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"operator": "OR",
|
||||
"negate": false,
|
||||
"cpeMatch": [
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:a:rubyonrails:rails:*:*:*:*:*:*:*:*",
|
||||
"matchCriteriaId": "4357891D-A07C-4E1B-B540-92D6C477E7BB",
|
||||
"versionEndExcluding": "5.2.4.3"
|
||||
},
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:a:rubyonrails:rails:*:*:*:*:*:*:*:*",
|
||||
"matchCriteriaId": "12B5617A-91AC-4B94-BE1A-057DBF322808",
|
||||
"versionStartIncluding": "6.0.0",
|
||||
"versionEndExcluding": "6.0.3.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"operator": "OR",
|
||||
"negate": false,
|
||||
"cpeMatch": [
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:o:debian:debian_linux:10.0:*:*:*:*:*:*:*",
|
||||
"matchCriteriaId": "07B237A9-69A3-4A9C-9DA0-4E06BD37AE73"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"url": "https://groups.google.com/g/rubyonrails-security/c/x9DixQDG9a0",
|
||||
"source": "support@hackerone.com",
|
||||
"tags": [
|
||||
"Mailing List",
|
||||
"Patch",
|
||||
"Third Party Advisory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "https://hackerone.com/reports/189878",
|
||||
"source": "support@hackerone.com",
|
||||
"tags": [
|
||||
"Exploit",
|
||||
"Third Party Advisory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "https://www.debian.org/security/2020/dsa-4766",
|
||||
"source": "support@hackerone.com",
|
||||
"tags": [
|
||||
"Third Party Advisory"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
131
nvd/testdata/golden/api/2021/CVE-2021-22903.json
vendored
Normal file
131
nvd/testdata/golden/api/2021/CVE-2021-22903.json
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
{
|
||||
"id": "CVE-2021-22903",
|
||||
"sourceIdentifier": "support@hackerone.com",
|
||||
"published": "2021-06-11T16:15:11.437",
|
||||
"lastModified": "2021-10-21T14:32:48.653",
|
||||
"vulnStatus": "Analyzed",
|
||||
"descriptions": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "The actionpack ruby gem before 6.1.3.2 suffers from a possible open redirect vulnerability. Specially crafted Host headers in combination with certain \"allowed host\" formats can cause the Host Authorization middleware in Action Pack to redirect users to a malicious website. This is similar to CVE-2021-22881. Strings in config.hosts that do not have a leading dot are converted to regular expressions without proper escaping. This causes, for example, `config.hosts \u003c\u003c \"sub.example.com\"` to permit a request with a Host header value of `sub-example.com`."
|
||||
},
|
||||
{
|
||||
"lang": "es",
|
||||
"value": "El actionpack ruby gem versiones anteriores a 6.1.3.2, sufre una posible vulnerabilidad de redireccionamiento abierto. Las cabeceras de Host especialmente diseñadas en combinación con determinados formatos \"allowed host\" pueden hacer que el middleware Host Authorization de Action Pack redirija a usuarios hacia un sitio web malicioso. Esto es similar a CVE-2021-22881. Las cadenas en config.hosts que no tienen un punto inicial se convierten en expresiones regulares sin un escape apropiado. Esto hace que, por ejemplo, \"config.hosts (( \"sub.example.com\"\" permita una petición con un valor de cabecera Host de \"sub-example.com\""
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"cvssMetricV31": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"cvssData": {
|
||||
"version": "3.1",
|
||||
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N",
|
||||
"attackVector": "NETWORK",
|
||||
"attackComplexity": "LOW",
|
||||
"privilegesRequired": "NONE",
|
||||
"userInteraction": "REQUIRED",
|
||||
"scope": "CHANGED",
|
||||
"confidentialityImpact": "LOW",
|
||||
"integrityImpact": "LOW",
|
||||
"availabilityImpact": "NONE",
|
||||
"baseScore": 6.1,
|
||||
"baseSeverity": "MEDIUM"
|
||||
},
|
||||
"exploitabilityScore": 2.8,
|
||||
"impactScore": 2.7
|
||||
}
|
||||
],
|
||||
"cvssMetricV2": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"cvssData": {
|
||||
"version": "2.0",
|
||||
"vectorString": "AV:N/AC:M/Au:N/C:P/I:P/A:N",
|
||||
"accessVector": "NETWORK",
|
||||
"accessComplexity": "MEDIUM",
|
||||
"authentication": "NONE",
|
||||
"confidentialityImpact": "PARTIAL",
|
||||
"integrityImpact": "PARTIAL",
|
||||
"availabilityImpact": "NONE",
|
||||
"baseScore": 5.8
|
||||
},
|
||||
"baseSeverity": "MEDIUM",
|
||||
"exploitabilityScore": 8.6,
|
||||
"impactScore": 4.9,
|
||||
"acInsufInfo": false,
|
||||
"obtainAllPrivilege": false,
|
||||
"obtainUserPrivilege": false,
|
||||
"obtainOtherPrivilege": false,
|
||||
"userInteractionRequired": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"weaknesses": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "CWE-601"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "support@hackerone.com",
|
||||
"type": "Secondary",
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "CWE-601"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"configurations": [
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"operator": "OR",
|
||||
"negate": false,
|
||||
"cpeMatch": [
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:a:rubyonrails:rails:*:*:*:*:*:*:*:*",
|
||||
"matchCriteriaId": "3CAFC5D0-4073-430A-B9A1-5CF37A75EC7F",
|
||||
"versionStartIncluding": "6.1.1",
|
||||
"versionEndExcluding": "6.1.3.2"
|
||||
},
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:a:rubyonrails:rails:6.1.0:rc2:*:*:*:*:*:*",
|
||||
"matchCriteriaId": "B4431B78-31D7-4845-920B-238B355BF890"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"url": "https://discuss.rubyonrails.org/t/cve-2021-22903-possible-open-redirect-vulnerability-in-action-pack/77867",
|
||||
"source": "support@hackerone.com",
|
||||
"tags": [
|
||||
"Mitigation",
|
||||
"Patch",
|
||||
"Vendor Advisory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "https://hackerone.com/reports/1148025",
|
||||
"source": "support@hackerone.com",
|
||||
"tags": [
|
||||
"Permissions Required",
|
||||
"Third Party Advisory"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
146
nvd/testdata/golden/api/2021/CVE-2021-3881.json
vendored
Normal file
146
nvd/testdata/golden/api/2021/CVE-2021-3881.json
vendored
Normal file
@ -0,0 +1,146 @@
|
||||
{
|
||||
"id": "CVE-2021-3881",
|
||||
"sourceIdentifier": "security@huntr.dev",
|
||||
"published": "2021-10-15T14:15:07.907",
|
||||
"lastModified": "2021-10-22T12:29:28.390",
|
||||
"vulnStatus": "Analyzed",
|
||||
"descriptions": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "libmobi is vulnerable to Out-of-bounds Read"
|
||||
},
|
||||
{
|
||||
"lang": "es",
|
||||
"value": "libmobi es vulnerable a una lectura fuera de límites"
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"cvssMetricV31": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"cvssData": {
|
||||
"version": "3.1",
|
||||
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
|
||||
"attackVector": "NETWORK",
|
||||
"attackComplexity": "LOW",
|
||||
"privilegesRequired": "NONE",
|
||||
"userInteraction": "NONE",
|
||||
"scope": "UNCHANGED",
|
||||
"confidentialityImpact": "HIGH",
|
||||
"integrityImpact": "HIGH",
|
||||
"availabilityImpact": "HIGH",
|
||||
"baseScore": 9.8,
|
||||
"baseSeverity": "CRITICAL"
|
||||
},
|
||||
"exploitabilityScore": 3.9,
|
||||
"impactScore": 5.9
|
||||
}
|
||||
],
|
||||
"cvssMetricV30": [
|
||||
{
|
||||
"source": "security@huntr.dev",
|
||||
"type": "Secondary",
|
||||
"cvssData": {
|
||||
"version": "3.0",
|
||||
"vectorString": "CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:L",
|
||||
"attackVector": "NETWORK",
|
||||
"attackComplexity": "LOW",
|
||||
"privilegesRequired": "NONE",
|
||||
"userInteraction": "REQUIRED",
|
||||
"scope": "CHANGED",
|
||||
"confidentialityImpact": "LOW",
|
||||
"integrityImpact": "LOW",
|
||||
"availabilityImpact": "LOW",
|
||||
"baseScore": 7.1,
|
||||
"baseSeverity": "HIGH"
|
||||
},
|
||||
"exploitabilityScore": 2.8,
|
||||
"impactScore": 3.7
|
||||
}
|
||||
],
|
||||
"cvssMetricV2": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"cvssData": {
|
||||
"version": "2.0",
|
||||
"vectorString": "AV:N/AC:L/Au:N/C:P/I:P/A:P",
|
||||
"accessVector": "NETWORK",
|
||||
"accessComplexity": "LOW",
|
||||
"authentication": "NONE",
|
||||
"confidentialityImpact": "PARTIAL",
|
||||
"integrityImpact": "PARTIAL",
|
||||
"availabilityImpact": "PARTIAL",
|
||||
"baseScore": 7.5
|
||||
},
|
||||
"baseSeverity": "HIGH",
|
||||
"exploitabilityScore": 10,
|
||||
"impactScore": 6.4,
|
||||
"acInsufInfo": false,
|
||||
"obtainAllPrivilege": false,
|
||||
"obtainUserPrivilege": false,
|
||||
"obtainOtherPrivilege": false,
|
||||
"userInteractionRequired": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"weaknesses": [
|
||||
{
|
||||
"source": "nvd@nist.gov",
|
||||
"type": "Primary",
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "CWE-125"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "security@huntr.dev",
|
||||
"type": "Secondary",
|
||||
"description": [
|
||||
{
|
||||
"lang": "en",
|
||||
"value": "CWE-125"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"configurations": [
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"operator": "OR",
|
||||
"negate": false,
|
||||
"cpeMatch": [
|
||||
{
|
||||
"vulnerable": true,
|
||||
"criteria": "cpe:2.3:a:libmobi_project:libmobi:*:*:*:*:*:*:*:*",
|
||||
"matchCriteriaId": "D2333D42-14FA-48A8-873C-573E718CBD86",
|
||||
"versionEndIncluding": "0.7"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"url": "https://github.com/bfabiszewski/libmobi/commit/bec783e6212439a335ba6e8df7ab8ed610ca9a21",
|
||||
"source": "security@huntr.dev",
|
||||
"tags": [
|
||||
"Patch",
|
||||
"Third Party Advisory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "https://huntr.dev/bounties/540fd115-7de4-4e19-a918-5ee61f5157c1",
|
||||
"source": "security@huntr.dev",
|
||||
"tags": [
|
||||
"Exploit",
|
||||
"Third Party Advisory"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
3
nvd/testdata/golden/last_updated.json
vendored
Normal file
3
nvd/testdata/golden/last_updated.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"api":"2023-11-28T00:00:00Z"
|
||||
}
|
175
nvd/types.go
Normal file
175
nvd/types.go
Normal file
@ -0,0 +1,175 @@
|
||||
package nvd
|
||||
|
||||
// Entry is based on https://csrc.nist.gov/schema/nvd/api/2.0/cve_api_json_2.0.schema
|
||||
type Entry struct {
|
||||
ResultsPerPage int `json:"resultsPerPage"`
|
||||
StartIndex int `json:"startIndex"`
|
||||
TotalResults int `json:"totalResults"`
|
||||
Format string `json:"format"`
|
||||
Version string `json:"version"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
Vulnerabilities []Vulnerability `json:"vulnerabilities"`
|
||||
}
|
||||
|
||||
type Vulnerability struct {
|
||||
Cve Cve
|
||||
}
|
||||
|
||||
type Cve struct {
|
||||
ID string `json:"id"`
|
||||
SourceIdentifier string `json:"sourceIdentifier,omitempty"`
|
||||
Published string `json:"published"`
|
||||
LastModified string `json:"lastModified"`
|
||||
VulnStatus string `json:"vulnStatus,omitempty"`
|
||||
EvaluatorComment string `json:"evaluatorComment,omitempty"`
|
||||
EvaluatorSolution string `json:"evaluatorSolution,omitempty"`
|
||||
EvaluatorImpact string `json:"evaluatorImpact,omitempty"`
|
||||
CisaExploitAdd string `json:"cisaExploitAdd,omitempty"`
|
||||
CisaActionDue string `json:"cisaActionDue,omitempty"`
|
||||
Descriptions []LangString `json:"descriptions"`
|
||||
Metrics Metrics `json:"metrics,omitempty"`
|
||||
Weaknesses []Weakness `json:"weaknesses,omitempty"`
|
||||
Configurations []Configuration `json:"configurations,omitempty"`
|
||||
References []Reference `json:"references"`
|
||||
VendorComments []VendorComment `json:"vendorComments,omitempty"`
|
||||
}
|
||||
|
||||
type LangString struct {
|
||||
Lang string `json:"lang"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type Reference struct {
|
||||
URL string `json:"url"`
|
||||
Source string `json:"source,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
type Metrics struct {
|
||||
CvssMetricV31 []CvssMetricV3 `json:"cvssMetricV31,omitempty"`
|
||||
CvssMetricV30 []CvssMetricV3 `json:"cvssMetricV30,omitempty"`
|
||||
CvssMetricV2 []CvssMetricV2 `json:"cvssMetricV2,omitempty"`
|
||||
}
|
||||
|
||||
// CvssMetricV3 is based on https://csrc.nist.gov/schema/nvd/api/2.0/cve_api_json_2.0.schema.
|
||||
// v3.0 and v3.1 have only one difference: `cvssData`.
|
||||
// But we can use `cvssData` v3.0 for v3.1 (see below).
|
||||
// So we can use the same structure for v3.0 and v3.1.
|
||||
type CvssMetricV3 struct {
|
||||
Source string `json:"source"`
|
||||
Type string `json:"type"`
|
||||
CvssData CvssDataV30 `json:"cvssData"`
|
||||
ExploitabilityScore float64 `json:"exploitabilityScore,omitempty"`
|
||||
ImpactScore float64 `json:"impactScore,omitempty"`
|
||||
}
|
||||
|
||||
// CvssDataV30 is based on https://csrc.nist.gov/schema/nvd/api/2.0/external/cvss-v3.0.json
|
||||
// v3.0 and v3.1 have only one difference: `patterns` for `vectorString`.
|
||||
// So we can use version 3.0 for version 3.1.
|
||||
type CvssDataV30 struct {
|
||||
Version string `json:"version"`
|
||||
VectorString string `json:"vectorString"`
|
||||
AttackVector string `json:"attackVector,omitempty"`
|
||||
AttackComplexity string `json:"attackComplexity,omitempty"`
|
||||
PrivilegesRequired string `json:"privilegesRequired,omitempty"`
|
||||
UserInteraction string `json:"userInteraction,omitempty"`
|
||||
Scope string `json:"scope,omitempty"`
|
||||
ConfidentialityImpact string `json:"confidentialityImpact,omitempty"`
|
||||
IntegrityImpact string `json:"integrityImpact,omitempty"`
|
||||
AvailabilityImpact string `json:"availabilityImpact,omitempty"`
|
||||
BaseScore float64 `json:"baseScore"`
|
||||
BaseSeverity string `json:"baseSeverity"`
|
||||
ExploitCodeMaturity string `json:"exploitCodeMaturity,omitempty"`
|
||||
RemediationLevel string `json:"remediationLevel,omitempty"`
|
||||
ReportConfidence string `json:"reportConfidence,omitempty"`
|
||||
TemporalScore float64 `json:"temporalScore,omitempty"`
|
||||
TemporalSeverity string `json:"temporalSeverity,omitempty"`
|
||||
ConfidentialityRequirement string `json:"confidentialityRequirement,omitempty"`
|
||||
IntegrityRequirement string `json:"integrityRequirement,omitempty"`
|
||||
AvailabilityRequirement string `json:"availabilityRequirement,omitempty"`
|
||||
ModifiedAttackVector string `json:"modifiedAttackVector,omitempty"`
|
||||
ModifiedAttackComplexity string `json:"modifiedAttackComplexity,omitempty"`
|
||||
ModifiedPrivilegesRequired string `json:"modifiedPrivilegesRequired,omitempty"`
|
||||
ModifiedUserInteraction string `json:"modifiedUserInteraction,omitempty"`
|
||||
ModifiedScope string `json:"modifiedScope,omitempty"`
|
||||
ModifiedConfidentialityImpact string `json:"modifiedConfidentialityImpact,omitempty"`
|
||||
ModifiedIntegrityImpact string `json:"modifiedIntegrityImpact,omitempty"`
|
||||
ModifiedAvailabilityImpact string `json:"modifiedAvailabilityImpact,omitempty"`
|
||||
EnvironmentalScore float64 `json:"environmentalScore,omitempty"`
|
||||
EnvironmentalSeverity string `json:"environmentalSeverity,omitempty"`
|
||||
}
|
||||
|
||||
type CvssMetricV2 struct {
|
||||
Source string `json:"source"`
|
||||
Type string `json:"type"`
|
||||
CvssData CvssDataV20 `json:"cvssData"`
|
||||
BaseSeverity string `json:"baseSeverity,omitempty"`
|
||||
ExploitabilityScore float64 `json:"exploitabilityScore,omitempty"`
|
||||
ImpactScore float64 `json:"impactScore,omitempty"`
|
||||
AcInsufInfo bool `json:"acInsufInfo"`
|
||||
ObtainAllPrivilege bool `json:"obtainAllPrivilege"`
|
||||
ObtainUserPrivilege bool `json:"obtainUserPrivilege"`
|
||||
ObtainOtherPrivilege bool `json:"obtainOtherPrivilege"`
|
||||
UserInteractionRequired bool `json:"userInteractionRequired"`
|
||||
}
|
||||
|
||||
// CvssDataV20 is based on https://csrc.nist.gov/schema/nvd/api/2.0/external/cvss-v2.0.json
|
||||
type CvssDataV20 struct {
|
||||
Version string `json:"version"`
|
||||
VectorString string `json:"vectorString"`
|
||||
AccessVector string `json:"accessVector,omitempty"`
|
||||
AccessComplexity string `json:"accessComplexity,omitempty"`
|
||||
Authentication string `json:"authentication,omitempty"`
|
||||
ConfidentialityImpact string `json:"confidentialityImpact,omitempty"`
|
||||
IntegrityImpact string `json:"integrityImpact,omitempty"`
|
||||
AvailabilityImpact string `json:"availabilityImpact,omitempty"`
|
||||
BaseScore float64 `json:"baseScore"`
|
||||
Exploitability string `json:"exploitability,omitempty"`
|
||||
RemediationLevel string `json:"remediationLevel,omitempty"`
|
||||
ReportConfidence string `json:"reportConfidence,omitempty"`
|
||||
TemporalScore float64 `json:"temporalScore,omitempty"`
|
||||
CollateralDamagePotential string `json:"collateralDamagePotential,omitempty"`
|
||||
TargetDistribution string `json:"targetDistribution,omitempty"`
|
||||
ConfidentialityRequirement string `json:"confidentialityRequirement,omitempty"`
|
||||
IntegrityRequirement string `json:"integrityRequirement,omitempty"`
|
||||
AvailabilityRequirement string `json:"availabilityRequirement,omitempty"`
|
||||
EnvironmentalScore float64 `json:"environmentalScore,omitempty"`
|
||||
}
|
||||
|
||||
type Weakness struct {
|
||||
Source string `json:"source"`
|
||||
Type string `json:"type"`
|
||||
Description []LangString `json:"description"`
|
||||
}
|
||||
|
||||
type Configuration struct {
|
||||
Operator string `json:"operator,omitempty"`
|
||||
Negate bool `json:"negate,omitempty"`
|
||||
Nodes []Node `json:"nodes"`
|
||||
}
|
||||
|
||||
type Node struct {
|
||||
Operator string `json:"operator"`
|
||||
Negate bool `json:"negate"`
|
||||
CpeMatch []CpeMatch `json:"cpeMatch"`
|
||||
}
|
||||
type CpeMatch struct {
|
||||
Vulnerable bool `json:"vulnerable"`
|
||||
Criteria string `json:"criteria"`
|
||||
MatchCriteriaID string `json:"matchCriteriaId"`
|
||||
VersionStartExcluding string `json:"versionStartExcluding,omitempty"`
|
||||
VersionStartIncluding string `json:"versionStartIncluding,omitempty"`
|
||||
VersionEndExcluding string `json:"versionEndExcluding,omitempty"`
|
||||
VersionEndIncluding string `json:"versionEndIncluding,omitempty"`
|
||||
}
|
||||
|
||||
type VendorComment struct {
|
||||
Organization string `json:"organization"`
|
||||
Comment string `json:"comment"`
|
||||
LastModified string `json:"lastModified"`
|
||||
}
|
||||
|
||||
type TimeInterval struct {
|
||||
LastModStartDate string
|
||||
LastModEndDate string
|
||||
}
|
@ -2,7 +2,6 @@ package oval_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@ -120,7 +119,7 @@ func TestConfig_Update(t *testing.T) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
b, err := ioutil.ReadFile(filePath)
|
||||
b, err := os.ReadFile(filePath)
|
||||
assert.NoError(t, err, tc.name)
|
||||
_, err = w.Write(b)
|
||||
assert.NoError(t, err, tc.name)
|
||||
@ -159,11 +158,11 @@ func TestConfig_Update(t *testing.T) {
|
||||
assert.True(t, ok, tc.name)
|
||||
|
||||
if *update {
|
||||
err = ioutil.WriteFile(goldenPath, actual, 0666)
|
||||
err = os.WriteFile(goldenPath, actual, 0666)
|
||||
assert.NoError(t, err, tc.name)
|
||||
}
|
||||
|
||||
expected, err := ioutil.ReadFile(goldenPath)
|
||||
expected, err := os.ReadFile(goldenPath)
|
||||
assert.NoError(t, err, tc.name)
|
||||
|
||||
assert.Equal(t, expected, actual, tc.name)
|
||||
|
@ -2,7 +2,7 @@ package oval_test
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/kylelemons/godebug/pretty"
|
||||
@ -178,7 +178,7 @@ func TestRedhatCVEJSON_UnmarshalJSON(t *testing.T) {
|
||||
}
|
||||
for testname, tt := range tests {
|
||||
t.Run(testname, func(t *testing.T) {
|
||||
xmlByte, err := ioutil.ReadFile(tt.in)
|
||||
xmlByte, err := os.ReadFile(tt.in)
|
||||
if err != nil {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package photon_test
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@ -139,7 +138,7 @@ func TestConfig_Update(t *testing.T) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
b, err := ioutil.ReadFile(filePath)
|
||||
b, err := os.ReadFile(filePath)
|
||||
assert.NoError(t, err, tc.name)
|
||||
_, err = w.Write(b)
|
||||
assert.NoError(t, err, tc.name)
|
||||
@ -182,11 +181,11 @@ func TestConfig_Update(t *testing.T) {
|
||||
assert.True(t, ok, tc.name)
|
||||
|
||||
if *update {
|
||||
err = ioutil.WriteFile(goldenPath, actual, 0666)
|
||||
err = os.WriteFile(goldenPath, actual, 0666)
|
||||
assert.NoError(t, err, tc.name)
|
||||
}
|
||||
|
||||
expected, err := ioutil.ReadFile(goldenPath)
|
||||
expected, err := os.ReadFile(goldenPath)
|
||||
assert.NoError(t, err, tc.name)
|
||||
|
||||
assert.Equal(t, expected, actual, tc.name)
|
||||
|
@ -2,7 +2,7 @@ package securitydataapi_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
@ -117,7 +117,7 @@ func TestRedhatCVEJSON_UnmarshalJSON(t *testing.T) {
|
||||
}
|
||||
for testname, tt := range tests {
|
||||
t.Run(testname, func(t *testing.T) {
|
||||
jsonByte, err := ioutil.ReadFile(tt.in)
|
||||
jsonByte, err := os.ReadFile(tt.in)
|
||||
if err != nil {
|
||||
t.Fatalf("unknown error: %s", err)
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package cvrf_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@ -107,7 +106,7 @@ func TestConfig_Update(t *testing.T) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
b, err := ioutil.ReadFile(filePath)
|
||||
b, err := os.ReadFile(filePath)
|
||||
assert.NoError(t, err, tc.name)
|
||||
_, err = w.Write(b)
|
||||
assert.NoError(t, err, tc.name)
|
||||
@ -146,10 +145,10 @@ func TestConfig_Update(t *testing.T) {
|
||||
goldenPath, ok := tc.goldenFiles[path]
|
||||
assert.True(t, ok, tc.name)
|
||||
if *update {
|
||||
err = ioutil.WriteFile(goldenPath, actual, 0666)
|
||||
err = os.WriteFile(goldenPath, actual, 0666)
|
||||
assert.NoError(t, err, tc.name)
|
||||
}
|
||||
expected, err := ioutil.ReadFile(goldenPath)
|
||||
expected, err := os.ReadFile(goldenPath)
|
||||
assert.NoError(t, err, tc.name)
|
||||
|
||||
assert.Equal(t, string(expected), string(actual), tc.name)
|
||||
|
@ -2,7 +2,6 @@ package ubuntu
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -147,7 +146,7 @@ func parse(r io.Reader) (vuln *Vulnerability, err error) {
|
||||
vuln.Patches = map[Package]Statuses{}
|
||||
vuln.UpstreamLinks = map[Package][]string{}
|
||||
|
||||
all, err := ioutil.ReadAll(r)
|
||||
all, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
@ -28,13 +27,14 @@ func GetLastUpdatedDate(dist string) (time.Time, error) {
|
||||
|
||||
t, ok := lastUpdated[dist]
|
||||
if !ok {
|
||||
return time.Unix(0, 0), nil
|
||||
return time.Unix(0, 0).UTC(), nil
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func getLastUpdatedDate() (map[string]time.Time, error) {
|
||||
lastUpdatedFilePath = filepath.Join(VulnListDir(), lastUpdatedFile) // update path if VulnListDir has been changed.
|
||||
lastUpdated := LastUpdated{}
|
||||
if _, err := os.Stat(lastUpdatedFilePath); os.IsNotExist(err) {
|
||||
return lastUpdated, nil
|
||||
@ -63,7 +63,7 @@ func SetLastUpdatedDate(dist string, lastUpdatedDate time.Time) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ioutil.WriteFile(lastUpdatedFilePath, b, 0600); err != nil {
|
||||
if err = os.WriteFile(lastUpdatedFilePath, b, 0600); err != nil {
|
||||
return xerrors.Errorf("failed to write last updated date: %w", err)
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@ package wolfi_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
@ -99,10 +98,10 @@ func TestUpdater_Update(t *testing.T) {
|
||||
goldenPath, ok := tt.goldenFiles[path]
|
||||
require.True(t, ok, path)
|
||||
if *update {
|
||||
err = ioutil.WriteFile(goldenPath, actual, 0666)
|
||||
err = os.WriteFile(goldenPath, actual, 0666)
|
||||
require.NoError(t, err, goldenPath)
|
||||
}
|
||||
expected, err := ioutil.ReadFile(goldenPath)
|
||||
expected, err := os.ReadFile(goldenPath)
|
||||
assert.NoError(t, err, goldenPath)
|
||||
|
||||
assert.JSONEq(t, string(expected), string(actual), path)
|
||||
|
Loading…
Reference in New Issue
Block a user