github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/cloud/aws/cache/cache.go (about) 1 package cache 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "os" 7 "path" 8 "path/filepath" 9 "strings" 10 "time" 11 12 "github.com/aquasecurity/defsec/pkg/state" 13 ) 14 15 type Cache struct { 16 path string 17 accountID string 18 region string 19 maxAge time.Duration 20 } 21 22 const SchemaVersion = 2 23 24 type CacheData struct { 25 SchemaVersion int `json:"schema_version"` 26 State *state.State `json:"state"` 27 Services map[string]ServiceMetadata `json:"service_metadata"` 28 Updated time.Time `json:"updated"` 29 } 30 31 type ServiceMetadata struct { 32 Name string `json:"name"` 33 Updated time.Time `json:"updated"` 34 } 35 36 var ErrCacheNotFound = fmt.Errorf("cache record not found") 37 var ErrCacheIncompatible = fmt.Errorf("cache record used incomatible schema") 38 var ErrCacheExpired = fmt.Errorf("cache record expired") 39 40 func New(cacheDir string, maxCacheAge time.Duration, accountID, region string) *Cache { 41 return &Cache{ 42 path: path.Join(cacheDir, "cloud", "aws", accountID, strings.ToLower(region), "data.json"), 43 accountID: accountID, 44 region: region, 45 maxAge: maxCacheAge, 46 } 47 } 48 49 func (c *Cache) load() (*CacheData, error) { 50 51 m, err := os.Open(c.path) 52 if err != nil { 53 return nil, ErrCacheNotFound 54 } 55 defer func() { _ = m.Close() }() 56 57 var data CacheData 58 if err := json.NewDecoder(m).Decode(&data); err != nil { 59 return nil, err 60 } 61 62 if data.SchemaVersion != SchemaVersion { 63 return nil, ErrCacheIncompatible 64 } 65 66 if time.Since(data.Updated) > c.maxAge { 67 return nil, ErrCacheExpired 68 } 69 70 return &data, nil 71 } 72 73 func (c *Cache) ListServices(required []string) (included, missing []string) { 74 75 data, err := c.load() 76 if err != nil { 77 return nil, required 78 } 79 80 for _, service := range required { 81 metadata, ok := data.Services[service] 82 if !ok { 83 missing = append(missing, service) 84 continue 85 } 86 if time.Since(metadata.Updated) > c.maxAge { 87 missing = append(missing, service) 88 continue 89 } 90 included = append(included, service) 91 } 92 93 return included, missing 94 } 95 96 func (c *Cache) LoadState() (*state.State, error) { 97 data, err := c.load() 98 if err != nil { 99 return nil, err 100 } 101 return data.State, nil 102 } 103 104 func (c *Cache) AddServices(s *state.State, includedServices []string) error { 105 data := &CacheData{ 106 SchemaVersion: SchemaVersion, 107 State: s, 108 Services: make(map[string]ServiceMetadata), 109 Updated: time.Now(), 110 } 111 112 if previous, err := c.load(); err == nil { 113 data.Services = previous.Services 114 } 115 116 for _, service := range includedServices { 117 data.Services[service] = ServiceMetadata{ 118 Name: service, 119 Updated: time.Now(), 120 } 121 } 122 123 if err := os.MkdirAll(filepath.Dir(c.path), 0700); err != nil { 124 return err 125 } 126 f, err := os.Create(c.path) 127 if err != nil { 128 return err 129 } 130 defer func() { _ = f.Close() }() 131 return json.NewEncoder(f).Encode(data) 132 }