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

Commit 612861d

Browse files
authored
Memcached (#4)
Added cache on NVD calls
1 parent e1b3849 commit 612861d

15 files changed

Lines changed: 180 additions & 60 deletions

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,6 @@ release: clean lint test integ binaries # Perform release (must pass VERSION=X.Y
4848
@test `git rev-parse --abbrev-ref HEAD` = 'main' || (echo "ERROR You are not on branch main" && exit 1)
4949
@git tag -a $(VERSION) -m "Release $(VERSION)"
5050
@git push origin --tags
51+
52+
memcached: # Start memcached
53+
@docker-compose up -d memcached

README.md

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
11
# Gobinsec
22

3-
This tool parses Go binary dependencies, calls NVD database to produce a vulnerability report for this binary.
3+
This tool parses Go binary dependencies and calls [NVD database](https://nvd.nist.gov/) to produce a vulnerability report.
44

5-
## Install
5+
## Table of Contents
6+
7+
1. [Installation](#installation)
8+
2. [Usage](#usage)
9+
3. [Configuration](#configuration)
10+
4. [Cache](#cache)
11+
5. [Version](#versions)
12+
6. [How to Fix Vulnerabilities](#how-to-fix-vulnerabilities)
13+
7. [Data Source](#data-source)
14+
8. [License](#license)
15+
16+
## Installation
617

718
Download binary for your platform in [latest release](https://github.com/intercloud/gobinsec/releases). Rename it *gobinsec*, make it executable with `chmod +x gobinsec` and move it somewhere in your *PATH*.
819

@@ -30,13 +41,15 @@ dependencies:
3041
- '?'
3142
```
3243
33-
Exit code is *1* if binary is vulnerable, *2* if there was an error analyzing binary and *0* otherwise. If binary is vulnerable, exposed vulnerabilities are printed in report.
44+
You can pass more than one binary to check on command line.
45+
46+
Exit code is *1* if exposed vulnerabilities were found, *2* if there was an error analyzing a binary and *0* otherwise. If a binary is vulnerable, exposed vulnerabilities are printed in report.
3447
3548
You can pass *-verbose* option on command line to print vulnerability report, even if binary is not vulnerable and for all vulnerabilities, even if they are ignored or not exposed.
3649
3750
You can set *-strict* flag on command line so that vulnerabilities without version are considered matching vulnerability. In this case, you should check vulnerability manually and disable it in configuration file if necessary.
3851
39-
You can pass more than one binary on command line. In this case, there will be cache on calls to NVD database.
52+
You can pass configuration file with *-config config.yml*, see configuration section below.
4053
4154
## Configuration
4255
@@ -51,20 +64,30 @@ Configuration file is in YAML format as follows:
5164
```yaml
5265
api-key: "28c6112c-a7bc-4a4e-9b14-75be6da02211"
5366
strict: false
67+
memcached:
68+
address: 127.0.0.1:11211
69+
expiration: 86400
5470
ignore:
5571
- "CVE-2020-14040"
5672
```
5773

58-
It has two entries:
74+
Configuration fields are the following:
5975

6076
- **api-key**: this is your NVD API key
6177
- **strict**: tells if we should consider vulnerability matches without version as matching dependency
78+
- **memcached** is the configuration for *memcached*, with **address** and **expiration** time in seconds
6279
- **ignore**: a list of CVE vulnerabilities to ignore
6380

6481
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*).
6582

6683
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.
6784

85+
## Cache
86+
87+
If you define the *memcached* configuration in your configuration file, *memcached* will be used to cache calls to NVD database. This is useful because if you perform more call that allowed, your calls will significantly slow down. An sample [docker-compose.yml](https://github.com/intercloud/gobinsec/blob/main/docker-compose.yml) to start a *memcached* instance is proposed in this project.
88+
89+
If you don't define the *memcached* configuration, the program will use a memory cache when you pass more than one binary to analyse on command line.
90+
6891
## Versions
6992

7093
Dependencies and vulnerabilities have versions. There are three types of them:

docker-compose.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
services:
2+
3+
memcached:
4+
image: memcached:1.6.13
5+
restart: unless-stopped
6+
ports:
7+
- 11211:11211

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require (
88
)
99

1010
require (
11+
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect
1112
github.com/mattn/go-colorable v0.1.9 // indirect
1213
github.com/mattn/go-isatty v0.0.14 // indirect
1314
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwelyab6n21ZBkbkmbevaf+WvMIiR7sr97hw=
2+
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
13
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
24
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
35
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=

gobinsec/binary.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ func LoadVulnerabilities(dependencies chan *Dependency, wg *sync.WaitGroup) {
105105
// Report prints a report on terminal
106106
// nolint:gocyclo // this is life
107107
func (b *Binary) Report(verbose bool) {
108-
fmt.Printf("%s: ",filepath.Base(b.Path))
109-
if (b.Vulnerable) {
108+
fmt.Printf("%s: ", filepath.Base(b.Path))
109+
if b.Vulnerable {
110110
ColorRed.Println("VULNERABLE")
111111
} else {
112112
ColorGreen.Println("OK")

gobinsec/cache-memcached.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package gobinsec
2+
3+
import "github.com/bradfitz/gomemcache/memcache"
4+
5+
// MemcachedClient is the Cache using memcached
6+
type MemcachedClient struct {
7+
Client *memcache.Client
8+
Expiration int32
9+
}
10+
11+
// NewMemcachedCache builds a memcached cache
12+
func NewMemcachedCache() Cache {
13+
cache := MemcachedClient{
14+
Client: memcache.New(config.Memcached.Address),
15+
Expiration: config.Memcached.Expiration,
16+
}
17+
return &cache
18+
}
19+
20+
// Get returns NVD response for given dependency
21+
func (mc *MemcachedClient) Get(d *Dependency) []byte {
22+
item, err := mc.Client.Get(d.Key())
23+
if err != nil {
24+
return nil
25+
}
26+
return item.Value
27+
}
28+
29+
// Set put NVD response for given dependency in cache
30+
func (mc *MemcachedClient) Set(d *Dependency, v []byte) {
31+
item := memcache.Item{
32+
Key: d.Key(),
33+
Value: v,
34+
Expiration: mc.Expiration,
35+
}
36+
mc.Client.Set(&item) // nolint:errcheck // we ignore error
37+
}
38+
39+
// Ping calls memcached
40+
func (mc *MemcachedClient) Ping() error {
41+
return mc.Client.Ping()
42+
}

gobinsec/cache-memory.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package gobinsec
2+
3+
import (
4+
"sync"
5+
)
6+
7+
// MemoryCache is a cache with a synced map
8+
type MemoryCache map[string][]byte
9+
10+
// lock for memory cache
11+
var lock sync.RWMutex
12+
13+
// NewMemoryCache builds a cache in memory
14+
func NewMemoryCache() *MemoryCache {
15+
cache := make(MemoryCache)
16+
return &cache
17+
}
18+
19+
// Get returns NVD response for given dependency
20+
func (mc *MemoryCache) Get(d *Dependency) []byte {
21+
key := d.Key()
22+
lock.RLock()
23+
defer lock.RUnlock()
24+
vulnerabilities, ok := (*mc)[key]
25+
if ok {
26+
return vulnerabilities
27+
}
28+
return nil
29+
}
30+
31+
// Set put NVD response for given dependency in cache
32+
func (mc *MemoryCache) Set(d *Dependency, v []byte) {
33+
key := d.Key()
34+
lock.Lock()
35+
defer lock.Unlock()
36+
(*mc)[key] = v
37+
}
38+
39+
// Ping does nothing
40+
func (mc *MemoryCache) Ping() error {
41+
return nil
42+
}

gobinsec/cache.go

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,18 @@
11
package gobinsec
22

3-
import (
4-
"sync"
5-
)
3+
var cache Cache
64

7-
type VulnerabilityCache map[string][]Vulnerability
8-
9-
var lock sync.RWMutex
10-
var cache = NewVulnerabilityCache()
11-
12-
func NewVulnerabilityCache() *VulnerabilityCache {
13-
cache := make(VulnerabilityCache)
14-
return &cache
15-
}
16-
17-
func (dc *VulnerabilityCache) Get(d *Dependency) []Vulnerability {
18-
key := d.Key()
19-
lock.RLock()
20-
defer lock.RUnlock()
21-
vulnerabilities, ok := (*dc)[key]
22-
if ok {
23-
return vulnerabilities
24-
}
25-
return nil
5+
type Cache interface {
6+
Get(d *Dependency) []byte
7+
Set(d *Dependency, v []byte)
8+
Ping() error
269
}
2710

28-
func (dc *VulnerabilityCache) Put(d *Dependency, v []Vulnerability) {
29-
key := d.Key()
30-
if v == nil {
31-
v = make([]Vulnerability, 0)
11+
func BuildCache() error {
12+
if config.Memcached == nil {
13+
cache = NewMemoryCache()
14+
} else {
15+
cache = NewMemcachedCache()
3216
}
33-
lock.Lock()
34-
defer lock.Unlock()
35-
(*dc)[key] = v
17+
return cache.Ping()
3618
}

gobinsec/config.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,15 @@ import (
88
)
99

1010
type Config struct {
11-
APIKey string `yaml:"api-key"`
12-
Ignore []string `yaml:"ignore"`
13-
Strict bool `yaml:"strict"`
11+
APIKey string `yaml:"api-key"`
12+
Memcached *MemcachedConfig `yaml:"memcached"`
13+
Ignore []string `yaml:"ignore"`
14+
Strict bool `yaml:"strict"`
15+
}
16+
17+
type MemcachedConfig struct {
18+
Address string `yaml:"address"`
19+
Expiration int32 `yaml:"expiration"`
1420
}
1521

1622
var config Config
@@ -37,6 +43,7 @@ func LoadConfig(path string, strict bool) error {
3743
return nil
3844
}
3945

46+
// IgnoreVulnerability tells if we should ignore given vulnerability
4047
func (c *Config) IgnoreVulnerability(id string) bool {
4148
for _, ignore := range c.Ignore {
4249
if ignore == id {

0 commit comments

Comments
 (0)