Skip to content

🗄️ Storage Interface and Manager

The storage module defines the unified Storage interface for persisting and querying CPE and CVE data, plus a StorageManager that layers a cache over a primary store. Different backends (in-memory, filesystem, database) implement the same interface so they can be swapped transparently.

Type: Storage

go
type Storage interface {
    Initialize() error
    Close() error
    StoreCPE(cpe *CPE) error
    RetrieveCPE(id string) (*CPE, error)
    UpdateCPE(cpe *CPE) error
    DeleteCPE(id string) error
    SearchCPE(criteria *CPE, options *MatchOptions) ([]*CPE, error)
    AdvancedSearchCPE(criteria *CPE, options *AdvancedMatchOptions) ([]*CPE, error)
    StoreCVE(cve *CVEReference) error
    RetrieveCVE(cveID string) (*CVEReference, error)
    UpdateCVE(cve *CVEReference) error
    DeleteCVE(cveID string) error
    SearchCVE(query string, options *SearchOptions) ([]*CVEReference, error)
    FindCVEsByCPE(cpe *CPE) ([]*CVEReference, error)
    FindCPEsByCVE(cveID string) ([]*CPE, error)
    StoreDictionary(dict *CPEDictionary) error
    RetrieveDictionary() (*CPEDictionary, error)
    StoreModificationTimestamp(key string, timestamp time.Time) error
    RetrieveModificationTimestamp(key string) (time.Time, error)
}

Storage is the contract every backend must satisfy. It covers lifecycle (Initialize/Close), CPE CRUD and search, CVE CRUD and search, CPE↔CVE relationship lookup, dictionary storage, and modification-timestamp tracking. Implementations include MemoryStorage and FileStorage.

Type: SearchOptions

go
type SearchOptions struct {
    Offset            int
    Limit             int
    SortBy            string
    SortAscending     bool
    Filters           map[string]interface{}
    FullTextQuery     string
    IncludeDeprecated bool
    DateStart         *time.Time
    DateEnd           *time.Time
    MinCVSS           float64
    MaxCVSS           float64
}

SearchOptions controls CVE search behavior: pagination (Offset/Limit), sorting (SortBy/SortAscending), custom Filters, full-text query, deprecation inclusion, date range, and CVSS score range.

Type: StorageStats

go
type StorageStats struct {
    TotalCPEs           int
    TotalCVEs           int
    TotalDictionaryItems int
    StorageBytes        int64
    LastUpdated         time.Time
}

StorageStats holds aggregate counters returned by StorageManager.GetStats.

Type: StorageManager

go
type StorageManager struct {
    Primary         Storage
    Cache           Storage
    CacheEnabled    bool
    CacheTTLSeconds int
}

StorageManager wraps a primary Storage and an optional cache Storage. When a cache is set, reads try the cache first and fall back to the primary, writing back on a miss; writes propagate to both.

🆕 NewSearchOptions

go
func NewSearchOptions() *SearchOptions

Creates a SearchOptions with sensible defaults: Offset=0, Limit=100, SortBy="id", SortAscending=true, empty Filters, IncludeDeprecated=false.

ParameterTypeDescription
(none)
ReturnTypeDescription
#1*SearchOptionsDefault-initialized search options
go
opts := cpeskills.NewSearchOptions()
opts.Limit = 50
opts.Filters["vendor"] = "microsoft"
results, _ := storage.SearchCVE("windows", opts)

🏗️ NewStorageManager

go
func NewStorageManager(primary Storage) *StorageManager

Creates a StorageManager over the given primary store. The cache is disabled by default (CacheEnabled=false); CacheTTLSeconds defaults to 3600.

ParameterTypeDescription
primaryStorageThe persistent primary store; must not be nil
ReturnTypeDescription
#1*StorageManagerA new manager with cache disabled
go
primary, _ := cpeskills.NewFileStorage("/tmp/cpe-data", true)
_ = primary.Initialize()
manager := cpeskills.NewStorageManager(primary)

⚡ SetCache

go
func (sm *StorageManager) SetCache(cache Storage)

Attaches a cache store and enables caching. After this call, GetCPE/GetCVE read through the cache and StoreCPE writes through to it.

ParameterTypeDescription
Receiver*StorageManagerThe manager
cacheStorageThe cache backend (typically a MemoryStorage)
ReturnTypeDescription
(none)
go
cache := cpeskills.NewMemoryStorage()
_ = cache.Initialize()
manager.SetCache(cache)

🔍 GetCPE

go
func (sm *StorageManager) GetCPE(id string) (*CPE, error)

Returns the CPE for id. When caching is enabled, the cache is tried first; on a miss the primary is queried and the result is written back to the cache. Cache errors are swallowed; only primary-store errors propagate.

