Skip to content
This repository was archived by the owner on Nov 26, 2024. It is now read-only.

Commit ccf371b

Browse files
c4s4Michel Casabianca
andauthored
Added -wait command line option (#11)
Co-authored-by: Michel Casabianca <michel.casabianca@intercloud.com>
1 parent 2e40797 commit ccf371b

7 files changed

Lines changed: 60 additions & 26 deletions

File tree

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,10 @@ Configuration file is in YAML format as follows:
9191
9292
```yaml
9393
api-key: "28c6112c-a7bc-4a4e-9b14-75be6da02211"
94+
wait: false
9495
strict: false
96+
ignore:
97+
- "CVE-2020-14040"
9598
memcachier:
9699
address: "mcx.cy.eu-central-1.ec2.memcachier.com:11211"
97100
username: "username"
@@ -105,26 +108,27 @@ memcached:
105108
file:
106109
name: "~/.gobinsec-cache.yml"
107110
expiration: "24h"
108-
ignore:
109-
- "CVE-2020-14040"
110111
```
111112

112113
Configuration fields are the following:
113114

114115
- **api-key**: this is your NVD API key
116+
- **wait**: tells if we should wait between NVD API calls to ensure that we are below rate limits
115117
- **strict**: tells if we should consider vulnerability matches without version as matching dependency
118+
- **ignore**: a list of CVE vulnerabilities to ignore
116119
- **memcachier** is the configuration for *memcachier*, see below
117120
- **memcached** is the configuration for *memcached*, see below
118121
- **file** is the configuration for *file* cache, see below
119-
- **ignore**: a list of CVE vulnerabilities to ignore
120122

121123
You can also set NVD API Key in your environment with variable *NVD_API_KEY*. This key may be overwritten with value in configuration file. Your API key must be set in environment to be able to run integration tests (with target *integ*).
122124

123125
Note that without API key, you will be limited to *10* requests in a rolling *60* second window while this limit is *100* with an API key.
124126

125127
## Cache
126128

127-
A cache is useful because if you perform more call to NVD database than allowed, your calls will significantly slow down. Gobinsec tries to build caches in this order:
129+
A cache is useful to limit NVD API calls. If you perform more call to NVD database than allowed, your calls will significantly slow down or you will get status code *403* calling the API.
130+
131+
Gobinsec tries to build caches in this order:
128132

129133
### Memcachier
130134

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ require (
1212
require (
1313
github.com/mattn/go-colorable v0.1.12 // indirect
1414
github.com/mattn/go-isatty v0.0.14 // indirect
15-
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
15+
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect
1616
)

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w
1414
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1515
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1616
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
17-
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
18-
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
17+
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc=
18+
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1919
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
2020
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
2121
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=

gobinsec/binary.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,11 @@ func (b *Binary) GetDependencies() error {
6161
dependencies <- dependency
6262
wg.Add(1)
6363
}
64-
for i := 0; i < NumGoroutines; i++ {
64+
numGoroutines := NumGoroutines
65+
if config.Wait {
66+
numGoroutines = 1
67+
}
68+
for i := 0; i < numGoroutines; i++ {
6569
go LoadVulnerabilities(dependencies, &wg)
6670
}
6771
wg.Wait()

gobinsec/config.go

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"gopkg.in/yaml.v3"
88
)
99

10+
// Config is the configuration from YAML config file and command line options
1011
type Config struct {
1112
APIKey string `yaml:"api-key"`
1213
Memcached *MemcachedConfig `yaml:"memcached"`
@@ -16,30 +17,31 @@ type Config struct {
1617
Strict bool `yaml:"strict"`
1718
Verbose bool `yaml:"verbose"`
1819
Cache bool `yaml:"cache"`
20+
Wait bool `yaml:"wait"`
1921
}
2022

23+
// config is shared
2124
var config Config
2225

23-
// LoadConfig loads configuration from given file
24-
func LoadConfig(path string, strict, verbose, cache bool) error {
25-
if path == "" {
26-
config.Strict = strict
27-
config.Verbose = verbose
28-
config.Cache = cache
29-
return nil
30-
}
31-
bytes, err := os.ReadFile(path)
32-
if err != nil {
33-
return fmt.Errorf("loading configuration file: %v", err)
34-
}
35-
if err := yaml.Unmarshal(bytes, &config); err != nil {
36-
return fmt.Errorf("parsing configuration: %v", err)
26+
// LoadConfig loads configuration from given file and overwrite with command line options
27+
func LoadConfig(path string, strict, wait, verbose, cache bool) error {
28+
if path != "" {
29+
bytes, err := os.ReadFile(path)
30+
if err != nil {
31+
return fmt.Errorf("loading configuration file: %v", err)
32+
}
33+
if err := yaml.Unmarshal(bytes, &config); err != nil {
34+
return fmt.Errorf("parsing configuration: %v", err)
35+
}
3736
}
3837
if config.APIKey == "" {
3938
config.APIKey = os.Getenv("NVD_API_KEY")
4039
}
4140
if strict {
42-
config.Strict = strict
41+
config.Strict = true
42+
}
43+
if wait {
44+
config.Wait = true
4345
}
4446
config.Verbose = verbose
4547
config.Cache = cache

gobinsec/dependency.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,19 @@ import (
55
"fmt"
66
"io"
77
"net/http"
8+
"time"
89
)
910

1011
const (
11-
URL = "https://services.nvd.nist.gov/rest/json/cves/1.0/?keyword="
12-
StatusCodeLimit = 300
12+
URL = "https://services.nvd.nist.gov/rest/json/cves/1.0/?keyword="
13+
StatusCodeLimit = 300
14+
RateMinuteWithoutKey = 10
15+
RateMinuteWithKey = 100
1316
)
1417

18+
// timeLastCall is time for last NVD API call
19+
var timeLastCall time.Time
20+
1521
// Dependency is a dependency with vulnerabilities
1622
type Dependency struct {
1723
Name string
@@ -37,6 +43,7 @@ func (d *Dependency) LoadVulnerabilities() error {
3743
return err
3844
}
3945
if vulnerabilities == nil {
46+
WaitBeforeCall()
4047
url := URL + d.Name
4148
if config.APIKey != "" {
4249
url += "&apiKey=" + config.APIKey
@@ -75,6 +82,22 @@ func (d *Dependency) LoadVulnerabilities() error {
7582
return nil
7683
}
7784

85+
// WaitBeforeCall waits in order not to exceed NVD call rate limit
86+
func WaitBeforeCall() {
87+
if config.Wait {
88+
elapsedSinceLastCall := time.Since(timeLastCall)
89+
timeToSleep := (60000 / RateMinuteWithoutKey) * time.Millisecond
90+
if config.APIKey != "" {
91+
timeToSleep = (60000 / RateMinuteWithKey) * time.Millisecond
92+
}
93+
timeToWait := timeToSleep - elapsedSinceLastCall
94+
if timeToWait > 0 {
95+
time.Sleep(timeToWait)
96+
}
97+
timeLastCall = time.Now()
98+
}
99+
}
100+
78101
// Key returns a key as a string for caching
79102
func (d *Dependency) Key() string {
80103
return d.Name

main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ func main() {
1919
version := flag.Bool("version", false, "Print gobinsec version")
2020
verbose := flag.Bool("verbose", false, "Print additional information in terminal")
2121
cache := flag.Bool("cache", false, "Print cache information in terminal")
22+
wait := flag.Bool("wait", false, "Wait between NVD API calls")
2223
strict := flag.Bool("strict", false, "Vulnerabilities without version are exposed")
2324
config := flag.String("config", "", "Configuration file")
2425
flag.Parse()
@@ -30,7 +31,7 @@ func main() {
3031
println("ERROR you must pass binary/ies to analyze on command line")
3132
os.Exit(CodeError)
3233
}
33-
if err := gobinsec.LoadConfig(*config, *strict, *verbose, *cache); err != nil {
34+
if err := gobinsec.LoadConfig(*config, *strict, *wait, *verbose, *cache); err != nil {
3435
println(fmt.Sprintf("ERROR %v", err))
3536
os.Exit(CodeError)
3637
}

0 commit comments

Comments
 (0)