103 lines
2.2 KiB
Go
103 lines
2.2 KiB
Go
// ©AngelaMos | 2026
|
|
// cache.go
|
|
|
|
package vuln
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
_ "modernc.org/sqlite"
|
|
|
|
"github.com/CarterPerez-dev/bomber/pkg/types"
|
|
)
|
|
|
|
type Cache struct {
|
|
db *sql.DB
|
|
ttl time.Duration
|
|
}
|
|
|
|
func NewCache(dbPath string, ttl time.Duration) (*Cache, error) {
|
|
dir := filepath.Dir(dbPath)
|
|
if err := os.MkdirAll(dir, 0o750); err != nil {
|
|
return nil, fmt.Errorf("create cache dir: %w", err)
|
|
}
|
|
|
|
db, err := sql.Open("sqlite", dbPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("open cache db: %w", err)
|
|
}
|
|
|
|
createSQL := `CREATE TABLE IF NOT EXISTS vuln_cache (
|
|
purl TEXT NOT NULL,
|
|
source TEXT NOT NULL,
|
|
data BLOB NOT NULL,
|
|
created_at INTEGER NOT NULL,
|
|
PRIMARY KEY (purl, source)
|
|
)`
|
|
if _, err := db.Exec(createSQL); err != nil {
|
|
_ = db.Close()
|
|
return nil, fmt.Errorf("create cache table: %w", err)
|
|
}
|
|
|
|
return &Cache{db: db, ttl: ttl}, nil
|
|
}
|
|
|
|
func (c *Cache) Put(purl, source string, matches []types.VulnMatch) error {
|
|
data, err := json.Marshal(matches)
|
|
if err != nil {
|
|
return fmt.Errorf("marshal cache data: %w", err)
|
|
}
|
|
|
|
upsertSQL := `INSERT OR REPLACE INTO vuln_cache (purl, source, data, created_at)
|
|
VALUES (?, ?, ?, ?)`
|
|
_, err = c.db.Exec(upsertSQL, purl, source, data, time.Now().UnixMilli())
|
|
if err != nil {
|
|
return fmt.Errorf("insert cache: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Cache) Get(purl, source string) ([]types.VulnMatch, bool, error) {
|
|
querySQL := `SELECT data, created_at FROM vuln_cache WHERE purl = ? AND source = ?`
|
|
row := c.db.QueryRow(querySQL, purl, source)
|
|
|
|
var data []byte
|
|
var createdAt int64
|
|
if err := row.Scan(&data, &createdAt); err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, false, nil
|
|
}
|
|
return nil, false, fmt.Errorf("query cache: %w", err)
|
|
}
|
|
|
|
created := time.UnixMilli(createdAt)
|
|
if time.Since(created) > c.ttl {
|
|
return nil, false, nil
|
|
}
|
|
|
|
var matches []types.VulnMatch
|
|
if err := json.Unmarshal(data, &matches); err != nil {
|
|
return nil, false, fmt.Errorf("unmarshal cache data: %w", err)
|
|
}
|
|
|
|
return matches, true, nil
|
|
}
|
|
|
|
func (c *Cache) Close() error {
|
|
return c.db.Close()
|
|
}
|
|
|
|
func DefaultCachePath() string {
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return ".bomber/cache.db"
|
|
}
|
|
return filepath.Join(home, ".bomber", "cache.db")
|
|
}
|