Skip to content
This repository was archived by the owner on Nov 26, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ build: # Build binary
@mkdir -p $(BUILD_DIR)
@go build -ldflags "-X main.Version=$(VERSION) -s -f" -o $(BUILD_DIR)/ ./...

.PHONY: install
install: # Build and install tool
@go install .

Expand Down
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This tool parses Go binary dependencies and calls [NVD database](https://nvd.nis
4. [Cache](#cache)
- [Memcachier](#memcachier)
- [Memcached](#memcached)
- [Memory](#memory)
- [SQLite](#sqlite)
5. [Versions](#versions)
6. [How to Fix Vulnerabilities](#how-to-fix-vulnerabilities)
7. [Information about vulnerabilities](#information-about-vulnerabilities)
Expand Down Expand Up @@ -93,11 +93,11 @@ Note that without API key, you will be limited to *10* requests in a rolling *60

## Cache

A cache is useful because if you perform more call to NVD database that allowed, your calls will significantly slow down. Gobinsec tries to build caches in this order:
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:

### Memcached

If no configuration is found for *Memcachier*, it will try to build a cache for *Memcached*, if following section is found in configuration file:
A cache is built with *Memcached* if following section is found in configuration file:

```yaml
memcached:
Expand Down Expand Up @@ -137,9 +137,15 @@ MEMCACHIER_PASSWORD

[Memcachier](https://www.memcachier.com) is an online cache provider with free tiers.

### Memory
### SQLite

If no configuration is found for *Memcachier* and *Memcached*, it will instantiate a memory cache. This cache will be useful if you pass more than one binary on command line.
If none of preceding configuration is found in configuration and none of related environment variables, *Gobinsec* will use *SQLite* for caching. By default, database file is stored in *~/.gobinsec.db* and cache duration is of one day (or *86400* seconds). You can overwrite these default values with following configuration section:

```yaml
sqlite:
file: "/path/to/database.db"
expiration: 86400
```

## Versions

Expand Down
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ go 1.17
require (
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d
github.com/fatih/color v1.13.0
github.com/mattn/go-sqlite3 v1.14.12
github.com/memcachier/gomemcache v0.0.0-20170425125614-d027381f7653
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
)

require (
github.com/mattn/go-colorable v0.1.9 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f // indirect
)
9 changes: 7 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@ github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwel
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/memcachier/gomemcache v0.0.0-20170425125614-d027381f7653 h1:222emoxOt/bCmNHp8Xt0Pr5Am3gIbqRKFpb4CQ9O2SI=
github.com/memcachier/gomemcache v0.0.0-20170425125614-d027381f7653/go.mod h1:KoYVbOQexD45AOLfn+gsFB6c3o4ANzP1QKzjE6tZbK0=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f h1:8w7RhxzTVgUzw/AH/9mUV5q0vMgy40SQRursCcfmkCw=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
Expand Down
4 changes: 4 additions & 0 deletions gobinsec/cache-memcached.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,7 @@ func (mc *MemcachedClient) Set(d *Dependency, v []byte) {
func (mc *MemcachedClient) Ping() error {
return mc.Client.Ping()
}

// Clean does nothing
func (mc *MemcachedClient) Clean() {
}
4 changes: 4 additions & 0 deletions gobinsec/cache-memcachier.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,7 @@ func (mc *MemcachierClient) Ping() error {
}
return mc.Client.Set(&item)
}

// Clean does nothing
func (mc *MemcachierClient) Clean() {
}
42 changes: 0 additions & 42 deletions gobinsec/cache-memory.go

This file was deleted.

147 changes: 147 additions & 0 deletions gobinsec/cache-sqlite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package gobinsec

import (
"database/sql"
"fmt"
"os"
"path/filepath"
"strings"
"time"

_ "github.com/mattn/go-sqlite3"
)

// SQLiteConfig is the configuration for SQLite
type SQLiteConfig struct {
File string `yaml:"file"`
Expiration int32 `yaml:"expiration"`
}

// NewSQLiteConfig builds configuration for SQLite cache
func NewSQLiteConfig(config *SQLiteConfig) *SQLiteConfig {
if config == nil {
config = &SQLiteConfig{}
}
if config.File == "" {
config.File = "~/.gobinsec.db"
}
if strings.HasPrefix(config.File, "~/") {
home, err := os.UserHomeDir()
if err != nil {
return nil
}
config.File = filepath.Join(home, config.File[2:])
}
if config.Expiration == 0 {
config.Expiration = 86400
}
return config
}

// SQLiteCache is the cache instance
type SQLiteCache struct {
Expiration int32
Database *sql.DB
}

// NewSQLiteCache builds a cache using SQLite
func NewSQLiteCache(config *SQLiteConfig) Cache {
database, err := sql.Open("sqlite3", config.File)
if err != nil {
return nil
}
cache := &SQLiteCache{
Database: database,
Expiration: config.Expiration,
}
return cache
}

// Get returns NVD response for given dependency
func (sc *SQLiteCache) Get(d *Dependency) []byte {
vulnerabilities, err := sc.RetrieveDependency(d)
if err != nil {
fmt.Printf("ERROR getting dependency: %v\n", err)
}
return vulnerabilities
}

// Set put NVD response for given dependency in cache
func (sc *SQLiteCache) Set(d *Dependency, v []byte) {
err := sc.InsertDependency(d, v)
if err != nil {
fmt.Printf("ERROR setting dependency: %v\n", err)
}
}

// Ping does nothing
func (sc *SQLiteCache) Ping() error {
return sc.CreateTable()
}

// Clean deletes expired entries
func (sc *SQLiteCache) Clean() {
err := sc.CleanTable()
if err != nil {
fmt.Printf("ERROR cleaning dependencies: %v\n", err)
}
}

// CreateTable creates dependencies table
func (sc *SQLiteCache) CreateTable() error {
query := `CREATE TABLE IF NOT EXISTS dependencies (
dependency TEXT,
date TEXT,
vulnerabilities TEXT
)`
_, err := sc.Database.Exec(query)
return err
}

// InsertDependency insert given dependency in database
func (sc *SQLiteCache) InsertDependency(d *Dependency, v []byte) error {
now := time.Now().UTC().Format("2006-01-02T15:04:05")
query := `INSERT INTO dependencies
(dependency, date, vulnerabilities)
VALUES
(?, ?, ?)`
_, err := sc.Database.Exec(query, d.Key(), now, string(v))
return err
}

// RetrieveDependency insert given dependency in database
func (sc *SQLiteCache) RetrieveDependency(d *Dependency) ([]byte, error) {
duration := time.Duration(sc.Expiration) * time.Second
limit := time.Now().UTC().Add(-duration).Format("2006-01-02T15:04:05")
query := `SELECT vulnerabilities
FROM dependencies
WHERE dependency = ?
AND date > ?`
row, err := sc.Database.Query(query, d.Key(), limit)
if err != nil {
return nil, err
}
if row.Err() != nil {
return nil, row.Err()
}
defer row.Close()
if row.Next() {
var vulnerabilities string
if err := row.Scan(&vulnerabilities); err != nil {
return nil, err
}
return []byte(vulnerabilities), nil
}
return nil, nil
}

// CleanTable deletes old expired entries
func (sc *SQLiteCache) CleanTable() error {
duration := time.Duration(sc.Expiration) * time.Second
limit := time.Now().UTC().Add(-duration).Format("2006-01-02T15:04:05")
query := `DELETE
FROM dependencies
WHERE date < ?`
_, err := sc.Database.Exec(query, limit)
return err
}
12 changes: 7 additions & 5 deletions gobinsec/cache.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
package gobinsec

var cache Cache
var CacheInstance Cache

type Cache interface {
Get(d *Dependency) []byte
Set(d *Dependency, v []byte)
Ping() error
Clean()
}

func BuildCache() error {
if conf := NewMemcachedConfig(config.Memcached); conf != nil {
cache = NewMemcachedCache(conf)
CacheInstance = NewMemcachedCache(conf)
} else if conf := NewMemcachierConfig(config.Memcachier); conf != nil {
cache = NewMemcachierCache(conf)
CacheInstance = NewMemcachierCache(conf)
} else {
cache = NewMemoryCache()
conf := NewSQLiteConfig(config.SQLite)
CacheInstance = NewSQLiteCache(conf)
}
return cache.Ping()
return CacheInstance.Ping()
}
1 change: 1 addition & 0 deletions gobinsec/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type Config struct {
APIKey string `yaml:"api-key"`
Memcached *MemcachedConfig `yaml:"memcached"`
Memcachier *MemcachierConfig `yaml:"memcachier"`
SQLite *SQLiteConfig `yaml:"sqlite"`
Ignore []string `yaml:"ignore"`
Strict bool `yaml:"strict"`
}
Expand Down
4 changes: 2 additions & 2 deletions gobinsec/dependency.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func NewDependency(name, version string) (*Dependency, error) {

// Vulnerabilities return list of vulnerabilities for given dependency
func (d *Dependency) LoadVulnerabilities() error {
vulnerabilities := cache.Get(d)
vulnerabilities := CacheInstance.Get(d)
if vulnerabilities == nil {
url := URL + d.Name
if config.APIKey != "" {
Expand All @@ -50,7 +50,7 @@ func (d *Dependency) LoadVulnerabilities() error {
if err != nil {
return err
}
cache.Set(d, vulnerabilities)
CacheInstance.Set(d, vulnerabilities)
}
var result Response
if err := json.Unmarshal(vulnerabilities, &result); err != nil {
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func main() {
}
}
}
gobinsec.CacheInstance.Clean()
if issue {
os.Exit(CodeVulnerable)
}
Expand Down
6 changes: 6 additions & 0 deletions test/config-sqlite.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
api-key: "${NVD_API_KEY}"
sqlite:
file: "~/.gobinsec.db"
expiration: 86400
ignore:
- "CVE-2020-14040"