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

Commit 36969ca

Browse files
authored
Added SQLite support for caching (#6)
1 parent bcc3fac commit 36969ca

13 files changed

Lines changed: 194 additions & 58 deletions

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ build: # Build binary
2323
@mkdir -p $(BUILD_DIR)
2424
@go build -ldflags "-X main.Version=$(VERSION) -s -f" -o $(BUILD_DIR)/ ./...
2525

26+
.PHONY: install
2627
install: # Build and install tool
2728
@go install .
2829

README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ This tool parses Go binary dependencies and calls [NVD database](https://nvd.nis
1010
4. [Cache](#cache)
1111
- [Memcachier](#memcachier)
1212
- [Memcached](#memcached)
13-
- [Memory](#memory)
13+
- [SQLite](#sqlite)
1414
5. [Versions](#versions)
1515
6. [How to Fix Vulnerabilities](#how-to-fix-vulnerabilities)
1616
7. [Information about vulnerabilities](#information-about-vulnerabilities)
@@ -93,11 +93,11 @@ Note that without API key, you will be limited to *10* requests in a rolling *60
9393

9494
## Cache
9595

96-
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:
96+
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:
9797

9898
### Memcached
9999

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

102102
```yaml
103103
memcached:
@@ -137,9 +137,15 @@ MEMCACHIER_PASSWORD
137137

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

140-
### Memory
140+
### SQLite
141141

142-
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.
142+
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:
143+
144+
```yaml
145+
sqlite:
146+
file: "/path/to/database.db"
147+
expiration: 86400
148+
```
143149
144150
## Versions
145151

go.mod

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ go 1.17
55
require (
66
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d
77
github.com/fatih/color v1.13.0
8+
github.com/mattn/go-sqlite3 v1.14.12
89
github.com/memcachier/gomemcache v0.0.0-20170425125614-d027381f7653
910
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
1011
)
1112

1213
require (
13-
github.com/mattn/go-colorable v0.1.9 // indirect
14+
github.com/mattn/go-colorable v0.1.12 // indirect
1415
github.com/mattn/go-isatty v0.0.14 // indirect
15-
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
16+
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f // indirect
1617
)

go.sum

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,22 @@ github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwel
22
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
33
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
44
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
5-
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
65
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
6+
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
7+
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
78
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
89
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
910
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
11+
github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
12+
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
1013
github.com/memcachier/gomemcache v0.0.0-20170425125614-d027381f7653 h1:222emoxOt/bCmNHp8Xt0Pr5Am3gIbqRKFpb4CQ9O2SI=
1114
github.com/memcachier/gomemcache v0.0.0-20170425125614-d027381f7653/go.mod h1:KoYVbOQexD45AOLfn+gsFB6c3o4ANzP1QKzjE6tZbK0=
1215
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1316
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
14-
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
1517
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
18+
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
19+
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f h1:8w7RhxzTVgUzw/AH/9mUV5q0vMgy40SQRursCcfmkCw=
20+
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1621
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
1722
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
1823
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=

gobinsec/cache-memcached.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,7 @@ func (mc *MemcachedClient) Set(d *Dependency, v []byte) {
7373
func (mc *MemcachedClient) Ping() error {
7474
return mc.Client.Ping()
7575
}
76+
77+
// Clean does nothing
78+
func (mc *MemcachedClient) Clean() {
79+
}

gobinsec/cache-memcachier.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,7 @@ func (mc *MemcachierClient) Ping() error {
8686
}
8787
return mc.Client.Set(&item)
8888
}
89+
90+
// Clean does nothing
91+
func (mc *MemcachierClient) Clean() {
92+
}

gobinsec/cache-memory.go

Lines changed: 0 additions & 42 deletions
This file was deleted.

gobinsec/cache-sqlite.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package gobinsec
2+
3+
import (
4+
"database/sql"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
"time"
10+
11+
_ "github.com/mattn/go-sqlite3"
12+
)
13+
14+
// SQLiteConfig is the configuration for SQLite
15+
type SQLiteConfig struct {
16+
File string `yaml:"file"`
17+
Expiration int32 `yaml:"expiration"`
18+
}
19+
20+
// NewSQLiteConfig builds configuration for SQLite cache
21+
func NewSQLiteConfig(config *SQLiteConfig) *SQLiteConfig {
22+
if config == nil {
23+
config = &SQLiteConfig{}
24+
}
25+
if config.File == "" {
26+
config.File = "~/.gobinsec.db"
27+
}
28+
if strings.HasPrefix(config.File, "~/") {
29+
home, err := os.UserHomeDir()
30+
if err != nil {
31+
return nil
32+
}
33+
config.File = filepath.Join(home, config.File[2:])
34+
}
35+
if config.Expiration == 0 {
36+
config.Expiration = 86400
37+
}
38+
return config
39+
}
40+
41+
// SQLiteCache is the cache instance
42+
type SQLiteCache struct {
43+
Expiration int32
44+
Database *sql.DB
45+
}
46+
47+
// NewSQLiteCache builds a cache using SQLite
48+
func NewSQLiteCache(config *SQLiteConfig) Cache {
49+
database, err := sql.Open("sqlite3", config.File)
50+
if err != nil {
51+
return nil
52+
}
53+
cache := &SQLiteCache{
54+
Database: database,
55+
Expiration: config.Expiration,
56+
}
57+
return cache
58+
}
59+
60+
// Get returns NVD response for given dependency
61+
func (sc *SQLiteCache) Get(d *Dependency) []byte {
62+
vulnerabilities, err := sc.RetrieveDependency(d)
63+
if err != nil {
64+
fmt.Printf("ERROR getting dependency: %v\n", err)
65+
}
66+
return vulnerabilities
67+
}
68+
69+
// Set put NVD response for given dependency in cache
70+
func (sc *SQLiteCache) Set(d *Dependency, v []byte) {
71+
err := sc.InsertDependency(d, v)
72+
if err != nil {
73+
fmt.Printf("ERROR setting dependency: %v\n", err)
74+
}
75+
}
76+
77+
// Ping does nothing
78+
func (sc *SQLiteCache) Ping() error {
79+
return sc.CreateTable()
80+
}
81+
82+
// Clean deletes expired entries
83+
func (sc *SQLiteCache) Clean() {
84+
err := sc.CleanTable()
85+
if err != nil {
86+
fmt.Printf("ERROR cleaning dependencies: %v\n", err)
87+
}
88+
}
89+
90+
// CreateTable creates dependencies table
91+
func (sc *SQLiteCache) CreateTable() error {
92+
query := `CREATE TABLE IF NOT EXISTS dependencies (
93+
dependency TEXT,
94+
date TEXT,
95+
vulnerabilities TEXT
96+
)`
97+
_, err := sc.Database.Exec(query)
98+
return err
99+
}
100+
101+
// InsertDependency insert given dependency in database
102+
func (sc *SQLiteCache) InsertDependency(d *Dependency, v []byte) error {
103+
now := time.Now().UTC().Format("2006-01-02T15:04:05")
104+
query := `INSERT INTO dependencies
105+
(dependency, date, vulnerabilities)
106+
VALUES
107+
(?, ?, ?)`
108+
_, err := sc.Database.Exec(query, d.Key(), now, string(v))
109+
return err
110+
}
111+
112+
// RetrieveDependency insert given dependency in database
113+
func (sc *SQLiteCache) RetrieveDependency(d *Dependency) ([]byte, error) {
114+
duration := time.Duration(sc.Expiration) * time.Second
115+
limit := time.Now().UTC().Add(-duration).Format("2006-01-02T15:04:05")
116+
query := `SELECT vulnerabilities
117+
FROM dependencies
118+
WHERE dependency = ?
119+
AND date > ?`
120+
row, err := sc.Database.Query(query, d.Key(), limit)
121+
if err != nil {
122+
return nil, err
123+
}
124+
if row.Err() != nil {
125+
return nil, row.Err()
126+
}
127+
defer row.Close()
128+
if row.Next() {
129+
var vulnerabilities string
130+
if err := row.Scan(&vulnerabilities); err != nil {
131+
return nil, err
132+
}
133+
return []byte(vulnerabilities), nil
134+
}
135+
return nil, nil
136+
}
137+
138+
// CleanTable deletes old expired entries
139+
func (sc *SQLiteCache) CleanTable() error {
140+
duration := time.Duration(sc.Expiration) * time.Second
141+
limit := time.Now().UTC().Add(-duration).Format("2006-01-02T15:04:05")
142+
query := `DELETE
143+
FROM dependencies
144+
WHERE date < ?`
145+
_, err := sc.Database.Exec(query, limit)
146+
return err
147+
}

gobinsec/cache.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
package gobinsec
22

3-
var cache Cache
3+
var CacheInstance Cache
44

55
type Cache interface {
66
Get(d *Dependency) []byte
77
Set(d *Dependency, v []byte)
88
Ping() error
9+
Clean()
910
}
1011

1112
func BuildCache() error {
1213
if conf := NewMemcachedConfig(config.Memcached); conf != nil {
13-
cache = NewMemcachedCache(conf)
14+
CacheInstance = NewMemcachedCache(conf)
1415
} else if conf := NewMemcachierConfig(config.Memcachier); conf != nil {
15-
cache = NewMemcachierCache(conf)
16+
CacheInstance = NewMemcachierCache(conf)
1617
} else {
17-
cache = NewMemoryCache()
18+
conf := NewSQLiteConfig(config.SQLite)
19+
CacheInstance = NewSQLiteCache(conf)
1820
}
19-
return cache.Ping()
21+
return CacheInstance.Ping()
2022
}

gobinsec/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type Config struct {
1111
APIKey string `yaml:"api-key"`
1212
Memcached *MemcachedConfig `yaml:"memcached"`
1313
Memcachier *MemcachierConfig `yaml:"memcachier"`
14+
SQLite *SQLiteConfig `yaml:"sqlite"`
1415
Ignore []string `yaml:"ignore"`
1516
Strict bool `yaml:"strict"`
1617
}

0 commit comments

Comments
 (0)