ParameterTypeDescription
Receiver*StorageManagerThe manager
idstringCPE identifier (typically the CPE 2.3 URI)
ReturnTypeDescription
#1*CPEThe located CPE
#2errorNon-nil if not found or the primary store fails
go
cpe, err := manager.GetCPE("cpe:2.3:o:microsoft:windows:10:*:*:*:*:*:*:*")
if err != nil {
    log.Printf("get cpe: %v", err)
}

💾 StoreCPE

go
func (sm *StorageManager) StoreCPE(cpe *CPE) error

Persists cpe to the primary store and, if caching is enabled, also writes it to the cache. Cache write failures are ignored so a primary-store success is never rolled back.

ParameterTypeDescription
Receiver*StorageManagerThe manager
cpe*CPEThe CPE to store
ReturnTypeDescription
#1errorNon-nil if the primary store fails
go
_ = manager.StoreCPE(&cpeskills.CPE{
    Cpe23:       "cpe:2.3:a:apache:log4j:2.0:*:*:*:*:*:*:*",
    Vendor:      cpeskills.Vendor("apache"),
    ProductName: cpeskills.Product("log4j"),
    Version:     cpeskills.Version("2.0"),
})

🔎 GetCVE

go
func (sm *StorageManager) GetCVE(cveID string) (*CVEReference, error)

Returns the CVE reference for cveID, with the same cache-read-through behavior as GetCPE.

ParameterTypeDescription
Receiver*StorageManagerThe manager
cveIDstringCVE identifier, e.g. CVE-2021-44228
ReturnTypeDescription
#1*CVEReferenceThe located CVE reference
#2errorNon-nil if not found or the primary store fails
go
cve, err := manager.GetCVE("CVE-2021-44228")
if err != nil {
    log.Printf("get cve: %v", err)
}
go
func (sm *StorageManager) Search(criteria *CPE, options *MatchOptions) ([]*CPE, error)

Searches the primary store (cache is bypassed) for CPEs matching criteria according to options. A nil criteria returns all CPEs.

ParameterTypeDescription
Receiver*StorageManagerThe manager
criteria*CPEMatch criteria; nil matches all
options*MatchOptionsMatch options; nil uses defaults
ReturnTypeDescription
#1[]*CPEMatching CPEs (empty slice if none)
#2errorNon-nil on store failure
go
results, _ := manager.Search(&cpeskills.CPE{
    Vendor:      cpeskills.Vendor("microsoft"),
    ProductName: cpeskills.Product("windows"),
}, &cpeskills.MatchOptions{})
fmt.Println(len(results))

🎯 AdvancedSearch

go
func (sm *StorageManager) AdvancedSearch(criteria *CPE, options *AdvancedMatchOptions) ([]*CPE, error)

Searches the primary store (cache bypassed) using advanced match options such as version ranges and regex filters.

ParameterTypeDescription
Receiver*StorageManagerThe manager
criteria*CPEMatch criteria
options*AdvancedMatchOptionsAdvanced match options
ReturnTypeDescription
#1[]*CPEMatching CPEs
#2errorNon-nil on store failure
go
results, _ := manager.AdvancedSearch(criteria, &cpeskills.AdvancedMatchOptions{})

🧹 InvalidateCache

go
func (sm *StorageManager) InvalidateCache(id string)

Removes the CPE with the given id from the cache (best-effort). No-op when caching is disabled. Errors are swallowed.

ParameterTypeDescription
Receiver*StorageManagerThe manager
idstringCPE identifier to evict
ReturnTypeDescription
(none)
go
manager.InvalidateCache("cpe:2.3:o:microsoft:windows:10:*:*:*:*:*:*:*")

🧽 ClearCache

go
func (sm *StorageManager) ClearCache() error

Clears the entire cache by re-initializing the cache store. No-op (returns nil) when caching is disabled or the cache is nil.

ParameterTypeDescription
Receiver*StorageManagerThe manager
ReturnTypeDescription
#1errorNon-nil if cache re-initialization fails
go
if err := manager.ClearCache(); err != nil {
    log.Printf("clear cache: %v", err)
}

📊 GetStats

go
func (sm *StorageManager) GetStats() (*StorageStats, error)

Collects statistics from the primary store: total CPE count (via a full search), total CVE count (via an empty CVE search), dictionary item count, and the last_update modification timestamp (falling back to time.Now() if absent).

ParameterTypeDescription
Receiver*StorageManagerThe manager
ReturnTypeDescription
#1*StorageStatsAggregate counters
#2errorNon-nil if a primary-store query fails
go
stats, _ := manager.GetStats()
fmt.Printf("CPEs=%d CVEs=%d items=%d\n", stats.TotalCPEs, stats.TotalCVEs, stats.TotalDictionaryItems)

🧭 Storage Manager Data Flow

Released under the MIT License.