Cybersecurity-Projects/PROJECTS/intermediate/sbom-generator-vulnerabilit.../internal/vuln/cache.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")
}