File: cache.go

package info (click to toggle)
golang-golang-x-vuln 0.0~git20230201.4c848ed-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 992 kB
  • sloc: sh: 288; asm: 40; makefile: 7
file content (154 lines) | stat: -rw-r--r-- 3,660 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package govulncheck

import (
	"encoding/json"
	"os"
	"path/filepath"
	"sync"
	"time"

	"golang.org/x/vuln/client"
	"golang.org/x/vuln/internal"
	"golang.org/x/vuln/osv"
)

// The cache uses a single JSON index file for each vulnerability database
// which contains the map from packages to the time the last
// vulnerability for that package was added/modified and the time that
// the index was retrieved from the vulnerability database. The JSON
// format is as follows:
//
// $GOMODCACHE/cache/download/vulndb/{db hostname}/indexes/index.json
//   {
//       Retrieved time.Time
//       Index client.DBIndex
//   }
//
// Each package also has a JSON file which contains the array of vulnerability
// entries for the package. The JSON format is as follows:
//
// $GOMODCACHE/cache/download/vulndb/{db hostname}/{import path}/vulns.json
//   []*osv.Entry

// FSCache is a thread-safe file-system cache implementing osv.Cache
//
// TODO: use something like cmd/go/internal/lockedfile for thread safety?
type FSCache struct {
	mu      sync.Mutex
	rootDir string
}

// Assert that *FSCache implements client.Cache.
var _ client.Cache = (*FSCache)(nil)

var (
	initDefaultCache sync.Once
	defaultCache     *FSCache
	defaultCacheErr  error
)

func DefaultCache() (*FSCache, error) {
	initDefaultCache.Do(func() {
		mod, err := internal.GoEnv("GOMODCACHE")
		if err != nil {
			defaultCacheErr = err
			return
		}
		defaultCache = &FSCache{
			rootDir: filepath.Join(mod, "/cache/download/vulndb"),
		}
	})
	return defaultCache, defaultCacheErr
}

type cachedIndex struct {
	Retrieved time.Time
	Index     client.DBIndex
}

func (c *FSCache) ReadIndex(dbName string) (client.DBIndex, time.Time, error) {
	c.mu.Lock()
	defer c.mu.Unlock()

	b, err := os.ReadFile(filepath.Join(c.rootDir, dbName, "index.json"))
	if err != nil {
		if os.IsNotExist(err) {
			return nil, time.Time{}, nil
		}
		return nil, time.Time{}, err
	}
	var index cachedIndex
	if err := json.Unmarshal(b, &index); err != nil {
		return nil, time.Time{}, err
	}
	return index.Index, index.Retrieved, nil
}

func (c *FSCache) WriteIndex(dbName string, index client.DBIndex, retrieved time.Time) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	path := filepath.Join(c.rootDir, dbName)
	if err := os.MkdirAll(path, 0755); err != nil {
		return err
	}
	j, err := json.Marshal(cachedIndex{
		Index:     index,
		Retrieved: retrieved,
	})
	if err != nil {
		return err
	}
	if err := os.WriteFile(filepath.Join(path, "index.json"), j, 0666); err != nil {
		return err
	}
	return nil
}

func (c *FSCache) ReadEntries(dbName string, p string) ([]*osv.Entry, error) {
	c.mu.Lock()
	defer c.mu.Unlock()

	ep, err := client.EscapeModulePath(p)
	if err != nil {
		return nil, err
	}
	b, err := os.ReadFile(filepath.Join(c.rootDir, dbName, ep, "vulns.json"))
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, err
	}
	var entries []*osv.Entry
	if err := json.Unmarshal(b, &entries); err != nil {
		return nil, err
	}
	return entries, nil
}

func (c *FSCache) WriteEntries(dbName string, p string, entries []*osv.Entry) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	ep, err := client.EscapeModulePath(p)
	if err != nil {
		return err
	}
	path := filepath.Join(c.rootDir, dbName, ep)
	if err := os.MkdirAll(path, 0777); err != nil {
		return err
	}
	j, err := json.Marshal(entries)
	if err != nil {
		return err
	}
	if err := os.WriteFile(filepath.Join(path, "vulns.json"), j, 0666); err != nil {
		return err
	}
	return nil
}